gstreamer学习笔记 之 同步

1、gstreamer 的同步机制由如下几个组件组成


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

2、GstClock

    GstClock是精确到纳秒的表示当前时间的一个计数。其值用absolute_time表示

  •  系统时钟,精度是微妙。(gstSystemClock)
  •  音频设备时钟,精度是纳秒。(gstAudioClock)
  •  网络时钟,(GstRtpClock、GstNetClientClock)
  •  其它任何element提供的时钟。
     选择时钟源
  • 从媒体流的最上(most upstream)开始选择一个可以提供时钟的元素;
  •  如果管道中所有元素都不能提供时钟,则采用系统时钟。
     时钟发布       
        为了同步不同的元素,管道需要负责为管道中的所有元素选择和发布一个全局的时钟。
        时钟发布的时机包括:            
  •  管道进入PLAYING状态;
  •  添加一个可以提供时钟的元素;
  •  发送一个GST_MESSAGE_CLOCK_PROVIDE消息——>bus——>通知父bin——>选择一个clock——>发送一个NEW_CLOCK消息——>bus;
    时钟移除    
    发送一个CLOCK_LOST消息——>PAUSED——>PLAYING

3、running_time
      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 = absolute_time - base_time;

 如果pipeline采用系统时钟,由于系统时钟一直都在变,即absolute_time在变,pause状态时running_time不变,则base_time=absolute_time-running_time,此时base_time会变;
 如果pipeline采用的audioclock,absolute_time会根据输出的sample数来计算,所以pause状态时不会变,又pause状态时running_time不变,则base_time=absolute_time-running_time,此时base_time也不会变;

4. stream time

    Stream time 可理解为在stream中的位置,其值介于零和媒体文件总长度之间。
    stream time可以用于:
        - 响应POSTION query;
        - seek中的位置;

   


5、GstBuffer中的时间戳

  1. struct GstBuffer {   
  2.   /* the parent structure */  
  3.   GstMiniObject          mini_object;  
  4.   
  5.   /* pointer to the pool owner of the buffer */  
  6.   GstBufferPool         *pool;  
  7.   
  8.   /* timestamp */  
  9.   /* presentation timestamp of the buffer, can be GST_CLOCK_TIME_NONE  
  10.   when the pts is not known or relevant. The pts contains the timestamp  
  11.   when the media should be presented to the user */  
  12.   GstClockTime           pts;  
  13.   /* decoding timestamp of the buffer, can be GST_CLOCK_TIME_NONE 
  14.   when the dts is not known or relevant. The dts contains the timestamp 
  15.   when the media should be processed. */  
  16.   GstClockTime           dts;  
  17.   /* duration in time of the buffer data, can be GST_CLOCK_TIME_NONE 
  18.   when the duration is not known or relevant. */  
  19.   GstClockTime           duration;  
  20.   
  21.   /* media specific offset */  
  22.   /* a media specific offset for the buffer data. For video frames, this is the 
  23.   frame number of this buffer. For audio samples, this is the offset of the first 
  24.   sample in this buffer. For file data or compressed data this is the byte offset 
  25.   of the first byte in this buffer */  
  26.   guint64                offset;  
  27.   /* the last offset contained in this buffer. It has the same format as offset */  
  28.   guint64                offset_end;  
  29. };  
6、segment片段的时间戳

    Stream 其实就是媒体数据从产生、搬运、处理再到展示的这么一个过程。典型的stream会以start事件作为开始,紧接着是segment事件,在segment事件中会标示其后所跟buffer的timestamp的范围。在此之后,buffer会源源不断的被逐个传递直到EOS事件结束这个stream。
  +-----+-------+ +-++-+     +-+ +---+
  |START|SEGMENT| |B||B| ... |B| |EOS|
  +-----+-------+ +-++-+     +-+ +---+
  segment的结构如下:

  1. struct GstSegment {  
  2.   /* flags for this segment */  
  3.   GstSegmentFlags flags;  
  4.   
  5.   /* the rate of the segment */  
  6.   gdouble         rate;  
  7.   /* the already applied rate to the segment */  
  8.   gdouble         applied_rate;  
  9.   
  10.   /* the format of the segment values */  
  11.   GstFormat       format;  
  12.   /* the base of the segment */  
  13.   guint64         base;  
  14.   /* the offset to apply to start or stop */  
  15.   guint64         offset;  
  16.   /* the start of the segment */  
  17.   guint64         start;  
  18.   /* the stop of the segment */  
  19.   guint64         stop;  
  20.   /* the stream time of the segment */  
  21.   guint64         time;  
  22.   
  23.   /* the position in the segment */  
  24.   guint64         position;  
  25.   /* the duration of the segment */  
  26.   guint64         duration;  
  27. };  

从上节我们知道,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


延迟(latency)的计算与实现
1、延迟的引入
        管道中元素与时钟的同步仅仅发生在各个sink中,如果其他元素对buffer没有延迟的话,那么延迟就为0。延迟的引入主要是基于这样的考 虑,buffer从source推送到sink会花费一定的时间,从而可能导致buffer被丢弃。这个问题一般发生在活动管道,sink被设置为 PLAYING并且buffer没有被预送(preroll)至sink。

2、延迟的实现
        一般的解决方案是在被预送(preroll)之前所有的sink都不能设置为PLAYING状态。为了达到这样的目的,管道需要跟踪所有需要预送的元素 (就是在状态改变后返回ASYNC的元素),这些元素发送一个ASYNC_START消息,当元素进行预送,便把状态设置为PAUSED,同时发送一个 ASYNC_DONE消息,该消息恰好与之前的ASYNC_START相对应。当管道收集了所有的与ASYNC_START消息对应的 ASYNC_DONE消息以后便可以开始计算全局延迟了。

3、延迟的计算

7、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。




pipeline选择时钟过程:

gstelement struct的时钟:
  GstBus               *bus;

  /* allocated clock */
  GstClock             *clock;
  GstClockTimeDiff      base_time; /* NULL/READY: 0 - PAUSED: current time - PLAYING: difference to clock */
  GstClockTime          start_time;

gstpipeline struct:

struct _GstPipelinePrivate
{
  /* with LOCK */
  gboolean auto_flush_bus;

  /* when we need to update stream_time or clock when going back to
   * PLAYING*/
  GstClockTime last_start_time;
  gboolean update_clock;

  GstClockTime latency;
};
struct _GstPipeline {
  GstBin         bin;

  /*< public >*/ /* with LOCK */
  GstClock      *fixed_clock;

  GstClockTime   stream_time;
  GstClockTime   delay;

  /*< private >*/
  GstPipelinePrivate *priv;

  gpointer _gst_reserved[GST_PADDING];
};
struct _GstPipeline {
  GstBin         bin;


  /*< public >*/ /* with LOCK */
  GstClock      *fixed_clock;


  GstClockTime   stream_time;
  GstClockTime   delay;


  /*< private >*/
  GstPipelinePrivate *priv;


  gpointer _gst_reserved[GST_PADDING];
};

GstPlayBin3继承于GstPipelineClass继承于GstBinClass继承于GstElementClass。
因playbin在change state的时候会调用父类的change_state,所以会调用pipeline的change_state,pipeline在change state的时候会选择时钟。


 




gobject记录:

1、gtype是 A numerical value which represents the unique identifier of a registered type.
2、G_TYPE_CHAR:gchar对应的基本类型。
#define G_TYPE_CHAR			G_TYPE_MAKE_FUNDAMENTAL (3)






















你可能感兴趣的:(计算机)