You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GStreamer双流水线重启延迟问题:如何用Clock Time替代Running Time

问题描述

我有两条GStreamer流水线,一条为「源流水线」,负责将直播摄像头流推送至外部通道,另一条为「接收端流水线」,从通道另一端读取流后输出至对应sink:

[videotestsrc] -> [appsink] ----- 串行通道 ------>  [appsrc] -> [autovideosink]
     第一流水线                                             第二流水线

第一条流水线以videotestsrc为起点,编码视频后封装为gdppay payload,随后将流输出到串行通道(为方便问题复现,也可替换为写入串口的filesink、udpsink等可被后续流水线读取的sink),由第二条流水线的源端读取后通过autovideosink播放:

源流水线

gst-launch-1.0 -v videotestsrc ! videoconvert ! video/x-raw,format=I420 ! x265enc ! gdppay ! udpsink host=127.0.0.1 port=5004

接收端流水线

gst-launch-1.0 -v udpsrc uri=udp://127.0.0.1:5004 ! gdpdepay ! h265parse ! avdec_h265 ! autovideosink

注意:使用udpsink/udpsrc会引入延迟,导致流水线报时间戳错误,将二者替换为对接串口的filesrc/filesink即可复现下述问题。


两条流水线同时启动时运行正常,但源流水线运行30s后停止再重启时,其Running Time会重置为0,导致新输出的所有缓冲区时间戳被持续运行的接收端流水线判定为旧帧(接收端已接收过0-30s的帧),因此播放端会卡在30s的帧直到源流水线重新跑到30s才恢复播放:

源流水线: [28][29][30][0 ][1 ][2 ][3 ]...[29][30][31]
接收端流水线:   [28][29][30][30][30][30][30]...[30][30][31]
     ________________________^ 
     源流水线重启
                             ^^^^^^^^^^^^^^^^...^^^^^^^^
                             接收端会一直显示30s时收到的帧,直到收到时间戳大于30s的新帧,但实际上源端重启后发送的每一帧都是最新的,应该立即播放。

已尝试方案

我发现给autovideosink添加sync=false参数可以解决该问题,但我更希望源端可以基于Clock time生成缓冲区的DTS和PTS。
我尝试给视频源设置is-live和do-timestamp参数,也尝试基于系统时间手动设置缓冲区的DTS、PTS,都没有达到预期效果,请问有什么可行的解决方案?


可行解决方案

可根据你的业务场景选择对应方案:

方案1:源端使用全局单调时钟生成时间戳(符合源端控时需求)

之前手动设置时间戳未生效,是因为没有覆盖流水线默认的base_time计算逻辑,按以下步骤修改即可:

  1. 源端全局使用系统单调时钟作为流水线时钟,启动时强制将流水线base_time设为0,此时缓冲区的running_time会直接等于单调时钟的纳秒值
  2. 给视频源开启do-timestamp=true和is-live=true,让源自动用当前时钟时间打时间戳
    对应gst-launch测试命令:
gst-launch-1.0 -v --clock-id=monotonic videotestsrc is-live=true do-timestamp=true ! videoconvert ! video/x-raw,format=I420 ! x265enc ! gdppay ! udpsink host=127.0.0.1 port=5004

如果是代码实现的流水线,启动前调用以下接口即可:

GstClock *mono_clock = gst_system_clock_obtain();
gst_element_set_clock(GST_ELEMENT(pipeline), mono_clock);
gst_pipeline_set_base_time(GST_PIPELINE(pipeline), 0);

方案2:接收端检测流重启自动重置同步基准(无需修改源端逻辑)

如果源端修改受限,可以在接收端做适配:

  1. 在gdpdepay后加identity元件,监听notify::caps信号,当caps重新上报时说明源端已重启
  2. 收到信号后调用gst_element_set_base_time()将autovideosink的base_time重置为当前时钟时间,即可清空之前的时间戳判断基准,新到的帧会直接按当前时间播放
    对应核心代码示例:
// 监听caps变化的回调
static void caps_changed_cb(GObject *obj, GParamSpec *pspec, gpointer user_data) {
    GstElement *sink = user_data;
    GstClock *clock = gst_element_get_clock(sink);
    if (clock) {
        GstClockTime now = gst_clock_get_time(clock);
        gst_element_set_base_time(sink, now);
        gst_object_unref(clock);
    }
}
// 流水线初始化时绑定信号
GstElement *identity = gst_element_factory_make("identity", NULL);
g_signal_connect(identity, "notify::caps", G_CALLBACK(caps_changed_cb), autosink);

方案3:更换为RTP封装自动处理时间戳偏移

如果允许修改封装协议,替换gdppay/gdpdepay为RTP相关封装是最省心的方案:

  • 源端流水线:x265enc ! rtph265pay config-interval=1 ! udpsink
  • 接收端流水线:udpsrc ! application/x-rtp,media=video,encoding-name=H265 ! rtpjitterbuffer ! rtph265depay ! h265parse ! avdec_h265 ! autovideosink
    rtpjitterbuffer会自动检测RTP序列号和时间戳跳变,源重启时会自动调整时间偏移,无需手动处理同步问题。

内容的提问来源于stack exchange,提问作者Garrit Strenge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 04:48:02