2025-11-07 15:04:34 +08:00
|
|
|
/*
|
|
|
|
|
本程序用于视频分流
|
|
|
|
|
1.推流摄像头画面至RTSP服务器,交由YOLO模型进行处理
|
|
|
|
|
2.接收YOLO传来的坐标,深度,警报等级等等数据
|
|
|
|
|
3.根据获取到的数据绘制边框和相应数据
|
|
|
|
|
4.将绘制完毕的视频流继续推流至RTSP服务器用于输出
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <opencv4/opencv2/opencv.hpp>
|
|
|
|
|
#include <mqtt/async_client.h>
|
2025-11-07 16:48:14 +08:00
|
|
|
#include <nlohmann/json.hpp>
|
2025-11-07 15:04:34 +08:00
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace cv;
|
|
|
|
|
using namespace chrono_literals;
|
|
|
|
|
|
2025-11-07 15:43:47 +08:00
|
|
|
// 全局变量
|
|
|
|
|
VideoCapture cap(0);
|
2025-11-07 16:48:14 +08:00
|
|
|
Mat handleFrame;
|
|
|
|
|
const string mqtt_url = "tcp://192.168.12.1:1883";
|
|
|
|
|
const string clientId = "video_subData";
|
|
|
|
|
const string Topic = "/video/Data";
|
|
|
|
|
const int Qos = 1;
|
|
|
|
|
mqtt::async_client client(mqtt_url, clientId);
|
2025-11-07 15:43:47 +08:00
|
|
|
|
|
|
|
|
// 函数声明
|
2025-11-07 16:48:14 +08:00
|
|
|
// mqtt初始化
|
|
|
|
|
void MqttInit();
|
|
|
|
|
// 摄像头管道初始化
|
2025-11-07 15:43:47 +08:00
|
|
|
bool videoInit(VideoCapture &cap);
|
2025-11-07 16:48:14 +08:00
|
|
|
// ffmpeg管道初始化
|
|
|
|
|
FILE *pipeInit();
|
|
|
|
|
// 对单个帧进行处理
|
|
|
|
|
bool processFrame(VideoCapture &cap, FILE *pipe, Mat &frame, int64 &count, chrono::steady_clock::time_point &t0);
|
|
|
|
|
// 资源清理
|
|
|
|
|
void cleanup(FILE *pipe, VideoCapture &cap);
|
|
|
|
|
// 主循环
|
|
|
|
|
void mainLoop(VideoCapture &cap, FILE *pipe);
|
|
|
|
|
// mqtt接收订阅消息的回调
|
|
|
|
|
void getMsgCallback(mqtt::const_message_ptr msg);
|
|
|
|
|
// 绘制矩形方框和深度信息
|
|
|
|
|
void drawRect(int x, int y, int w, int h, double distance);
|
2025-11-07 15:43:47 +08:00
|
|
|
|
2025-11-07 15:04:34 +08:00
|
|
|
int main()
|
|
|
|
|
{
|
2025-11-07 15:43:47 +08:00
|
|
|
// 初始化摄像头
|
|
|
|
|
if (!videoInit(cap))
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化FFmpeg管道
|
|
|
|
|
FILE *pipe = pipeInit();
|
|
|
|
|
if (!pipe)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 主处理循环
|
|
|
|
|
mainLoop(cap, pipe);
|
|
|
|
|
|
|
|
|
|
// 清理资源
|
|
|
|
|
cleanup(pipe, cap);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-07 16:48:14 +08:00
|
|
|
// 绘制矩形方框和深度信息
|
|
|
|
|
void drawRect(int x, int y, int w, int h, double distance)
|
|
|
|
|
{
|
|
|
|
|
Rect r(x, y, w, h);
|
|
|
|
|
rectangle(handleFrame, r, Scalar(0, 255, 0), 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mqtt初始化
|
|
|
|
|
void MqttInit()
|
|
|
|
|
{
|
|
|
|
|
// 设置回调
|
|
|
|
|
client.set_connected_handler([](const string &cause)
|
|
|
|
|
{ cout << "连接成功" << endl; });
|
|
|
|
|
|
|
|
|
|
client.set_message_callback(getMsgCallback);
|
|
|
|
|
|
|
|
|
|
client.connect()->wait();
|
|
|
|
|
client.subscribe(Topic, Qos)->wait();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mqtt接收订阅消息的回调
|
|
|
|
|
void getMsgCallback(mqtt::const_message_ptr msg)
|
|
|
|
|
{
|
|
|
|
|
string payload = msg->to_string();
|
|
|
|
|
|
|
|
|
|
cout << payload << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-07 15:43:47 +08:00
|
|
|
// 摄像头初始化
|
|
|
|
|
bool videoInit(VideoCapture &cap)
|
|
|
|
|
{
|
|
|
|
|
if (!cap.isOpened())
|
|
|
|
|
{
|
|
|
|
|
cerr << "摄像头打开失败,请重试" << endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 降低分辨率,固定MJPG编码
|
|
|
|
|
cap.set(CAP_PROP_FOURCC, VideoWriter::fourcc('M', 'J', 'P', 'G'));
|
|
|
|
|
cap.set(CAP_PROP_FRAME_WIDTH, 640);
|
|
|
|
|
cap.set(CAP_PROP_FRAME_HEIGHT, 480);
|
|
|
|
|
cap.set(CAP_PROP_FPS, 30);
|
|
|
|
|
cap.set(CAP_PROP_BUFFERSIZE, 1);
|
|
|
|
|
|
|
|
|
|
cout << "摄像头初始化成功" << endl;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FFmpeg管道初始化
|
2025-11-07 16:48:14 +08:00
|
|
|
FILE *pipeInit()
|
2025-11-07 15:43:47 +08:00
|
|
|
{
|
2025-11-07 15:04:34 +08:00
|
|
|
FILE *pipe = popen(
|
|
|
|
|
"ffmpeg "
|
|
|
|
|
"-f rawvideo -pixel_format bgr24 -video_size 640x480 -framerate 30 "
|
|
|
|
|
"-i - "
|
|
|
|
|
"-c:v h264_rkmpp "
|
2025-11-07 15:27:03 +08:00
|
|
|
"-rc_mode 2 -qp_init 32 "
|
|
|
|
|
"-profile baseline -coder cavlc "
|
|
|
|
|
"-g 1 -bf 0 "
|
|
|
|
|
"-fflags nobuffer -flags low_delay "
|
2025-11-07 15:43:47 +08:00
|
|
|
"-f h264 udp://192.168.12.1:8888?pkt_size=1316",
|
|
|
|
|
"w");
|
2025-11-07 15:27:03 +08:00
|
|
|
|
2025-11-07 15:43:47 +08:00
|
|
|
if (!pipe)
|
2025-11-07 15:27:03 +08:00
|
|
|
{
|
2025-11-07 15:43:47 +08:00
|
|
|
cerr << "FFmpeg管道打开失败" << endl;
|
|
|
|
|
return nullptr;
|
2025-11-07 15:04:34 +08:00
|
|
|
}
|
2025-11-07 15:43:47 +08:00
|
|
|
|
|
|
|
|
// 设置无缓冲模式
|
|
|
|
|
setvbuf(pipe, NULL, _IONBF, 0);
|
|
|
|
|
cout << "FFmpeg管道初始化成功" << endl;
|
|
|
|
|
|
|
|
|
|
return pipe;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理单帧
|
2025-11-07 16:48:14 +08:00
|
|
|
bool processFrame(VideoCapture &cap, FILE *pipe, Mat &frame, int64 &count, chrono::steady_clock::time_point &t0)
|
2025-11-07 15:43:47 +08:00
|
|
|
{
|
|
|
|
|
// 读取帧
|
|
|
|
|
cap.read(frame);
|
|
|
|
|
if (frame.empty())
|
2025-11-07 15:04:34 +08:00
|
|
|
{
|
2025-11-07 15:43:47 +08:00
|
|
|
cerr << "读取帧失败" << endl;
|
|
|
|
|
return false;
|
2025-11-07 15:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
2025-11-07 16:48:14 +08:00
|
|
|
// 拷贝视频帧
|
|
|
|
|
handleFrame = frame;
|
|
|
|
|
|
2025-11-07 15:43:47 +08:00
|
|
|
// 添加提示文本
|
2025-11-07 16:48:14 +08:00
|
|
|
putText(frame, "press 'q' to quit", Point(0, 20),
|
2025-11-07 15:43:47 +08:00
|
|
|
FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 255, 0), 2, LINE_8);
|
|
|
|
|
|
|
|
|
|
// FPS计算与显示
|
|
|
|
|
++count;
|
|
|
|
|
auto now = chrono::steady_clock::now();
|
|
|
|
|
if (chrono::duration_cast<chrono::milliseconds>(now - t0).count() / 1000.0 > 1.0)
|
2025-11-07 15:04:34 +08:00
|
|
|
{
|
2025-11-07 16:48:14 +08:00
|
|
|
string fps = "FPS:" + to_string(count);
|
2025-11-07 15:43:47 +08:00
|
|
|
count = 0;
|
|
|
|
|
t0 = now;
|
2025-11-07 15:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
2025-11-07 15:43:47 +08:00
|
|
|
// 写入管道
|
|
|
|
|
fwrite(frame.data, 1, frame.total() * frame.elemSize(), pipe);
|
|
|
|
|
fflush(pipe);
|
2025-11-07 15:04:34 +08:00
|
|
|
|
2025-11-07 15:43:47 +08:00
|
|
|
// 可选:显示窗口
|
|
|
|
|
// imshow("测试画面", frame);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2025-11-07 15:04:34 +08:00
|
|
|
|
2025-11-07 15:43:47 +08:00
|
|
|
// 主处理循环
|
2025-11-07 16:48:14 +08:00
|
|
|
void mainLoop(VideoCapture &cap, FILE *pipe)
|
2025-11-07 15:43:47 +08:00
|
|
|
{
|
2025-11-07 15:04:34 +08:00
|
|
|
int64 count = 0;
|
|
|
|
|
auto t0 = chrono::steady_clock::now();
|
|
|
|
|
Mat frame;
|
2025-11-07 15:43:47 +08:00
|
|
|
cout << "开始视频处理循环..." << endl;
|
|
|
|
|
|
2025-11-07 16:48:14 +08:00
|
|
|
// 创建全屏窗口
|
|
|
|
|
namedWindow("处理后的画面", WINDOW_NORMAL);
|
|
|
|
|
setWindowProperty("处理后的画面", WND_PROP_FULLSCREEN, WINDOW_FULLSCREEN);
|
|
|
|
|
|
|
|
|
|
// 获取屏幕尺寸(通过获取全屏窗口的实际大小)
|
|
|
|
|
cv::Rect windowRect = getWindowImageRect("处理后的画面");
|
|
|
|
|
int screenWidth = windowRect.width > 0 ? windowRect.width : 1920;
|
|
|
|
|
int screenHeight = windowRect.height > 0 ? windowRect.height : 1080;
|
|
|
|
|
|
|
|
|
|
Mat displayFrame; // 用于存储缩放后的画面
|
|
|
|
|
|
2025-11-07 15:04:34 +08:00
|
|
|
while (true)
|
|
|
|
|
{
|
2025-11-07 15:43:47 +08:00
|
|
|
if (!processFrame(cap, pipe, frame, count, t0))
|
2025-11-07 15:04:34 +08:00
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-11-07 16:48:14 +08:00
|
|
|
|
|
|
|
|
// 将 handleFrame 缩放到全屏尺寸
|
|
|
|
|
resize(handleFrame, displayFrame, Size(screenWidth, screenHeight));
|
|
|
|
|
imshow("处理后的画面", displayFrame);
|
2025-11-07 15:43:47 +08:00
|
|
|
// 检测退出键
|
2025-11-07 15:04:34 +08:00
|
|
|
if (cv::waitKey(1) == 'q')
|
|
|
|
|
{
|
2025-11-07 15:43:47 +08:00
|
|
|
cout << "用户请求退出" << endl;
|
2025-11-07 15:04:34 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-07 15:43:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 资源清理
|
2025-11-07 16:48:14 +08:00
|
|
|
void cleanup(FILE *pipe, VideoCapture &cap)
|
2025-11-07 15:43:47 +08:00
|
|
|
{
|
|
|
|
|
if (pipe)
|
|
|
|
|
{
|
|
|
|
|
pclose(pipe);
|
|
|
|
|
cout << "FFmpeg管道已关闭" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cap.isOpened())
|
|
|
|
|
{
|
|
|
|
|
cap.release();
|
|
|
|
|
cout << "摄像头已释放" << endl;
|
|
|
|
|
}
|
2025-11-07 15:04:34 +08:00
|
|
|
|
|
|
|
|
destroyAllWindows();
|
2025-11-07 15:43:47 +08:00
|
|
|
cout << "所有资源已清理完毕" << endl;
|
2025-11-07 15:04:34 +08:00
|
|
|
}
|