目录

Gstreamer的音视频同步-.

目录

Gstreamer的音视频同步 .

一 概述

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

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

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

[cpp]

  1. +———–+
  2. |   Audio   |
  3. +–|           |
  4. /   +———–+
  5. +———-+ /
  6. |  demux   |/
  7. |          |\
  8. +———-+ \
  9. \   +———–+
  10. +–|   Video   |
  11. |           |
  12. +———–+

二  提供时钟

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

[cpp]

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

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

  3. #define DEFAULT_PROVIDE_CLOCK   TRUE

  4. static

    void

  5. gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink,

  6. GstBaseAudioSinkClass * g_class)

  7. {

  8. baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK

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

  10. baseaudiosink->provided_clock = gst_audio_clock_new ( “GstAudioSinkClock” ,

  11. (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink);

  12. }

  13. /*

  14. * 查询是否 @sink 将提供 clock

  15. */

  16. gboolean

  17. gst_base_audio_sink_get_provide_clock (GstBaseAudioSink * sink)

  18. {

  19. gboolean result;

  20. result = sink->provide_clock;

  21. return result;

  22. }

  23. /* 查询clock的时间

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

  25. */

  26. static GstClockTime

  27. gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)

  28. {

  29. result = gst_util_uint64_scale_int (samples, GST_SECOND,

  30. sink->ringbuffer->spec.rate);

  31. return result;

  32. }

三 视频如何同步?

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

[cpp]

  1. GObject
  2. +—-GstObject
  3. +—-GstElement
  4. +—-GstBaseSink
  5. +—-GstVideoSink
  6. +—-GstXvImageSink

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

[cpp]

  1. /*

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

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

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

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

  6. */

  7. static GstFlowReturn

  8. gst_base_sink_chain (GstPad * pad, GstBuffer * buf)

  9. {

  10. basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));

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

  12. }

  13. static GstFlowReturn

  14. gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,

  15. guint8 obj_type, gpointer obj)

  16. {

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

  18. }

  19. static GstFlowReturn

  20. gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,

  21. guint8 obj_type, gpointer obj)

  22. {

  23. result = gst_base_sink_queue_object_unlocked (basesink, pad,

  24. obj_type, obj, TRUE);

  25. }

  26. static GstFlowReturn

  27. gst_base_sink_queue_object_unlocked (GstBaseSink * basesink, GstPad * pad,

  28. guint8 obj_type, gpointer obj, gboolean prerollable)

  29. {

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

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

  32. }

  33. }

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

  35. static GstFlowReturn

  36. gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad,

  37. guint8 obj_type, gpointer obj)

  38. {

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

  40. ret =

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

  42. obj_type);

  43. if (G_UNLIKELY (ret != GST_FLOW_OK))

  44. goto sync_failed;

  45. if (!OBJ_IS_BUFFERLIST (obj_type)) {

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

  47. } else {

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

  49. }

  50. }

  51. static GstFlowReturn

  52. gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,

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

  54. {

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

  56. return GST_FLOW_OK;

  57. }

  58. /*

  59. * @time: the running_time to be reached

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

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

  62. * subclasses that use their own internal synchronisation.

  63. */

  64. GstClockReturn

  65. gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,

  66. GstClockTimeDiff * jitter)

  67. {

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

  69. goto no_clock;

  70. base_time = GST_ELEMENT_CAST (sink)->base_time;

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

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

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

  74. return ret;

  75. }

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

END