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