diff --git a/src/rtmp_manager.cpp b/src/rtmp_manager.cpp index cbf2e7c..5858826 100644 --- a/src/rtmp_manager.cpp +++ b/src/rtmp_manager.cpp @@ -63,114 +63,41 @@ void RTMPManager::init() std::string RTMPManager::make_key(const std::string& name) { return name + "_main"; } // ========== 创建推流管线 ========== -GstElement* RTMPManager::create_pipeline(const Camera& cam) -{ - const int width = cam.width; - const int height = cam.height; - const int fps = cam.fps; - const int bitrate = cam.bitrate; - - // MediaMTX 中的 stream key - const std::string stream_name = cam.name; - - // RTMP 推送到 MediaMTX - // mediamtx.yml 中 paths 会自动创建 - const std::string rtmp_url = "rtmp://127.0.0.1:1935/" + stream_name; - - /* - * Pipeline 说明: - * v4l2src -> mpph264enc -> h264parse -> flvmux -> rtmpsink - * - * - 不使用 tee(降低死锁概率) - * - 不引入音频(MediaMTX 不强制要求) - * - 纯视频、纯 RTMP、纯 TCP - */ - 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 name=parse " - " ! flvmux streamable=true " - " ! rtmpsink location=\"" + - rtmp_url + - "\" " - " sync=false async=false"; - - GError* error = nullptr; - GstElement* pipeline = gst_parse_launch(pipeline_str.c_str(), &error); - if (error) - { - LOG_ERROR(std::string("[RTMP] Pipeline creation failed: ") + error->message); - g_error_free(error); - return nullptr; - } - - return pipeline; -} - // GstElement* RTMPManager::create_pipeline(const Camera& cam) // { -// const int out_width = cam.width; // 推流分辨率(如 1280 / 960 / 720) -// const int out_height = cam.height; +// const int width = cam.width; +// const int height = cam.height; // const int fps = cam.fps; // const int bitrate = cam.bitrate; -// // MediaMTX stream key +// // MediaMTX 中的 stream key // const std::string stream_name = cam.name; + +// // RTMP 推送到 MediaMTX +// // mediamtx.yml 中 paths 会自动创建 // const std::string rtmp_url = "rtmp://127.0.0.1:1935/" + stream_name; // /* -// * A 方案 Pipeline: +// * Pipeline 说明: +// * v4l2src -> mpph264enc -> h264parse -> flvmux -> rtmpsink // * -// * v4l2src (原生 1280x960) -// * -> videoscale + caps(编码前缩放) -// * -> queue(低延迟) -// * -> mpph264enc(CBR) -// * -> h264parse -// * -> flvmux -// * -> rtmpsink +// * - 不使用 tee(降低死锁概率) +// * - 不引入音频(MediaMTX 不强制要求) +// * - 纯视频、纯 RTMP、纯 TCP // */ // std::string pipeline_str = "v4l2src name=src device=" + cam.device + -// " io-mode=dmabuf " - -// // ⭐ 相机真实输出:原生 1280x960 -// "! video/x-raw,format=NV12," -// "width=1280,height=960," -// "framerate=" + -// std::to_string(fps) + +// " ! video/x-raw,format=NV12,width=" + std::to_string(width) + +// ",height=" + std::to_string(height) + ",framerate=" + std::to_string(fps) + // "/1 " - -// // ⭐ 编码前缩放(关键) -// "! videoscale " -// "! video/x-raw," -// "width=" + -// std::to_string(out_width) + ",height=" + std::to_string(out_height) + -// " " - -// // ⭐ 低延迟队列,防止下游阻塞反噬 v4l2 -// "! queue max-size-buffers=2 max-size-time=0 leaky=downstream " - -// // ⭐ 硬件编码,SIM 卡友好 -// "! mpph264enc " -// "rc-mode=cbr " -// "bps=" + -// std::to_string(bitrate) + -// " " -// "gop=" + -// std::to_string(fps) + -// " " -// "header-mode=each-idr " -// "profile=baseline " - -// "! h264parse config-interval=1 " -// "! flvmux streamable=true " -// "! rtmpsink location=\"" + +// " ! mpph264enc bps=" + +// std::to_string(bitrate) + " gop=" + std::to_string(fps) + +// " rc-mode=cbr " +// " ! h264parse name=parse " +// " ! flvmux streamable=true " +// " ! rtmpsink location=\"" + // rtmp_url + // "\" " -// "sync=false async=false"; +// " sync=false async=false"; // GError* error = nullptr; // GstElement* pipeline = gst_parse_launch(pipeline_str.c_str(), &error); @@ -184,6 +111,65 @@ GstElement* RTMPManager::create_pipeline(const Camera& cam) // return pipeline; // } +GstElement* RTMPManager::create_pipeline(const Camera& cam) +{ + const int out_width = cam.width; + const int out_height = cam.height; + + const int fps = 30; // ⭐ 强制 30 + const int bitrate = cam.bitrate; // 建议 3.5M~4.0M + const int gop = 15; // ⭐ 半秒 I 帧 + + 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 name=src device=" + cam.device + + " io-mode=dmabuf " + + "! video/x-raw,format=NV12," + "width=1280,height=960," + "framerate=30/1 " + + "! videoscale " + "! video/x-raw," + "width=" + + std::to_string(out_width) + ",height=" + std::to_string(out_height) + + " " + + "! queue max-size-buffers=2 max-size-time=0 leaky=downstream " + + "! mpph264enc " + "rc-mode=cbr " + "bps=" + + std::to_string(bitrate) + + " " + "gop=" + + std::to_string(gop) + + " " + "header-mode=each-idr " + "profile=main " // ⭐ 关键改动 + + "! h264parse config-interval=1 " + "! video/x-h264,stream-format=avc,alignment=au " + "! flvmux streamable=true " + + "! rtmpsink location=\"" + + rtmp_url + + "\" " + "sync=false async=false"; + + GError* error = nullptr; + GstElement* pipeline = gst_parse_launch(pipeline_str.c_str(), &error); + if (error) + { + LOG_ERROR(std::string("[RTMP] Pipeline creation failed: ") + error->message); + g_error_free(error); + return nullptr; + } + + return pipeline; +} + // ========== 主推流循环 ========== void RTMPManager::stream_loop(Camera cam, StreamContext* ctx) {