GStreamer RTSP服务器推送数据异常:断言错误与流无法播放排查
GStreamer RTSP推送问题排查与修复
问题概况
作为GStreamer/RTSP新手,需要搭建两条流水线:
videotestsrc -> x264enc -> appsink:将采集编码后的视频数据主动推送给RTSP服务器的appsrcappsrc -> rtph264pay:RTSP服务器端的流处理流水线
放弃官方示例的need-data拉取模式,改为从appsink主动推送,但遇到以下问题:
- 首次推送数据给appsrc时触发断言错误:
GStreamer-CRITICAL **: 17:20:43.792: gst_segment_to_running_time: assertion 'segment->format == format' failed - 后续推送无警告,但gst-launch、ffplay无法读取流,ffplay执行
ffplay rtsp://127.0.0.1:8554/test时卡在nan : 0.000 fd= 0 aq= 0KB vq= 0KB sq= 0B f=0/0 - 运行环境为WSL2
错误原因分析
- 断言错误根源:RTSP端的appsrc未初始化时间片段(Segment),推送数据时GStreamer无法将缓冲区时间戳转换为运行时间,导致格式不匹配断言失败。
- 流无法播放原因:H264流格式不兼容、Sample引用计数错误、RTSP流水线状态未同步等多重问题叠加,导致客户端无法解析流数据。
代码修复要点
1. 初始化AppSrc的时间Segment
在MediaConfigure函数中,添加segment初始化代码,确保appsrc有有效的时间基准:
void GStreamer_RTSP::MediaConfigure( GstRTSPMediaFactory *factory, GstRTSPMedia *media, void *aParent ) { GStreamer_RTSP *lThis = (GStreamer_RTSP *)aParent; GstElement *lElement = gst_rtsp_media_get_element( media ); GstElement *lRTSPSource = gst_bin_get_by_name( GST_BIN( lElement ), "rtspsrc" ); // 初始化时间Segment GstSegment segment; gst_segment_init(&segment, GST_FORMAT_TIME); gst_app_src_start_segment(GST_APP_SRC(lRTSPSource), &segment); gst_app_src_set_stream_type( GST_APP_SRC( lRTSPSource ), GST_APP_STREAM_TYPE_STREAM ); // 修正caps,添加H264流格式参数 auto caps = gst_caps_new_simple( "video/x-h264", "width", G_TYPE_INT, 1000, "height", G_TYPE_INT, 500, "framerate", GST_TYPE_FRACTION, 10, 1, "stream-format", G_TYPE_STRING, "byte-stream", "alignment", G_TYPE_STRING, "au", NULL ); gst_app_src_set_caps( GST_APP_SRC( lRTSPSource ), caps ); gst_caps_unref( caps ); // 启动RTSP流水线 gst_element_set_state(lElement, GST_STATE_PLAYING); gst_object_unref( lElement ); gst_object_unref( lRTSPSource ); const std::lock_guard<std::mutex> lock( lThis->mMediaMutex ); lThis->mRTSPStreams.insert( media ); g_signal_connect( media, "unprepared", (GCallback)&GStreamer_RTSP::MediaUnconfigure, aParent ); }
2. 配置x264enc输出字节流格式
修改InitPipeline中x264enc的参数,确保输出符合rtph264pay要求的字节流:
void GStreamer_RTSP::InitPipeline() { // ... 其他代码 ... g_object_set( lEncoder, "tune", 4, "byte-stream", TRUE, NULL ); // 4 = zerolatency + 输出字节流 // ... 其他代码 ... }
3. 修复Sample推送的引用计数问题
在NewFrameFromSink中,为每个RTSP流复制独立的Sample,避免引用计数冲突:
GstFlowReturn GStreamer_RTSP::NewFrameFromSink( GstElement *aElement, void *aParent ) { GStreamer_RTSP *lThis = (GStreamer_RTSP *)aParent; if( lThis->mRTSPStreams.empty() ) { return GST_FLOW_OK; } const std::lock_guard<std::mutex> lock( lThis->mMediaMutex ); GstSample *aSample = gst_app_sink_pull_sample( GST_APP_SINK( aElement ) ); for( const auto &lOutgoingStream : lThis->mRTSPStreams ) { GstElement *lElement = gst_rtsp_media_get_element( lOutgoingStream ); GstElement *lRTSPSource = gst_bin_get_by_name( GST_BIN( lElement ), "rtspsrc" ); // 为每个流复制Sample,避免引用计数错误 GstSample* sample_copy = gst_sample_copy(aSample); gst_app_src_push_sample( GST_APP_SRC( lRTSPSource ), sample_copy ); gst_sample_unref(sample_copy); gst_object_unref( lElement ); gst_object_unref( lRTSPSource ); } gst_sample_unref( aSample ); return GST_FLOW_OK; }
4. 可选:添加缓冲区时间戳处理(若仍有同步问题)
如果客户端播放仍有卡顿或不同步,可确保推送的缓冲区带有正确的时间戳。在NewFrameFromSink中,可手动设置时间戳(若原始Sample时间戳无效):
GstBuffer* buffer = gst_sample_get_buffer(aSample); GstClockTime timestamp = gst_clock_get_time(gst_element_get_clock(lPipeline)); gst_buffer_set_pts(buffer, timestamp); gst_buffer_set_dts(buffer, timestamp);
修改后验证
- 重新编译运行代码,首次推送不再出现断言错误
- 使用ffplay或gst-launch测试流:
或ffplay rtsp://127.0.0.1:8554/testgst-launch-1.0 rtspsrc location=rtsp://127.0.0.1:8554/test ! rtph264depay ! h264parse ! avdec_h264 ! autovideosink
内容的提问来源于stack exchange,提问作者David Levy
相关产品推荐
相关产品推荐

