diff --git a/src/rtmp_manager.cpp b/src/rtmp_manager.cpp index 3f1ca5c..b271e31 100644 --- a/src/rtmp_manager.cpp +++ b/src/rtmp_manager.cpp @@ -114,42 +114,43 @@ std::string RTMPManager::make_key(const std::string& name) { return name + "_mai GstElement* RTMPManager::create_pipeline(const Camera& cam) { const int fps = 30; - const int bitrate = cam.bitrate; // 3.5M~4M - const int gop = fps; // 建议:1 秒一个 IDR + const int bitrate = cam.bitrate; + const int gop = fps; const std::string stream_name = cam.name; const std::string rtmp_url = "rtmp://127.0.0.1:1935/" + stream_name; - std::string pipeline_str = "v4l2src device=" + cam.device + - " io-mode=dmabuf do-timestamp=true " + std::string pipeline_str = + // === V4L2 === + "v4l2src device=" + cam.device + + " io-mode=dmabuf do-timestamp=true " - // ✅ 相机原生格式:完全匹配驱动 - "! video/x-raw,format=NV12," - "width=1280,height=960,framerate=30/1 " + // === 完全匹配驱动格式 === + "! video/x-raw,format=NV12,width=1280,height=960,framerate=30/1 " - // ✅ 缓冲,只负责解耦,不丢帧 - "! queue max-size-buffers=4 max-size-time=0 max-size-bytes=0 " + // === 解耦缓冲 === + "! queue max-size-buffers=4 max-size-time=0 max-size-bytes=0 " - // ✅ 硬件编码 - "! mpph264enc " - "rc-mode=cbr " - "bps=" + - std::to_string(bitrate) + - " " - "gop=" + - std::to_string(gop) + - " " - "profile=main " - "header-mode=each-idr " + // === 硬件编码(命名!)=== + "! mpph264enc name=enc " + "rc-mode=cbr " + "bps=" + + std::to_string(bitrate) + + " " + "gop=" + + std::to_string(gop) + + " " + "profile=main " + "header-mode=each-idr " - // ✅ H.264 打包 - "! h264parse config-interval=1 " - "! video/x-h264,stream-format=avc,alignment=au " + // === H264 === + "! h264parse config-interval=1 " + "! video/x-h264,stream-format=avc,alignment=au " - // ✅ FLV / RTMP - "! flvmux streamable=true " - "! rtmpsink location=\"" + - rtmp_url + "\" sync=false async=false"; + // === RTMP === + "! flvmux streamable=true " + "! rtmpsink location=\"" + + rtmp_url + "\" sync=false async=false"; GError* error = nullptr; GstElement* pipeline = gst_parse_launch(pipeline_str.c_str(), &error); @@ -168,8 +169,8 @@ void RTMPManager::stream_loop(Camera cam, StreamContext* ctx) { const std::string key = make_key(cam.name); - constexpr int64_t START_TIMEOUT_MS = 5000; // 启动阶段无帧 - constexpr int64_t NO_FRAME_TIMEOUT_MS = 10000; // 运行阶段突然无帧 ⇒ pipeline 卡死重启 + constexpr int64_t START_TIMEOUT_MS = 12000; // 启动阶段无帧 + constexpr int64_t NO_FRAME_TIMEOUT_MS = 15000; // 运行阶段突然无帧 ⇒ pipeline 卡死重启 while (ctx->thread_running) { @@ -198,20 +199,22 @@ void RTMPManager::stream_loop(Camera cam, StreamContext* ctx) gst_element_set_name(pipeline, key.c_str()); GstBus* bus = gst_element_get_bus(pipeline); - // 3) 帧探测:记录 last_frame_ms + // 3) 帧探测:挂在 encoder 的 src pad(真实“视频已生成”) ctx->last_frame_ms.store(0, std::memory_order_relaxed); + { - GstElement* src = gst_bin_get_by_name(GST_BIN(pipeline), "src"); - if (src) + GstElement* enc = gst_bin_get_by_name(GST_BIN(pipeline), "enc"); + if (!enc) { LOG_ERROR("[RTMP] Failed to find encoder element"); } + else { - GstPad* pad = gst_element_get_static_pad(src, "src"); + GstPad* pad = gst_element_get_static_pad(enc, "src"); if (pad) { gst_pad_add_probe( pad, GST_PAD_PROBE_TYPE_BUFFER, [](GstPad*, GstPadProbeInfo*, gpointer data) -> GstPadProbeReturn { - auto ts = static_cast*>(data); + auto* ts = static_cast*>(data); auto now = std::chrono::steady_clock::now().time_since_epoch(); auto ms = std::chrono::duration_cast(now).count(); ts->store(ms, std::memory_order_relaxed); @@ -220,7 +223,7 @@ void RTMPManager::stream_loop(Camera cam, StreamContext* ctx) &(ctx->last_frame_ms), nullptr); gst_object_unref(pad); } - gst_object_unref(src); + gst_object_unref(enc); } }