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;
}

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