文章

gstreamer插件编写指南:时钟

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

时钟

GStreamer 中的时间定义为特定GstClock对象通过gst_clock_get_time () 方法返回的值。

在典型的计算机中,有许多时间源可以用作时间源,例如系统时间、声卡、CPU 性能计数器……因此,GStreamer 中有许多GstClock实现。时钟时间并不总是从 0 或某个已知值开始计算。有些时钟从某个已知的起始日期开始计时,有些时钟则从上次重启开始计时,等等……。

由于时钟返回的是绝对时间,因此通常不会直接使用。取而代之的是,利用两个时钟时间之间的差值来测量时钟的经过时间。

时钟运行时间

通过使用gst_clock_get_time()函数,时钟会根据该时钟返回绝对时间。根据绝对时间计算出的运行时间,就是绝对时间的前一个快照(称为基准时间)之间的差值。因此:

运行时间 = 绝对时间 - 基准时间

GStreamerGstPipeline对象在进入播放状态时会维护一个GstClock对象和一个基准时间。流水线为流水线中的每个元素提供一个选定GstClock的句柄和选定的基准时间。流水线在选择基准时间时,运行时间会反映出在播放状态下花费的总时间。因此,当流水线暂停时,运行时间将保持不变。

由于流水线中的所有对象都有相同的时钟和基准时间,因此它们都能根据流水线时钟计算运行时间。

缓冲区运行时间

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

现在,同步的问题是确保在时钟达到相同的运行时间时,播放具有特定运行时间的缓冲区。通常这项任务由汇元素完成。汇元素还必须考虑到流水线中配置的延迟,并在与流水线时钟同步之前将其添加到缓冲区的运行时间中。

每个元素的义务

让我们澄清一下 GStreamer 和流水线中每个元素之间的契约。

非实时源元素

在可能的情况下,非实时源元素必须在其传送的每个缓冲区中放置一个时间戳。它们必须以缓冲区的运行时间从 0 开始的方式选择时间戳和 SEGMENT 事件的值。

某些程序源(如 filesrc)无法在所有缓冲区上生成时间戳。但它可以也必须在第一个缓冲区(运行时间为 0)上创建时间戳。

然后,信号源推送 SEGMENT 事件,接着是带有时间戳的缓冲区。

实时源元素

实时源元素必须在其传送的每个缓冲区中放置一个时间戳。它们在选择时间戳和 SEGMENT 事件值时,必须确保缓冲区的运行时间与捕获缓冲区中第一个字节时流水线时钟的运行时间完全一致。

解析器/解码器/编码器元素

解析器/解码器元素必须使用输入的时间戳,并将这些时间戳传输到生成的输出缓冲区。在可能的情况下,它们可以对丢失的输入缓冲区进行内插或重建时间戳。

解复用器元素

解复用器元素通常可以将存储在媒体文件中的时间戳设置到输出缓冲区上。它们需要确保同时播放的输出缓冲区具有相同的运行时间。解复用器还需要考虑缓冲区上的传入时间戳,并以此计算传出缓冲区时间戳的偏移量。

复用器元素

多路复用器元素应使用传入缓冲区的运行时间将不同的数据流进行多路复用。它们应将输入缓冲区的运行时间复制到输出缓冲区。

汇元素

如果元素的目的是在特定时间(实时播放)输出采样,则该元素应需要时钟,并因此实现set_clock 方法。

然后,当流水线时钟达到运行时间 + 延迟时,管道应确保准确播放带有运行时间的采样。有些元素可能会使用gst_clock_id_wait()等时钟 API 来执行此操作。其他汇可能需要使用其他方式来安排数据的及时播放。

本文由作者按照 CC BY 4.0 进行授权