GStreamer-时钟和同步

在播放复杂媒体时,每个声音和视频采样必须在特定时间以特定顺序播放。为此,GStreamer 提供了一种同步机制。

GStreamer 为以下用例提供支持:

  • 访问速度快于播放速度的非实时源。这是从文件中读取媒体并以同步方式播放的情况。在这种情况下,需要同步多个流,如音频、视频和字幕。

  • 从多个实时源捕获和同步混流/混音媒体。这是一个典型的用例,您从麦克风/摄像头录制音频和视频并将其合成到文件中进行存储。

  • 使用缓冲技术对(慢速)网络流进行流式传输。这种典型的 Web 流媒体案例是您使用 HTTP 从流媒体服务器访问内容。

  • 从实时源捕获并以可配置的延迟播放。例如,当从相机录制、附加一些效果并显示结果时,会使用此方法。当使用 UDP 通过网络流式传输低延迟内容时,也会使用它。

  • 从预先录制的内容中同时实时录制和播放。这用于播放以前录制的音频并录制新采样的音频录制情况,目的是使新音频与以前录制的数据完美同步。

GStreamer 使用 GstClock 对象、缓冲区时间戳和 SEGMENT 事件来同步pipeline中的流,我们将在下一节中看到。

时钟运行时间

在典型的计算机中,有很多来源可以用作时间源,例如系统时间、声卡、CPU 性能计数器等。为此,GStreamer 有许多可用的 GstClock 实现。 请注意,时钟时间不必从 0 或任何其他已知值开始。 一些时钟从特定的开始日期开始计数,其他时钟从上次重新启动等开始。

GstClock 使用 gst_clock_get_time () 根据该时钟返回绝对时间(absolute-time)。 时钟的绝对时间(absolute-time)(或时钟时间)是单调递增的。

运行时间(running-time)是称为基准时间(base-time)(绝对时的先前快照)与任何其他绝对时间之间的差。

running-time = absolute-time - base-time

GStreamer 中的 GstPipeline 对象在进入 PLAYING 状态时维护一个 GstClock 对象和一个基准时间。 pipeline将所选 GstClock 的句柄与所选基准时间一起提供给pipeline中的每个element。 pipeline将以这样一种方式选择一个基准时间,即运行时间反映在 PLAYING 状态中花费的总时间。 结果是,当pipeline暂停时,运行时间停止。

由于pipeline中的所有对象都具有相同的时钟和基准时间,因此它们都可以根据pipeline 时钟计算运行时间。

缓冲区运行时间

要计算缓冲区运行时间,我们需要缓冲区时间戳和缓冲区之前的 SEGMENT 事件。 首先我们可以将 SEGMENT 事件转换为 GstSegment 对象,然后我们可以使用 gst_segment_to_running_time() 函数来执行缓冲区运行时间的计算。

同步现在是确保在时钟达到相同运行时间时播放具有特定运行时间的缓冲区的问题。 通常,此任务由sink elements执行。 这些elements 还必须考虑已配置pipeline的延迟,并在与pipeline时钟同步之前将其添加到缓冲区运行时间。

非实时源时间戳缓冲区的运行时间从 0 开始。刷新查找后,它们将再次从运行时间 0 开始生成缓冲区。

当捕获缓冲区的第一个字节时,实时源需要使用与pipeline运行时间匹配的运行时间来为缓冲区添加时间戳。

缓冲流时间

缓冲区流时间,也称为在流中的位置,是一个介于 0 和媒体总持续时间之间的值,它是根据缓冲区时间戳和前面的 SEGMENT 事件计算得出的。

流时间用于:

  • 使用 POSITION 查询报告流中的当前位置。
  • 在seek事件和查询中使用的位置。
  • 用于同步受控值的位置。

流时间从不用于同步流,这仅通过运行时间完成。

时间概述

以下是 GStreamer 中使用的各种时间线的概述。

下图表示pipeline中播放 100 毫秒样本并在 50 毫秒和 100 毫秒之间重复播放时不同时间。
在这里插入图片描述

您可以看到缓冲区的运行时间如何始终与时钟时间一起单调递增。 缓冲区在其运行时间等于clock-time - base-time时播放。 stream-time 表示在流中的位置,重复时向后跳转。

时钟供提供者

时钟提供程序是pipeline中可以提供 GstClock 对象的element。当element处于 PLAYING 状态时,时钟对象需要报告一个单调递增的绝对时间。允许在element暂停时暂停时钟。

时钟提供程序的存在是因为它们以某种速率播放媒体,并且该速率不一定与系统时钟速率相同。例如,声卡可能以 44.1 kHz 播放,但这并不意味着根据系统时钟恰好在 1 秒后,声卡播放了 44100 个采样。这仅适用于近似值。事实上,音频设备有一个基于我们可以暴露的播放采样数量的内部时钟。

如果具有内部时钟的element需要同步,则需要根据内部时钟估计根据pipeline时钟的时间何时发生。为了估计这一点,它需要将其时钟从属于pipeline时钟。

如果pipeline时钟正好是某个element的内部时钟,则该element可以跳过从属这一步,直接使用pipeline时钟来调度播放。这可以更快更准确。因此,通常,具有内部时钟的element(如音频输入或输出设备)将成为pipeline的时钟提供者。

当pipeline进入 PLAYING 状态时,它将遍历pipeline中从sink 到source的所有element,并询问每个element是否可以提供时钟。可以提供时钟的最后一个element将用作pipeline中的时钟提供器。该算法更喜欢从典型的playback pipeline中选择audio sink的时钟和从典型capture pipeline中选择 source elements的时钟。

有一些总线消息可以让您了解pipeline中的时钟和时钟提供程序。您可以通过查看总线上的 NEW_CLOCK 消息来了解在pipeline中选择了哪个时钟。当时钟提供程序从pipeline中移除时,会发布一条 CLOCK_LOST 消息,应用程序应进入 PAUSED 再返回到 PLAYING 以选择新时钟。

延迟

延迟是在时间戳 X 捕获的采样到达sink所需的时间。该时间是根据pipeline中的时钟测量的。对于唯一与时钟同步的element是sink的pipeline,延迟始终为 0,因为没有其他element延迟缓冲区。

对于具有实时源的pipeline,会引入延迟,主要是因为实时源的工作方式。考虑一个音频源,它将在时间 0 开始捕获第一个样本。如果源以44100Hz的频率一次推送带有44100个样本的缓冲区,那么它将在1s处收集缓冲区。。由于缓冲区的时间戳为 0,时间为时钟现在 >= 1 秒,sink将丢弃此缓冲区,因为为时已晚。在sink中没有任何延迟补偿,所有缓冲区都将被丢弃。

延迟补偿

在pipeline 进入 PLAYING 状态之前,除了选择时钟和计算基准时间之外,它还将计算pipeline中的延迟。它通过对pipeline 中的所有sinks执行 LATENCY 查询来实现这一点。pipeline 然后选择其中的最大延迟并使用 这个值配置一个LATENCY 事件。

所有sinks elements 将按照LATENCY 事件中的值延迟播放 。由于所有sinks 的延迟时间相同,因此它们将相对同步。

动态延迟

向pipeline中添加/删除element 或更改element 属性可以更改pipeline中的延迟。element可以通过在总线上发布 LATENCY 消息来请求pipeline 中的延迟更改。然后,应用程序可以决定是否查询和重新分配新的延迟。更改pipeline 中的延迟可能会导致视觉或听觉故障,因此应仅在允许的情况下由应用程序完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值