diff --git a/src/rtsp_manager.cpp b/src/rtsp_manager.cpp index 590bf6b..1764df1 100644 --- a/src/rtsp_manager.cpp +++ b/src/rtsp_manager.cpp @@ -94,30 +94,37 @@ GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam) ",height=" + std::to_string(h) + ",framerate=" + std::to_string(cam.fps) + "/1"; - // 修正后的管线:加 videoconvert,删掉 encoder 不支持的属性 + // 关键:tee 一路给 RTSP,一路给 fakesink,保持 v4l2src / mpph264enc 永远在跑 + // + // 结构: + // v4l2src -> caps -> tee name=t + // tee src0: t. -> queue -> mpph264enc -> h264parse -> rtph264pay (给 RTSP) + // tee src1: t. -> queue -> fakesink (假消费者,让 DMA 持续 dequeue) std::string launch_str = "( v4l2src device=" + cam.device + " io-mode=2 is-live=true do-timestamp=true" " ! " + caps + - " ! videoconvert" + " ! tee name=t " " ! queue leaky=downstream max-size-time=0 max-size-bytes=0 max-size-buffers=0" " ! mpph264enc name=enc rc-mode=cbr bps=" + std::to_string(cam.bitrate) + " gop=" + std::to_string(cam.fps) + " header-mode=1" " ! h264parse" - " ! rtph264pay name=pay0 pt=96 config-interval=1 )"; + " ! rtph264pay name=pay0 pt=96 config-interval=1 " + " t. ! queue max-size-buffers=1 max-size-time=0 max-size-bytes=0" + " ! fakesink sync=false )"; LOG_INFO("[RTSP] Launch for " + cam.name + ": " + launch_str); GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new(); gst_rtsp_media_factory_set_launch(factory, launch_str.c_str()); - // ★ 必改:先关掉 shared,避免 VLC 拉流不启动 pipeline + // 先保持每个客户端独立 pipeline,逻辑简单可靠 gst_rtsp_media_factory_set_shared(factory, FALSE); - // 客户端断开时不 reset pipeline(我们手动处理) + // 客户端断开时不要乱 reset,交给我们自己处理 / 或干脆不动 gst_rtsp_media_factory_set_suspend_mode(factory, GST_RTSP_SUSPEND_MODE_NONE); g_signal_connect_data(factory, @@ -189,6 +196,7 @@ void RTSPManager::on_media_created(GstRTSPMediaFactory *, GstRTSPMedia *media, g LOG_INFO(std::string("[RTSP] media-configure for camera: ") + cam_name); + // 保存 media,用于之后 unmount 时统一管理 g_object_ref(media); { std::lock_guard lock(media_map_mutex); @@ -203,20 +211,8 @@ void RTSPManager::on_media_created(GstRTSPMediaFactory *, GstRTSPMedia *media, g (GClosureNotify)g_free, (GConnectFlags)0); - GstElement *pipeline = gst_rtsp_media_get_element(media); - if (!pipeline) - { - LOG_ERROR("[RTSP] Pipeline NULL for camera: " + std::string(cam_name)); - return; - } - - LOG_INFO("[RTSP] Forcing pipeline reset (READY → PLAYING) to generate IDR"); - - // 关键:强制重新启动 pipeline,让 v4l2src/mpph264enc 正式启动 - gst_element_set_state(pipeline, GST_STATE_READY); - gst_element_set_state(pipeline, GST_STATE_PLAYING); - - gst_object_unref(pipeline); + // 不再手动改 pipeline 状态,交给 gst-rtsp-server 自己管理 + // 如果后面发现确实有卡在 PAUSED 的情况,再在这里加逻辑 } void RTSPManager::on_media_unprepared(GstRTSPMedia *media, gpointer user_data)