diff --git a/src/rtmp_manager.cpp b/src/rtmp_manager.cpp index 07961aa..d0488c3 100644 --- a/src/rtmp_manager.cpp +++ b/src/rtmp_manager.cpp @@ -72,30 +72,40 @@ GstElement* RTMPManager::create_pipeline(const Camera& cam) const std::string stream_name = cam.name + "_main"; const std::string app = "camera"; - const std::string location = "rtmp://127.0.0.1:1935/" + app + "/" + stream_name + "?vhost=live"; + // 两个不同的 SRS 实例:live 用于远控,record 用于录像 + const std::string live_rtmp = "rtmp://127.0.0.1:1935/" + app + "/" + stream_name + "?vhost=live"; + const std::string record_rtmp = "rtmp://127.0.0.1:2935/" + app + "/" + stream_name + "?vhost=record"; - std::string pipeline_str = "v4l2src name=src device=" + cam.device + - " ! " - "video/x-raw,format=NV12,width=" + - std::to_string(width) + ",height=" + std::to_string(height) + - ",framerate=" + std::to_string(fps) + - "/1 ! " - "tee name=t " - // 主分支:编码 + RTMP 推流 - "t. ! queue ! " - "mpph264enc bps=" + - std::to_string(bitrate) + " gop=" + std::to_string(fps) + - " ! " - "h264parse ! flvmux streamable=true name=mux " - // 静音音频轨道(避免 RTMP 播放器报错) - "audiotestsrc wave=silence ! audioconvert ! audioresample ! " - "voaacenc ! aacparse ! mux. " - // 输出 RTMP - "mux. ! rtmpsink name=sink location=\"" + - location + - "\" sync=false " - // 第二分支:预留 (AI 分析/录制) - "t. ! queue ! fakesink sync=false"; + // 双路推流管线:一份流编码后 tee 分支分别送往两个 flvmux+rtmpsink + std::string pipeline_str = + "v4l2src name=src device=" + cam.device + + " ! " + "video/x-raw,format=NV12,width=" + + std::to_string(width) + ",height=" + std::to_string(height) + ",framerate=" + std::to_string(fps) + + "/1 ! " + "mpph264enc bps=" + + std::to_string(bitrate) + " gop=" + std::to_string(fps) + + " rc-mode=cbr ! " + "h264parse ! tee name=t " + + // --- 分支1:低延时远控 SRS --- + "t. ! queue max-size-buffers=5 leaky=downstream ! " + "flvmux streamable=true name=mux_live " + "audiotestsrc wave=silence ! audioconvert ! audioresample ! voaacenc ! aacparse ! mux_live. " + "mux_live. ! rtmpsink location=\"" + + live_rtmp + + "\" sync=false async=false " + + // --- 分支2:录像 SRS --- + "t. ! queue max-size-buffers=5 leaky=downstream ! " + "flvmux streamable=true name=mux_record " + "audiotestsrc wave=silence ! audioconvert ! audioresample ! voaacenc ! aacparse ! mux_record. " + "mux_record. ! rtmpsink location=\"" + + record_rtmp + + "\" sync=false async=false " + + // --- 可选第三分支:AI/检测 --- + "t. ! queue ! fakesink sync=false"; GError* error = nullptr; GstElement* pipeline = gst_parse_launch(pipeline_str.c_str(), &error);