diff --git a/src/rtsp_manager.cpp b/src/rtsp_manager.cpp index fd6c13a..795381b 100644 --- a/src/rtsp_manager.cpp +++ b/src/rtsp_manager.cpp @@ -82,56 +82,64 @@ void RTSPManager::init() GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam) { - // 程序启动时设置一次 v4l2 格式 - if (!set_v4l2_format(cam.device, cam.width, cam.height)) - { - LOG_ERROR("[RTSP] Failed to set V4L2 format for " + cam.name); - } + // --- 设置一次 V4L2 格式 --- + set_v4l2_format(cam.device, cam.width, cam.height); - int out_width = cam.width; - int out_height = cam.height; + int w = cam.width; + int h = cam.height; std::string caps = "video/x-raw,format=NV12," "width=" + - std::to_string(out_width) + - ",height=" + std::to_string(out_height) + + std::to_string(w) + + ",height=" + std::to_string(h) + ",framerate=" + std::to_string(cam.fps) + "/1"; - // 注意: - // 1) config-interval 放到 rtph264pay 上 - // 2) mpph264enc 加 gop / rc-mode / bps - // 3) is-live + do-timestamp 避免 preroll 卡死 + // -------------------------- + // ★★★ 稳定管线(重点全在这里)★★★ + // + // 1) v4l2src is-live=true do-timestamp=true → 防止 preroll 卡死 + // 2) queue max-size=0 → 避免首帧丢失 + // 3) mpph264enc + // rc-mode=cbr bps=xxx → 保持稳定码率 + // gop=fps → 1s 一个 I 帧 + // option-force-idr=true → 强制 IDR 输出 + // option-idr-interval=fps → VLC 必需 + // 4) h264parse → 整理 NAL + // 5) rtph264pay config-interval=1 → 每秒 SPS+PPS(最关键) + // -------------------------- std::string launch_str = "( v4l2src device=" + cam.device + " io-mode=2 is-live=true do-timestamp=true" " ! " + caps + - " ! queue leaky=downstream max-size-buffers=0 max-size-bytes=0 max-size-time=0" + " ! queue leaky=downstream max-size-time=0 max-size-bytes=0 max-size-buffers=0" " ! mpph264enc rc-mode=cbr bps=" + std::to_string(cam.bitrate) + " gop=" + std::to_string(cam.fps) + + " option-force-idr=true" + " option-idr-interval=" + + std::to_string(cam.fps) + " header-mode=1" " ! h264parse" " ! rtph264pay name=pay0 pt=96 config-interval=1 )"; - LOG_INFO("[RTSP] Launch pipeline for " + cam.name + ": " + launch_str); + LOG_INFO("[RTSP] Launch: " + launch_str); GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new(); gst_rtsp_media_factory_set_launch(factory, launch_str.c_str()); - // 多客户端共享同一条 pipeline + // shared=TRUE 依然能用,因为我们让编码器永远产帧 gst_rtsp_media_factory_set_shared(factory, TRUE); - // 不在客户端断开时 RESET pipeline,避免频繁 s_stream(0/1) + // 不要在客户端断开时 RESET 管线(防止 maxim4c 重启) gst_rtsp_media_factory_set_suspend_mode(factory, GST_RTSP_SUSPEND_MODE_NONE); - g_signal_connect_data(factory, - "media-configure", + // 绑定事件 + g_signal_connect_data(factory, "media-configure", G_CALLBACK(on_media_created), g_strdup(cam.name.c_str()), - (GClosureNotify)g_free, - (GConnectFlags)0); + (GClosureNotify)g_free, 0); return factory; }