/* 本程序用于视频分流 1.推流摄像头画面至RTSP服务器,交由YOLO模型进行处理 2.接收YOLO传来的坐标,深度,警报等级等等数据 3.根据获取到的数据绘制边框和相应数据 4.将绘制完毕的视频流继续推流至RTSP服务器用于输出 */ #include #include #include #include using namespace std; using namespace cv; using namespace chrono_literals; // 全局变量 VideoCapture cap(0); 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); // 函数声明 // mqtt初始化 void MqttInit(); // 摄像头管道初始化 bool videoInit(VideoCapture &cap); // 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); int main() { // 初始化摄像头 if (!videoInit(cap)) { return -1; } // 初始化FFmpeg管道 FILE *pipe = pipeInit(); if (!pipe) { return -1; } // 主处理循环 mainLoop(cap, pipe); // 清理资源 cleanup(pipe, cap); return 0; } // 绘制矩形方框和深度信息 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; } // 摄像头初始化 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管道初始化 FILE *pipeInit() { FILE *pipe = popen( "ffmpeg " "-f rawvideo -pixel_format bgr24 -video_size 640x480 -framerate 30 " "-i - " "-c:v h264_rkmpp " "-rc_mode 2 -qp_init 32 " "-profile baseline -coder cavlc " "-g 1 -bf 0 " "-fflags nobuffer -flags low_delay " "-f h264 udp://192.168.12.1:8888?pkt_size=1316", "w"); if (!pipe) { cerr << "FFmpeg管道打开失败" << endl; return nullptr; } // 设置无缓冲模式 setvbuf(pipe, NULL, _IONBF, 0); cout << "FFmpeg管道初始化成功" << endl; return pipe; } // 处理单帧 bool processFrame(VideoCapture &cap, FILE *pipe, Mat &frame, int64 &count, chrono::steady_clock::time_point &t0) { // 读取帧 cap.read(frame); if (frame.empty()) { cerr << "读取帧失败" << endl; return false; } // 拷贝视频帧 handleFrame = frame; // 添加提示文本 putText(frame, "press 'q' to quit", Point(0, 20), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 255, 0), 2, LINE_8); // FPS计算与显示 ++count; auto now = chrono::steady_clock::now(); if (chrono::duration_cast(now - t0).count() / 1000.0 > 1.0) { string fps = "FPS:" + to_string(count); count = 0; t0 = now; } // 写入管道 fwrite(frame.data, 1, frame.total() * frame.elemSize(), pipe); fflush(pipe); // 可选:显示窗口 // imshow("测试画面", frame); return true; } // 主处理循环 void mainLoop(VideoCapture &cap, FILE *pipe) { int64 count = 0; auto t0 = chrono::steady_clock::now(); Mat frame; cout << "开始视频处理循环..." << endl; // 创建全屏窗口 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; // 用于存储缩放后的画面 while (true) { if (!processFrame(cap, pipe, frame, count, t0)) { break; } // 将 handleFrame 缩放到全屏尺寸 resize(handleFrame, displayFrame, Size(screenWidth, screenHeight)); imshow("处理后的画面", displayFrame); // 检测退出键 if (cv::waitKey(1) == 'q') { cout << "用户请求退出" << endl; break; } } } // 资源清理 void cleanup(FILE *pipe, VideoCapture &cap) { if (pipe) { pclose(pipe); cout << "FFmpeg管道已关闭" << endl; } if (cap.isOpened()) { cap.release(); cout << "摄像头已释放" << endl; } destroyAllWindows(); cout << "所有资源已清理完毕" << endl; }