From 07fd7850989f22b70ec8dedebc0277c787cf11f5 Mon Sep 17 00:00:00 2001 From: cxh Date: Tue, 25 Nov 2025 08:54:26 +0800 Subject: [PATCH] 1 --- src/rtsp_manager.cpp | 67 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/src/rtsp_manager.cpp b/src/rtsp_manager.cpp index 8310623..fd6c13a 100644 --- a/src/rtsp_manager.cpp +++ b/src/rtsp_manager.cpp @@ -32,6 +32,28 @@ bool set_v4l2_format(const std::string &dev, int width, int height) } struct v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + // 先读当前格式,避免每次都硬 S_FMT + if (ioctl(fd, VIDIOC_G_FMT, &fmt) == 0) + { + bool match = true; + if (fmt.fmt.pix_mp.width != (unsigned int)width || + fmt.fmt.pix_mp.height != (unsigned int)height || + fmt.fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) + { + match = false; + } + + if (match) + { + close(fd); + LOG_INFO("[RTSP] V4L2 format already NV12 " + std::to_string(width) + "x" + std::to_string(height) + " for " + dev); + return true; + } + } + memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; fmt.fmt.pix_mp.width = width; @@ -46,6 +68,8 @@ bool set_v4l2_format(const std::string &dev, int width, int height) return false; } + LOG_INFO("[RTSP] Set V4L2 format to NV12 " + std::to_string(width) + "x" + std::to_string(height) + " for " + dev); + close(fd); return true; } @@ -67,7 +91,6 @@ GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam) int out_width = cam.width; int out_height = cam.height; - // 先拼 caps,方便看 std::string caps = "video/x-raw,format=NV12," "width=" + @@ -75,14 +98,13 @@ GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam) ",height=" + std::to_string(out_height) + ",framerate=" + std::to_string(cam.fps) + "/1"; - // 关键改动: - // 1) v4l2src 加 is-live / do-timestamp,避免 preroll 卡死 - // 2) queue 放宽限制,避免首帧被丢导致 pipeline 出错 - // 3) suspend_mode 改为 NONE,避免客户端断开时 RESET pipeline 导致反复 s_stream(0/1) + // 注意: + // 1) config-interval 放到 rtph264pay 上 + // 2) mpph264enc 加 gop / rc-mode / bps + // 3) is-live + do-timestamp 避免 preroll 卡死 std::string launch_str = "( v4l2src device=" + cam.device + " io-mode=2 is-live=true do-timestamp=true" - " ! identity sync=false" " ! " + caps + " ! queue leaky=downstream max-size-buffers=0 max-size-bytes=0 max-size-time=0" @@ -90,14 +112,18 @@ GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam) std::to_string(cam.bitrate) + " gop=" + std::to_string(cam.fps) + " header-mode=1" - " ! h264parse config-interval=1" - " ! rtph264pay name=pay0 pt=96 )"; + " ! h264parse" + " ! rtph264pay name=pay0 pt=96 config-interval=1 )"; + + LOG_INFO("[RTSP] Launch pipeline for " + cam.name + ": " + launch_str); GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new(); gst_rtsp_media_factory_set_launch(factory, launch_str.c_str()); + + // 多客户端共享同一条 pipeline gst_rtsp_media_factory_set_shared(factory, TRUE); - // 不再在客户端断开时 RESET / 销毁 pipeline,避免频繁触发 MIPI / maxim4c 重新配置 + // 不在客户端断开时 RESET pipeline,避免频繁 s_stream(0/1) gst_rtsp_media_factory_set_suspend_mode(factory, GST_RTSP_SUSPEND_MODE_NONE); g_signal_connect_data(factory, @@ -169,6 +195,7 @@ void RTSPManager::on_media_created(GstRTSPMediaFactory *, GstRTSPMedia *media, g LOG_INFO(std::string("[RTSP] media-configure for camera: ") + cam_name); + // media 自身加引用,存入 map g_object_ref(media); { @@ -184,16 +211,26 @@ void RTSPManager::on_media_created(GstRTSPMediaFactory *, GstRTSPMedia *media, g (GClosureNotify)g_free, (GConnectFlags)0); - // ★★★ 关键补丁:阻止 preroll 卡死 v4l2 驱动,强制 PLAYING + // 强制 pipeline 进入 PLAYING,避免 preroll 阶段卡死在 PAUSED GstElement *pipeline = gst_rtsp_media_get_element(media); if (pipeline) { - gst_element_set_state(pipeline, GST_STATE_PLAYING); - LOG_INFO(std::string("[RTSP] Force pipeline PLAYING for camera: ") + cam_name); + GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) + { + LOG_ERROR(std::string("[RTSP] Failed to set pipeline PLAYING for camera: ") + cam_name); + } + else + { + LOG_INFO(std::string("[RTSP] Force pipeline PLAYING for camera: ") + cam_name); + } + + // ⚠️ 这里一定要 unref,一次 get_element 一次 unref + gst_object_unref(pipeline); } else { - LOG_WARN(std::string("[RTSP] Pipeline is NULL for camera: ") + cam_name); + LOG_ERROR(std::string("[RTSP] Pipeline is NULL for camera: ") + cam_name); } } @@ -213,6 +250,10 @@ void RTSPManager::on_media_unprepared(GstRTSPMedia *media, gpointer user_data) if (vec.empty()) media_map.erase(it); } + else + { + LOG_WARN(std::string("[RTSP] media-unprepared but no entry in media_map for camera: ") + cam_name); + } } g_object_unref(media);