Gstreamer的音視頻同步 .

一 概述

    Gstreamer的音頻視頻同步,概括起來是一個比較大的問題,因爲在網上可以看到很多音視頻同步的辦法。這裏我們只看最普通的一種。以音頻時鐘做爲參考時鐘(要求參考時鐘上的時間是線性遞增的);生成數據流時依據參考時鐘上的時間給每個數據塊都打上時間戳(一般包括開始時間和結束時間);在播放時,讀取數據上的時間戳,同時參考當前參考時鐘上的時間來安排播放(如果數據塊上的時間大於參考時鐘的時間,則不急於播放,直到參考時鐘達到數據塊的開始時間;如果數據塊上的時間小於參考時鐘的時間,則應"儘快"播放或者乾脆"丟棄"該數據塊,以使得播放趕上播放進度。)

    Gstreamer的音視頻分離器如下圖:

    demux element將音頻,視頻分離後,給各自的解碼器進行解碼播放。     

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

二  提供時鐘

    默認情況下,是有AudioSink來提供參考時鐘的。下面開始代碼之旅。

  1. /* gst-plugins-base-0.10.32/gst-libs/gst/audio/gstbaseaudiosink.c */  
  2. /*默認的情況下是由這個element來提供clock的。*/  
  3. #define DEFAULT_PROVIDE_CLOCK   TRUE  
  4.   
  5. static void  
  6. gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink,  
  7.     GstBaseAudioSinkClass * g_class)  
  8. {  
  9.   baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK  
  10.   /* 這裏在clock類裏面新建了一個時鐘 */  
  11.   baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock",  
  12.     (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink);  
  13. }  
  14.   
  15. /* 
  16.  * 查詢是否 @sink 將提供 clock 
  17.  */  
  18. gboolean  
  19. gst_base_audio_sink_get_provide_clock (GstBaseAudioSink * sink)  
  20. {  
  21.   gboolean result;  
  22.   result = sink->provide_clock;  
  23.   return result;  
  24. }  
  25.   
  26. /* 查詢clock的時間 
  27.  * 如果將這裏的返回結果變慢,那麼視頻播放就會變慢。當然視頻很音頻就不同步了。 
  28.  */  
  29. static GstClockTime  
  30. gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)  
  31. {  
  32.   result = gst_util_uint64_scale_int (samples, GST_SECOND,  
  33.       sink->ringbuffer->spec.rate);  
  34.   return result;  
  35. }  
三 視頻如何同步?

    以我實驗的視頻爲例,視頻使用的是xvimagesink element它的繼承關係如下

  1. GObject  
  2.  +----GstObject  
  3.        +----GstElement  
  4.              +----GstBaseSink  
  5.                    +----GstVideoSink  
  6.                          +----GstXvImageSink  
 從element的chain func開始(PS: 爲什麼從chain開始,參考[Gstreamer初見]).

  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.   
  12.   return gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, buf);  
  13. }  
  14.   
  15. static GstFlowReturn  
  16. gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,  
  17.     guint8 obj_type, gpointer obj)  
  18. {  
  19.   result = gst_base_sink_chain_unlocked (basesink, pad, obj_type, obj);  
  20. }  
  21.   
  22. static GstFlowReturn  
  23. gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,  
  24.     guint8 obj_type, gpointer obj)  
  25. {  
  26.   result = gst_base_sink_queue_object_unlocked (basesink, pad,  
  27.       obj_type, obj, TRUE);  
  28. }  
  29.   
  30. static GstFlowReturn  
  31. gst_base_sink_queue_object_unlocked (GstBaseSink * basesink, GstPad * pad,  
  32.     guint8 obj_type, gpointer obj, gboolean prerollable)   
  33. {  
  34.   while (G_UNLIKELY (!g_queue_is_empty (q))) {  
  35.     ret = gst_base_sink_render_object (basesink, pad, ot, o);  
  36.   }  
  37. }  
  38.   
  39. /* gstreamer-0.10.32/libs/gst/base/gstbasesink.c */  
  40. static GstFlowReturn  
  41. gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad,  
  42.      guint8 obj_type, gpointer obj)  
  43. {  
  44.   /* 這裏開始做同步,同步成功後,纔開始播放 */  
  45.   ret =  
  46.      gst_base_sink_do_sync (basesink, pad, sync_obj, &late, &step_end,  
  47.      obj_type);  
  48.   if (G_UNLIKELY (ret != GST_FLOW_OK))  
  49.     goto sync_failed;  
  50.   
  51.   if (!OBJ_IS_BUFFERLIST (obj_type)) {  
  52.     ret = bclass->render (basesink, buf);  
  53.   } else {  
  54.     ret = bclass->render_list (basesink, buflist);  
  55.   }  
  56. }  
  57.   
  58. static GstFlowReturn  
  59. gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,  
  60.     GstMiniObject * obj, gboolean * late, gboolean * step_end, guint8 obj_type)  
  61. {  
  62.   status = gst_base_sink_wait_clock (basesink, stime, &jitter);  
  63.   return GST_FLOW_OK;  
  64. }  
  65.   
  66.   
  67. /* 
  68.  * @time: the running_time to be reached 
  69.  * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL 
  70.  * 
  71.  * This function will block until @time is reached. It is usually called by 
  72.  * subclasses that use their own internal synchronisation. 
  73.  */  
  74. GstClockReturn  
  75. gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,  
  76.     GstClockTimeDiff * jitter)  
  77. {  
  78.   if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (sink)) == NULL))  
  79.     goto no_clock;  
  80.   
  81.   base_time = GST_ELEMENT_CAST (sink)->base_time;  
  82.   
  83.   sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);  
  84.   
  85.   /* 這裏一直等待到時間 */  
  86.   ret = gst_clock_id_wait (sink->priv->cached_clock_id, jitter);  
  87.   
  88.   return ret;  
  89. }  

    這裏同步完成,其實這裏還有最後一個小問題,那麼就是AudioClock是以什麼爲時鐘的呢。其實就是以聲卡的時鐘爲時鐘的。因爲聲卡有時鐘同步功能。所以我們計算一同播放了多少個sample,就可以計算出當前播放了多長的時間。 So.

END

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