Gstreamer的同步机制

本文编译自gstreamer源代码中的文档,原文的路径是gstreamer/docs/design/part-synchronisation.txt。

本文描述了Gstreamer的同步机制,Gstreamer中实现同步的组件如下:

  • GstClock,是全局的,用于pipeline中的所有elements。
  • GstBuffer的timestamps。
  • buffers之前的NEW_SEGMENT event。

GstClock

GstClock是精确到纳秒的表示当前时间的一个计数。其值用absolute_time表示。这个计数的源的选择如下:

  • 系统时间,精度是微妙。
  • 音频设备。
  • 基于网络的数据包,比如RTP数据包。
  • 其它。

在Gstreamer中任何element都可以提供GstClock,pipeline从所有可用的GstClock中选定一个并用于pipeline中所有的elements。时间计数单调递增,可以不是从0开始计数。

Running time

选定了clock之后pipeline会维护一个基于选定时钟的running_time。running_time指的是pipeline处于PLAYING状态下的时间总和,计算方法如下:

  • 如果pipeline处于NULL或者READY状态则running_time处于undefined。
  • PAUSE状态下,running_time的值保持不变,如果处于刚开始启动时的PAUSE状态,running_time的值为0。
  • flushing seek之后,running_time被置为0。这需要向所有被flush的elemnt指定一个新的base_time。

上述的计算方法在pipeline的状态从PLAYING状态设定为PAUSE状态时记录running_time,当从PAUSE状态转变为PLAYING状态后基于absolute_time恢复running_time。针对PAUSE后继续计数的clock,比如system clock,以及PAUSE后不再计数的clock,比如audioclock都是适用的。

running_time的计算方法如下:

C.running_time = absolute_time - base_time

Timestamps

GstBuffer的timestamps以及NEW_SEGMENT event定义了 buffer timestamps 到 running_time的变换

B: GstBuffer

  • B.timestamp = buffer timestamp (GST_BUFFER_TIMESTAMP)

NS:  NEWSEGMENT event preceeding the buffers.

  • NS.start: start field in the NEWSEGMENT event
  • NS.stop: stop field in the NEWSEGMENT event
  • NS.rate: rate field of NEWSEGMENT event
  • NS.abs_rate: absolute value of rate field of NEWSEGMENT event
  • NS.time: time field in the NEWSEGMENT event
  • NS.accum: total accumulated time of all previous NEWSEGMENT events. This field is kept in the GstSegment structure.

符合同步要求的buffers其B.timestamp需在NS.start和NS.stop之间,B.timestamp不在这个范围内的buffers需要丢掉或者进行修正。

对于running_time存在如下变换:

if (NS.rate > 0.0)
      B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
else
      B.running_time = (NS.stop - B.timestamp) / NS.abs_rate + NS.accum

B.running_time由NEWSEGMENT event以及该segment的buffers得到。

可显示的第一个buffer的running_time的值为0。

对于 NS.rate > 1.0,timestamps的值缩小从而使得播放速度加快。

For negative rates, timestamps are received stop NS.stop to NS.start so that the first buffer received will be transformed into B.running_time of 0 (B.timestamp == NS.stop and NS.accum == 0).

Synchronisation

对于running_time的计算方法如下:

  • 采用clock以及element的base_time:
C.running_time = absolute_time - base_time
  • 采用buffer timestamp和其前面的NEWSEGMENT event,假定为正向播放:
B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum

这里的前缀C.和B.代表不同的计算方法。

同步播放的目的就是确保running_time为B.running_time的buffer在C.running_time的时刻播放。

这需要满足如下条件:

B.running_time = C.running_time

也就是:

B.running_time = absolute_time - base_time

或者

absolute_time = B.running_time + base_time

具有B.running_time的buffer应当被播放时的absolute_time记为B.sync_time,那么:

B.sync_time = B.running_time + base_time

这意味着等到clock到了B.sync_time的时候才播放buffer,对于多个流中具有相同的running_time的buffer应该同时播放。

