您的位置:首页 > 其它

Gstreamer的音视频同步

2014-11-13 15:02 671 查看
(Original: http://blog.csdn.net/maeom/article/details/7729840)
一 概述

    Gstreamer的音频视频同步,概括起来是一个比较大的问题,因为在网上可以看到很多音视频同步的办法。这里我们只看最普通的一种。以音频时钟做为参考时钟(要求参考时钟上的时间是线性递增的);生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间);在播放时,读取数据上的时间戳,同时参考当前参考时钟上的时间来安排播放(如果数据块上的时间大于参考时钟的时间,则不急于播放,直到参考时钟达到数据块的开始时间;如果数据块上的时间小于参考时钟的时间,则应"尽快"播放或者干脆"丢弃"该数据块,以使得播放赶上播放进度。)

    Gstreamer的因视频分离器如下图:

    demux element将音频,视频分离后,给各自的解码器进行解码播放。      

[cpp] view
plaincopy

                  +-----------+  

                  |   Audio   |  

               +--|           |  

              /   +-----------+  

+----------+ /  

|  demux   |/  

|          |\  

+----------+ \  

              \   +-----------+  

               +--|   Video   |  

                  |           |  

                  +-----------+  

二  提供时钟

    默认情况下,是有AudioSink来提供参考时钟的。下面开始代码之旅。

[cpp] view
plaincopy

/* gst-plugins-base-0.10.32/gst-libs/gst/audio/gstbaseaudiosink.c */  

/*默认的情况下是由这个element来提供clock的。*/  

#define DEFAULT_PROVIDE_CLOCK   TRUE  

  

static void  

gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink,  

    GstBaseAudioSinkClass * g_class)  

{  

  baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK  

  /* 这里在clock类里面新建了一个时钟 */  

  baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock",  

    (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink);  

}  

  

/* 

 * 查询是否 @sink 将提供 clock 

 */  

gboolean  

gst_base_audio_sink_get_provide_clock (GstBaseAudioSink * sink)  

{  

  gboolean result;  

  result = sink->provide_clock;  

  return result;  

}  

  

/* 查询clock的时间 

 * 如果将这里的返回结果变慢,那么视频播放就会变慢。当然视频很音频就不同步了。 

 */  

static GstClockTime  

gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)  

{  

  result = gst_util_uint64_scale_int (samples, GST_SECOND,  

      sink->ringbuffer->spec.rate);  

  return result;  

}  

三 视频如何同步?

    以我实验的视频为例,视频使用的是xvimagesink element它的继承关系如下

[cpp] view
plaincopy

GObject  

 +----GstObject  

       +----GstElement  

             +----GstBaseSink  

                   +----GstVideoSink  

                         +----GstXvImageSink  

 从element的chain func开始(PS: 为什么从chain开始,参考[Gstreamer初见]).

[cpp] view
plaincopy

/* 

 * gst-plugins-base/sys/xvimage/xvimagesink.c 

 * gst-plugins-base/gst-libs/gst/video/gstvideosink.c 

 * 这两个文件里都没有chain函数. 

 * 在gstreamer-0.10.32/libs/gst/base/gstbasesink.c中 chain函数为 

 */  

static GstFlowReturn  

gst_base_sink_chain (GstPad * pad, GstBuffer * buf)  

{  

  basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));  

  

  return gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, buf);  

}  

  

static GstFlowReturn  

gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,  

    guint8 obj_type, gpointer obj)  

{  

  result = gst_base_sink_chain_unlocked (basesink, pad, obj_type, obj);  

}  

  

static GstFlowReturn  

gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,  

    guint8 obj_type, gpointer obj)  

{  

  result = gst_base_sink_queue_object_unlocked (basesink, pad,  

      obj_type, obj, TRUE);  

}  

  

static GstFlowReturn  

gst_base_sink_queue_object_unlocked (GstBaseSink * basesink, GstPad * pad,  

    guint8 obj_type, gpointer obj, gboolean prerollable)   

{  

  while (G_UNLIKELY (!g_queue_is_empty (q))) {  

    ret = gst_base_sink_render_object (basesink, pad, ot, o);  

  }  

}  

  

/* gstreamer-0.10.32/libs/gst/base/gstbasesink.c */  

static GstFlowReturn  

gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad,  

     guint8 obj_type, gpointer obj)  

{  

  /* 这里开始做同步,同步成功后,才开始播放 */  

  ret =  

     gst_base_sink_do_sync (basesink, pad, sync_obj, &late, &step_end,  

     obj_type);  

  if (G_UNLIKELY (ret != GST_FLOW_OK))  

    goto sync_failed;  

  

  if (!OBJ_IS_BUFFERLIST (obj_type)) {  

    ret = bclass->render (basesink, buf);  

  } else {  

    ret = bclass->render_list (basesink, buflist);  

  }  

}  

  

static GstFlowReturn  

gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,  

    GstMiniObject * obj, gboolean * late, gboolean * step_end, guint8 obj_type)  

{  

  status = gst_base_sink_wait_clock (basesink, stime, &jitter);  

  return GST_FLOW_OK;  

}  

  

  

/* 

 * @time: the running_time to be reached 

 * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL 

 * 

 * This function will block until @time is reached. It is usually called by 

 * subclasses that use their own internal synchronisation. 

 */  

GstClockReturn  

gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,  

    GstClockTimeDiff * jitter)  

{  

  if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (sink)) == NULL))  

    goto no_clock;  

  

  base_time = GST_ELEMENT_CAST (sink)->base_time;  

  

  sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);  

  

  /* 这里一直等待到时间 */  

  ret = gst_clock_id_wait (sink->priv->cached_clock_id, jitter);  

  

  return ret;  

}  

    这里同步完成,其实这里还有最后一个小问题,那么就是AudioClock是以什么为时钟的呢。其实就是以声卡的时钟为时钟的。因为声卡有时钟同步功能。所以我们计算一同播放了多少个sample,就可以计算出当前播放了多长的时间。 So. 

END
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息