1. 引言
我们知道多媒体的同步主要是为了能让多个流中具有相同时间戳(timestamp)的媒体资源在同一时刻被播放出来,比如播放电影时需要使得视频、音频同步的被播放出来,而不至于出现画面相对于声音超前或者落后的情况出现。
在Gstreamer中有一套同步机制,其所涉及到的内容包括buffer、segment、stream 以及 clock 。。。
2. Clock
GstClock 的时间精度为纳秒级,其时钟源的选择可以为系统时间、音频设备等。
GStPipline 会选择一个时钟源并分发到其他各个elements,GstClock 的值不必要从零开始。
3. Running time
Running time 是根据时钟计算出来的,也就是pipline出在PLAYING状态下的时间总和:
- 当pipline在NULL/READY转台下时,GstClock并没有被定义;
- 在PAUSED状态下,running time暂停计数;
- 在flushing seek操作之后,running time为0.
C.running_time = absolute_time - base_time
4. stream time
Stream time 可理解为在stream中的位置,其值介于零和媒体文件总长度之间。
stream time可以用于:
- 响应POSTION query;
- seek中的位置;
5. Buffer
struct GstBuffer {
/* the parent structure */
GstMiniObject mini_object;
/* pointer to the pool owner of the buffer */
GstBufferPool *pool;
/* timestamp */
/* presentation timestamp of the buffer, can be GST_CLOCK_TIME_NONE
when the pts is not known or relevant. The pts contains the timestamp
when the media should be presented to the user */
GstClockTime pts;
/* decoding timestamp of the buffer, can be GST_CLOCK_TIME_NONE
when the dts is not known or relevant. The dts contains the timestamp
when the media should be processed. */
GstClockTime dts;
/* duration in time of the buffer data, can be GST_CLOCK_TIME_NONE
when the duration is not known or relevant. */
GstClockTime duration;
/* media specific offset */
/* a media specific offset for the buffer data. For video frames, this is the
frame number of this buffer. For audio samples, this is the offset of the first
sample in this buffer. For file data or compressed data this is the byte offset
of the first byte in this buffer */
guint64 offset;
/* the last offset contained in this buffer. It has the same format as offset */
guint64 offset_end;
};
Buffer在Gstreamer框架中就是一个包含内存区域和metadata信息的对象,经过各个组件从上游传递到下游。
值得注意的是一个buffer可以挂载多个内存块,也可以附加任意的metadata。
由于buffer中的数据是可以存放在多个独立的内存块内的,因此对数据的访问可以有两种方式,其一是通过array of pointers逐个访问各个内存块,其二就是利用gst_buffer_map()和gst_buffer_map_unmap()将各个独立的内存块重新映射到单一完整的内存块中,这样访问起来会比较方便,但是这样是需要以内存拷贝为代价的。但是对于原本就是单一内存块的buffer来说,_map和_unmap()操作是不会产生内存拷贝行为的。
在GstBuffer中和我们今天要讨论的同步问题最相关的参数要数pts这个参数了,它是这个buffer的时间戳(timestamp),其对应的是stream time,也就是说当stream time的值到达与timestamp值相同的时刻时,该buffer中的媒体数据应该开始被播放出来。
6. Stream
Stream 其实就是媒体数据从产生、搬运、处理再到展示的这么一个过程。典型的stream会以start事件作为开始,紧接着是segment事件,在segment事件中会标示其后所跟buffer的timestamp的范围。在此之后,buffer会源源不断的被逐个传递直到EOS事件结束这个stream。
+-----+-------+ +-++-+ +-+ +---+
|START|SEGMENT| |B||B| ... |B| |EOS|
+-----+-------+ +-++-+ +-+ +---+
7. Segment
struct GstSegment {
/* flags for this segment */
GstSegmentFlags flags;
/* the rate of the segment */
gdouble rate;
/* the already applied rate to the segment */
gdouble applied_rate;
/* the format of the segment values */
GstFormat format;
/* the base of the segment */
guint64 base;
/* the offset to apply to start or stop */
guint64 offset;
/* the start of the segment */
guint64 start;
/* the stop of the segment */
guint64 stop;
/* the stream time of the segment */
guint64 time;
/* the position in the segment */
guint64 position;
/* the duration of the segment */
guint64 duration;
};
从上节我们知道,stream有start time(START)和stop time(EOS),而且start time总是为零,stop time则为媒体流的整个长度,如果stop time不确定可为-1.而segment则代表了stream中的一段媒体流,也有start和stop以及processing rate。
complete stream
+------------------------------------------------+
0 duration
segment
|--------------------------|
start stop
在播放过程中,segment 事件一般由source或者demuxer发出,其目的是为了通知下游的elements其所要请求的时刻,也就是要说明这个segment将要在stream time的什么位置被处理或者播放出来。
segment结构体中:
base成员表示该segment在当前running time上的位置;
time 成员表示该segment在当前stream time上的位置;
start、stop成员表示其可以接纳的time stamp范围。对于time stamp值不在该范围内的buffer将被丢弃。
对于一个buffer,其time stamp在当前running time和stream time的位置分别为:
running time
如果 S.rate > 0.0
running_time = (B.timestamp - S.start) / ABS (S.rate) + S.base
否则
running_time = (S.stop - B.timestamp) / ABS (S.rate) + S.base
stream time
stream_time = (B.timestamp - S.start) * ABS (S.applied_rate) + S.time
8. Synchronisation
综上所得,以时钟为参考的running time的计算值为
C.running_time = absolute_time - base_time
而buffer的time stamp对应的running time为:
B.running_time = (B.timestamp - (S.start + S.offset)) / ABS (S.rate) + S.base
所谓同步就是在 C.running_time 值增大到和 B.running_time 一样大时,播放相应buffer中的数据。也就是满足下面的公式:
B.running_time = C.running_time
对于多个流的情况,具有同样timestamp的buffer将同时被播放,这就要求demuxer确保其传递出去的同步的buffer具有相同的timestamp。
本文参考文献为Gstreamer 1.0 Core 设计文档:part-synchronisation.txt , part-buffer.txt , part-streams.txt , part-segments.txt