dumuxer必须确保向输出pads发出的NEWSEGMENT能为buffers生成一样的running_time,从而使之保持同步。通常向pads发出同样的NEWSEGMENT来确保同步的buffer具有一样的timestamp。

Stream time

stream time,也称作在流中的位置,是一个在0和媒体文件长度(时间)之间的值。具有如下用途:

  • report the POSITION query in the pipeline
  • the position used in seek events/queries
  • the position used to synchronize controller values

通过buffer和其前面的NEWSEGMENT event来计算stream time:

stream_time = (B.timestamp - NS.start) * NS.abs_applied_rate + NS.time

对于播放速度为负的情况,B.timestamp将从NS.stop 到 NS.start,使得stream time反向。在PLAYING 状态,也可以采用pipeline clock 计算当前的stream_time。 Give the two formulas above to match the clock times with buffer timestamps allows us to rewrite the above formula for stream_time (and for positive rates).

C.running_time = absolute_time - base_time

B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum

=>

(B.timestamp - NS.start) / NS.abs_rate + NS.accum = absolute_time - base_time;

=>

(B.timestamp - NS.start) / NS.abs_rate = absolute_time - base_time - NS.accum;

=>

(B.timestamp - NS.start) = (absolute_time - base_time - NS.accum) * NS.abs_rate

 filling (B.timestamp - NS.start) in the above formule for stream time

=>

stream_time = (absolute_time - base_time - NS.accum) * NS.abs_rate * NS.abs_applied_rate + NS.time

最后的计算公式通常是sink用于report当前的position的准确和有效的方式。Note that the stream time is never used for synchronisation against the clock.
### GStreamer 实现音视频同步的最佳实践与方案 GStreamer 是一种强大的多媒体处理框架,支持复杂的音频和视频流操作。为了实现高质量的音视频同步效果,在设计和开发过程中需遵循最佳实践并合理利用工具。 #### 音视频同步的核心原理 音视频同步的关键在于时间戳管理。GStreamer 使用 PTS(Presentation Time Stamp)来标记媒体帧的时间点[^1]。通过精确控制这些时间戳,可以确保音频和视频在同一时间轴上对齐。此外,还需要考虑缓冲区延迟、网络抖动等因素的影响。 #### 同步机制的设计方法 以下是几种常见的音视频同步设计方案: 1. **基于队列缓冲的同步** 在 GStreamer 中可以通过 `queue` 和 `sync` 属性来调节不同流之间的延迟差异。例如,当音频到达速度较快时,可适当增加其排队等待时间以匹配视频流的速度。 2. **使用 clock 对象统一调度** GStreamer 提供了一个全局时钟对象用于协调多个组件的工作节奏。开发者应该优先启用此特性,并确保所有源都绑定到同一个 master clock 上运行[^2]。 3. **动态调整播放速率** 如果检测到明显的脱节现象,则允许程序临时改变某些轨道(通常是次要的一方如背景音乐层)的回放速度直至恢复正常状态为止。这种方法虽然简单有效但也存在引入额外失真的风险因此要谨慎应用[^3]。 #### 示例代码展示 下面给出一段 Python 脚本演示如何构建基本的 AV Sync 流程: ```python import gi gi.require_version('Gst', '1.0') from gi.repository import Gst, GObject def create_pipeline(): pipeline = Gst.parse_launch(""" videotestsrc ! videoconvert ! autovideosink name=video_sink sync=true \ audiotestsrc freq=440 wave=sine volume=0.5 ! audioconvert ! autoaudiosink name=audio_sink sync=true """) bus = pipeline.get_bus() bus.add_signal_watch() return pipeline if __name__ == "__main__": GObject.threads_init() Gst.init(None) pl = create_pipeline() pl.set_state(Gst.State.PLAYING) loop = GObject.MainLoop() try: loop.run() except KeyboardInterrupt: pass pl.set_state(Gst.State.NULL) ``` 上述脚本创建了一条测试性的音画组合管线,并强制启用了两个输出端口上的同步选项。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值