gstreamer时间管理

    PCM早些时候一般用于电话语音传输,电话语音的频率范围是从0-3.4kHz,根据奈奎斯特采样,只要高于最高频率的两倍,就可以实现声音不失真的还原,故只要采样率大于3.4k*2,即可还原电话语音,采用8000Hz只是一种ITU(国际电信联盟)规定,实际上只要大于6.8kHz,都可以实现采样。所以为什么是8000这个数字,可以认为仅仅是一个约定而已。

  GstClock用_get_time()返回单调递增的时间,它的精度和base time取决于时钟实现,但总是以纳秒为单位。既然时钟的基线没有被定义,时钟返回的值本身毫无意义,只有两个时钟之间的差值才有意义。

GStreamer的时间同步涉及3种时间:stream time是基于数据本身,segment就是以stream time为参考。running time基于播放花费的时间,除了数据本身,还与播放速度有关。Clock time是参考时钟的时间,可能由系统、音频设备或其他源提供。

Gstreamer时间同步的基本做法是:

  • a. segment开始播放时,记录seg.start对应的clock_time,也就是base_time;
  • b. 播放某一帧时,根据其stream time计算对应的clock time,也就是dst_time。
  • c. 将dst_time于当前的clock_time比较,如果早了,则帧已经过时;如果刚好等于,则刚好显示它;如果晚了,则需要等待(dst_time - cur_clock_time),再显示它。

步骤b的计算公式为:

cpos = (pos - seg.start)/abs_rate + base_time

其中,abs_rate为播放速度,如两倍速为2,1/2慢速播为0.5。由于数据在上下游element之间传输有延迟, gstreamer对这个公式有一些细微的调整。

在gst_pipeline_provide_clock_func()中,首先检查pipeline是否强制指定了参考时钟,是则返回;再调用gst_bin_provide_clock_func()遍历所有子element,最后在找到的时钟中,选择等级最高的,如果找到则返回;如果还是没找到,则调用gst_system_clock_obtain()创建GstSystemClock并返回。

在gst_pipeline_set_clock()中,调用gst_bin_set_clock_func()将新的时钟设置到所有子element。
Gst_clock_get_time()得到当前时间,并根据它设置pipeline的base_time。

在gst_queue_loop()中gst_base_sink_do_sync()负责时间同步,gst_video_sink_show_frame()负责显示帧。

时钟的提供与同步原理
clock providers:
        由于媒体播放的频率与全局时钟的频率不一定相一致,需要元素提供一个时间,使得按照其需要的频率进行播放。主要负责保证时钟与当前媒体时间相一致。需要维护播放延迟、缓冲时间管理等,影响A/V同步等。

clock slaves:
        负责从包含他们的管道中获得分配一个时钟。常常需要调用gst_clock_id_wait()来等待播放他们当前的sample,或者丢弃。
        当一个时钟被标记为GST_CLOCK_FLAG_CAN_SET_MASTER时,它可以通过设置从属于另一个时钟,随后通过不断地校正使得它与从属的 主时钟进行同步。主要用于当组件提供一个内部时钟,而此时管道又分配了一个时钟时,通过不断地校正从而使得它们之间同步。        在主从时钟的机制中又引入了内部时间和外部时间的概念:
        内部时间:时钟自己提供的时间,未做调整的时间;
        外部时间:在内部时间基础上,经过校正的时间。
        在clock中保存着internal_calibration, external_calibration, rate_numerator, rate_denominator属性,并利用这些值校正出外部时间,校正公式为
        external = (internal – cinternal) * cnum / cdenom + cexternal;
        其中external, internal, cinternal, cnum, cdenom, cexternal分别表示外部时间,内部时间,clock中保存的内部校正值,校正率分子,校正率分母,外部校正值。

同步

视频同步例子

 

gst_base_sink_query_latency

/**
 * gst_base_sink_query_latency:
 * @sink: the sink
 * @live: (out) (allow-none): if the sink is live
 * @upstream_live: (out) (allow-none): if an upstream element is live
 * @min_latency: (out) (allow-none): the min latency of the upstream elements
 * @max_latency: (out) (allow-none): the max latency of the upstream elements
 *
 * Query the sink for the latency parameters. The latency will be queried from
 * the upstream elements. @live will be TRUE if @sink is configured to
 * synchronize against the clock. @upstream_live will be TRUE if an upstream
 * element is live.
 *
 * If both @live and @upstream_live are TRUE, the sink will want to compensate
 * for the latency introduced by the upstream elements by setting the
 * @min_latency to a strictly possitive value.
 *
 * This function is mostly used by subclasses.
 *
 * Returns: TRUE if the query succeeded.
 *
 * Since: 0.10.12
 */
gboolean
gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
    gboolean * upstream_live, GstClockTime * min_latency,
    GstClockTime * max_latency)
{
  gboolean l, us_live, res, have_latency;
  GstClockTime min, max, render_delay;
  GstQuery *query;
  GstClockTime us_min, us_max;

  /* we are live when we sync to the clock */
  GST_OBJECT_LOCK (sink);
  l = sink->sync;
  have_latency = sink->priv->have_latency;
  render_delay = sink->priv->render_delay;
  GST_OBJECT_UNLOCK (sink);

  /* assume no latency */
  min = 0;
  max = -1;
  us_live = FALSE;

  if (have_latency) {
    GST_DEBUG_OBJECT (sink, "we are ready for LATENCY query");
    /* we are ready for a latency query this is when we preroll or when we are
     * not async. */
    query = gst_query_new_latency ();

    /* ask the peer for the latency */
    if ((res = gst_pad_peer_query (sink->sinkpad, query))) {
      /* get upstream min and max latency */
      gst_query_parse_latency (query, &us_live, &us_min, &us_max);

      if (us_live) {
        /* upstream live, use its latency, subclasses should use these
         * values to create the complete latency. */
        min = us_min;   //50000000
        max = us_max;
      }
      if (l) {
        /* we need to add the render delay if we are live */
        if (min != -1)
          min += render_delay;  //min=70000000, render_delay=20000000
        if (max != -1)
          max += render_delay;
      }
    }
    gst_query_unref (query);
  } else {
    GST_DEBUG_OBJECT (sink, "we are not yet ready for LATENCY query");
    res = FALSE;
  }

  /* not live, we tried to do the query, if it failed we return TRUE anyway */
  if (!res) {
    if (!l) {
      res = TRUE;
      GST_DEBUG_OBJECT (sink, "latency query failed but we are not live");
    } else {
      GST_DEBUG_OBJECT (sink, "latency query failed and we are live");
    }
  }

  if (res) {
    GST_DEBUG_OBJECT (sink, "latency query: live: %d, have_latency %d,"
        " upstream: %d, min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, l,
        have_latency, us_live, GST_TIME_ARGS (min), GST_TIME_ARGS (max));

    if (live)
      *live = l;
    if (upstream_live)
      *upstream_live = us_live;
    if (min_latency)
      *min_latency = min; 
    if (max_latency)
      *max_latency = max;
  }
  return res;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章