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