在前边中我们学习过如何使用 GObject 的特性在应用程序和元件间进行简单交互。这个方法能够满足简单的直接设置,但对于比 set 和 get 更复杂的方法时就不行。为了满足一些更复杂的应用,GStreamer 使用了基于 Glib 中的 GInterface 类型的接口。这部分讲述的大多处理接口并没有给予相应代码。详情请参考 API 手册。在这里,我们仅讲述这些接口的范围和目的。
目前的例子中,我们仅支持本地文件作为数据源,这可以通过使用"filesrc"元件。明显地,GStreamer 支持更多来自不同路径的数据源。但是我们不需要应用程序知道一个特定元件的实现细节,比如来自特定网络的元件的名字等。因此就有了 URI 接口,它可用来得到一个源元件,这个元件可以有特定的 URI 类型。对于 URI 的命名没有严格的规则,但我们通常遵循一些已有的习惯。假如当你安装好正确的插件后, GStreamer 支持下边所示的路径。
file:////
http:////
mms:////
dvb://
...
为了让源元件或者接收元件支持特定的 URI,可以使用函数 gst_element_make_from_uri ()达到此目的。其中对源元件使用 GST_URI_SRC 类型,接收元件使用 GST_URI_SINK 类型
图象平衡接口给元件控制视频相关的性质提供了一种方法,例如光亮度、对比度等。这个元件存在的一个至关重要的原因就是:使用 GObject 还没有办法动态注册一些特性。图象平衡接口已经被很多插件所实现,像 xvimagesink、 Video4linux 以及 Video4linux2 等。
GstVideoOverlay创建该接口是为了解决将视频流嵌入到应用程序窗口中的问题。应用程序为实现此接口的元素提供了一个窗口句柄,然后元素将使用此窗口句柄进行绘制,而不是创建新的顶层窗口。这对于将视频嵌入视频播放器很有用。此接口实现,除其他外,该Video4linux2 元件通过glimagesink,ximagesink,xvimagesink和sdlvideosink。
播放复杂媒体时,每个声音和视频样本必须在特定时间以特定顺序播放。为此,GStreamer提供了一种同步机制。GStreamer为以下用例提供支持:
GStreamer使用一个GstClock对象,使用缓冲区时间戳和一个SEGMENT 事件来同步管道中的流。
在典型的计算机中,有许多源可以用作时间源,例如系统时间,声卡,CPU性能计数器等。因此,GStreamer具有许多GstClock可用的实现。请注意,时钟时间不一定非要从0或任何其他已知值开始。一些时钟从特定的开始日期开始计数,其他时钟从上次重启开始,依此类推。GstClock根据gst_clock_get_time()返回该时钟的绝对时间。时钟的绝对时间(或时钟时间)是单调增加。运行时间绝对时间与是先前事件的绝对时间(也称之为基础时间)之间的差。
running-time = absolute-time - base-time
管道进入PLAYING状态时会维护一个GstClock对象和一个基准时间。管道为管道的每个元件提供了选定GstClock中的句柄以及选定的基准时间。管道通过选择基准时间来计算该PLAYING 状态花费的总时间,当管道设为PAUSED为时,运行时间保持不变。
由于流水线中的所有对象都具有相同的时钟和基准时间,因此它们都可以根据流水线时钟来计算运行时间。
要计算缓冲区运行时间,我们需要一个缓冲区时间戳和该缓冲区之前的SEGMENT事件。首先,我们可以将 SEGMENT事件转换为GstSegment对象,然后可以使用该 gst_segment_to_running_time ()函数执行缓冲区运行时间的计算。
现在,同步是要确保在时钟达到相同运行时间时播放具有特定运行时间的缓冲区。通常,此任务由sink元件执行,这些元件与管道时钟同步时,还必须考虑配置的管道延迟,并将其添加到缓冲区运行时间。
非实时源时间戳缓冲区的运行时间从0开始。执行刷新定位后,它们将从运行时间0开始再次生成缓冲区。
当实时源捕获缓冲区的第一个字节时,需要使用运行时间与管道运行时间匹配的运行时间来标记缓冲区。
缓冲区流时间(也称为流中的位置)是介于0和媒体总持续时间之间的值,它是根据缓冲区时间戳和先前SEGMENT事件计算得出的。流时间用于:
流时间不能用于同步流,仅在计算运行时间时才使用。
这是GStreamer中使用的各种时间表的概述。
下图显示了播放100ms样本并重复50ms与100ms之间的部分时管道中的不同时间。
时钟提供者是管道中可以提供GstClock对象的元件 。时钟对象需要报告一个绝对时间,该绝对时间在元件处于PLAYING 状态时单调增加。允许在元件为PAUSED时暂停时钟。
之所以存在时钟提供者,是因为它们以某种速率播放媒体,并且此速率不一定与系统时钟速率相同。例如,声卡可能以44.1 kHz的速度播放,但这并不意味着在根据系统时钟恰好 1秒之后,声卡已经播放了44100个采样。仅通过近似是正确的。实际上,音频设备具有一个内部时钟,该时钟取决于我们可以播放的采样数。
如果具有内部时钟的元件需要同步,则需要根据内部时钟来估计何时根据管道时钟发生时间。为了估计这一点,它需要将其时钟从属于管道时钟。
如果管道时钟恰好是元件的内部时钟,则该元件可以跳过从属步骤,而直接使用管道时钟来计划播放。这可以更快,更准确。因此,通常,具有内部时钟的元件(例如音频输入或输出设备)将成为管道的时钟提供者。
当管道进入PLAYING状态时,它将遍历管道中从sink到source的所有元件,并询问每个元件是否可以提供时钟。可以提供时钟的最后一个元件将用作管道中的时钟提供者。该算法更喜欢典型回放管道中来自音频接收器的时钟和典型捕获管道中来自源元件的时钟。
存在一些总线消息,以使您了解管道中的时钟和时钟提供程序。通过查看NEW_CLOCK总线上的消息,可以看到在管道中选择了什么时钟。从管道中删除时钟提供者后,将CLOCK_LOST发布一条消息,并且应用程序从PAUSED到PLAYING时选择一个新的时钟。
等待时间是在时间戳X处捕获的样本到达接收器所花费的时间。此时间是根据管道中的时钟进行测量的。对于唯一与时钟同步的元件是接收器的管道,由于没有其他元件延迟缓冲区,因此延迟始终为0。
对于具有实时源的管道,引入了延迟,这主要是因为实时源的工作方式。考虑一个音频源,它将在时间0开始捕获第一个采样。如果该源以44100Hz的频率一次推送具有44100个采样的缓冲区,它将在第二个1处收集该缓冲区。由于该缓冲区的时间戳为0,该时间为现在是>= 1第二个时钟,接收器将丢弃此缓冲区,因为为时已晚。在接收器中没有任何延迟补偿的情况下,所有缓冲区将被丢弃。
在管道进入PLAYING状态之前,除了选择时钟和计算基准时间外,还将计算管道中的等待时间。它通过LATENCY对管道中的所有接收器进行查询来做到这一点。然后,管道选择管道中的最大延迟,并使用LATENCY事件对其进行配置。
所有接收器元件都将延迟LATENCY事件中的值播放。由于所有接收器都以相同的时间延迟,因此它们将相对同步。
向管道添加元件或从管道中删除元件或更改元件属性可以更改管道中的延迟。元件可以通过LATENCY在总线上发布消息来请求更改管道中的延迟。然后,应用程序可以决定是否查询和重新分配新的延迟。更改管道中的延迟可能会导致视觉或听觉故障,因此仅应在允许的情况下由应用程序完成。