1
This commit is contained in:
parent
1b4567d2d8
commit
53f496ca7e
@ -82,7 +82,7 @@ void RTSPManager::init()
|
||||
|
||||
GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam)
|
||||
{
|
||||
// --- 设置一次 V4L2 格式 ---
|
||||
// 启动前把 v4l2 格式设成我们想要的
|
||||
set_v4l2_format(cam.device, cam.width, cam.height);
|
||||
|
||||
int w = cam.width;
|
||||
@ -95,26 +95,19 @@ GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam)
|
||||
",height=" + std::to_string(h) +
|
||||
",framerate=" + std::to_string(cam.fps) + "/1";
|
||||
|
||||
// --------------------------
|
||||
// ★★★ 稳定管线(重点全在这里)★★★
|
||||
//
|
||||
// 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(最关键)
|
||||
// --------------------------
|
||||
// 注意几点:
|
||||
// 1) 给 mpph264enc 起一个名字 name=enc,方便后面在 on_media_created 里拿到
|
||||
// 2) option-force-idr / option-idr-interval 依然在这里设置一遍,保证周期 IDR
|
||||
// 3) config-interval=1 一定要写在 rtph264pay 上
|
||||
std::string launch_str =
|
||||
"( v4l2src device=" + cam.device +
|
||||
" io-mode=2 is-live=true do-timestamp=true"
|
||||
" ! " +
|
||||
caps +
|
||||
" ! queue leaky=downstream max-size-time=0 max-size-bytes=0 max-size-buffers=0"
|
||||
" ! mpph264enc rc-mode=cbr bps=" +
|
||||
" ! mpph264enc name=enc"
|
||||
" rc-mode=cbr"
|
||||
" bps=" +
|
||||
std::to_string(cam.bitrate) +
|
||||
" gop=" + std::to_string(cam.fps) +
|
||||
" option-force-idr=true"
|
||||
@ -124,24 +117,23 @@ GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam)
|
||||
" ! h264parse"
|
||||
" ! rtph264pay name=pay0 pt=96 config-interval=1 )";
|
||||
|
||||
LOG_INFO("[RTSP] Launch: " + launch_str);
|
||||
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=TRUE 依然能用,因为我们让编码器永远产帧
|
||||
// 先保持 shared=TRUE,节省资源
|
||||
gst_rtsp_media_factory_set_shared(factory, TRUE);
|
||||
|
||||
// 不要在客户端断开时 RESET 管线(防止 maxim4c 重启)
|
||||
// 客户端断开时不重置 pipeline,防止反复 s_stream(0/1)
|
||||
gst_rtsp_media_factory_set_suspend_mode(factory, GST_RTSP_SUSPEND_MODE_NONE);
|
||||
|
||||
// 绑定事件
|
||||
g_signal_connect_data(factory,
|
||||
"media-configure",
|
||||
G_CALLBACK(on_media_created),
|
||||
g_strdup(cam.name.c_str()),
|
||||
(GClosureNotify)g_free,
|
||||
GConnectFlags(0));
|
||||
(GConnectFlags)0);
|
||||
|
||||
return factory;
|
||||
}
|
||||
@ -205,9 +197,8 @@ void RTSPManager::on_media_created(GstRTSPMediaFactory *, GstRTSPMedia *media, g
|
||||
|
||||
LOG_INFO(std::string("[RTSP] media-configure for camera: ") + cam_name);
|
||||
|
||||
// media 自身加引用,存入 map
|
||||
// 把 media 保存起来,方便后面 unmount
|
||||
g_object_ref(media);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(media_map_mutex);
|
||||
media_map[cam_name].push_back(media);
|
||||
@ -221,27 +212,44 @@ void RTSPManager::on_media_created(GstRTSPMediaFactory *, GstRTSPMedia *media, g
|
||||
(GClosureNotify)g_free,
|
||||
(GConnectFlags)0);
|
||||
|
||||
// 强制 pipeline 进入 PLAYING,避免 preroll 阶段卡死在 PAUSED
|
||||
// 获取底层 pipeline
|
||||
GstElement *pipeline = gst_rtsp_media_get_element(media);
|
||||
if (pipeline)
|
||||
if (!pipeline)
|
||||
{
|
||||
GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
{
|
||||
LOG_ERROR(std::string("[RTSP] Failed to set pipeline PLAYING for camera: ") + cam_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO(std::string("[RTSP] Force pipeline PLAYING for camera: ") + cam_name);
|
||||
}
|
||||
LOG_ERROR(std::string("[RTSP] Pipeline is NULL for camera: ") + cam_name);
|
||||
return;
|
||||
}
|
||||
|
||||
// ⚠️ 这里一定要 unref,一次 get_element 一次 unref
|
||||
gst_object_unref(pipeline);
|
||||
// ★★★ 关键:拿到 encoder 元素,强制打一发 IDR,保证 VLC 第一次连接就能拿到关键帧
|
||||
GstElement *enc = gst_bin_get_by_name(GST_BIN(pipeline), "enc");
|
||||
if (enc)
|
||||
{
|
||||
LOG_INFO(std::string("[RTSP] Forcing IDR for camera: ") + cam_name);
|
||||
// 这里用和 launch 里同名的属性,确保 Rockchip mpph264enc 能识别
|
||||
g_object_set(enc, "option-force-idr", TRUE, NULL);
|
||||
// 如果你想更狠一点,也可以顺便再 set 一次 idr-interval
|
||||
// g_object_set(enc, "option-idr-interval", cam.fps, NULL); // 这里需要你把 fps 传进来才行
|
||||
|
||||
gst_object_unref(enc);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(std::string("[RTSP] Pipeline is NULL for camera: ") + cam_name);
|
||||
LOG_WARN(std::string("[RTSP] Encoder 'enc' not found in pipeline for camera: ") + cam_name);
|
||||
}
|
||||
|
||||
// 强制 pipeline 进入 PLAYING,避免停在 PAUSED/preroll
|
||||
GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
{
|
||||
LOG_ERROR(std::string("[RTSP] Failed to set pipeline PLAYING for camera: ") + cam_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO(std::string("[RTSP] Force pipeline PLAYING for camera: ") + cam_name);
|
||||
}
|
||||
|
||||
// gst_rtsp_media_get_element() 返回的是加过 ref 的对象,需要 unref 一次
|
||||
gst_object_unref(pipeline);
|
||||
}
|
||||
|
||||
void RTSPManager::on_media_unprepared(GstRTSPMedia *media, gpointer user_data)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user