深入淺出gstreamer的調度模式 .

gstreamer的官方文檔裏,調度的英文是schedule. 什麼是schedule? 它的英文解釋爲" to plan that something will happen at a particular time “.在gstreamer裏,調度的意義跟它基本差不多。在gstreamer裏,調度的目的主要有以下幾個:

      (1)在適當的時候通知每個元件去完成相應的任務,確保這些職責的執行。這個任務,就是數據處理。

      (2)每個元件都從它的上一個源元件獲取數據,併爲下一個元件準備好數據。即獲取輸入數據處理數據,最後輸出數據給下一個元件。   

       GStreamer中,襯墊(pad)是用來在元件間協商連接和數據流的。襯墊可以看作元件間互相連接的接口,數據流通過這些接口流入流出元件。Pad具有特殊的數據處理能力:襯墊可以限制通過它的數據類型。只有當兩個襯墊允許通過的數據類型兼容時纔可以將它們連接起來。

    第一個source元件,只有src襯墊,即只輸出數據;最後一個sink(輸出)元件,只有sink襯墊,即只接收數據;管道中其他元件,一般都有一個sink襯墊和source襯墊。

    Gstreamer的調度,也可以理解爲不同元件的pad連接和通信,即處理pad之間的數據流的啓動,暫停,繼續和終止。

      調度模式有兩種:PUSH模式和PULL模式。在此,我將其翻譯爲推送模式和拉拽模式。

1.什麼是推送模式和拉拽模式?

      Downstream(下游) 和 Upstream(上游)模式是管道中用於描述數據流動方向的術語。從source元件到sink元件的數據流動稱作“下游”,從sink元件到source元件的數據流動模式稱爲“上游”模式。

       在推送模式下,上游元件通過調用gst_pad_push,實現把數據“推送”給下游元件。

       在拉拽模式下,下游元件通過請求調用gst_pad_pull_range(),從上游組件那裏把數據“拉”過來。

     上面提到的數據,一般是一個緩存(buffer).

      用得最多的數據流模式是推送模式。拉拽模式可以用在某些特殊情況下,如demuxer元件。

2.推送模式是怎麼工作的?

       
  .----------.             .----------.           .----------.
  | element1 |      | element2 |      | element3 |
 ...        src -> sink       src -> sink       ...
  '----------'               '----------'            '----------'      

      如上圖所示,假設元件1的src pad工作在push模式。當元件1打算把數據推送給下一個與之相連接的元件2時,元件1的src pad通常產生數據,元件2的相應sink pad(相對於元件1,被稱爲peer pad)接受數據。通常,元件1會實現一個loop函數,該函數會被循環調用直到它返回值爲假。該函數可以根據需要進行阻塞循環調用,但當元件1的pad解除激活時,該函數就解除阻塞(即不再調用)。

       元件2的pad通過實現一個鏈條(chain)函數來接受元件1的pad生產的數據。

       push模式的實現是由元件1通過gst_pad_push()來實現的. 產生數據的元件pad, 注意:這個方法根據傳入的pad參數,具體調用相應的鏈接函數xx_chain。該方法的函數原型是GstFlowReturngst_pad_push (GstPad * pad, GstBuffer * buffer)。在該函數的實現裏,通過調用 GST_PAD_CHAINFUNC (peer) (peer, buffer)來把數據推送給下一個元件,同時下一個元件根據他來接受數據。

      總之,推送模式下,源元件發起數據傳輸,是管道中的驅動力量;下游元件在chain函數中接收buffer.

這樣,就完成了從上游元件到下游元件的buffer傳遞。

 

3.拉拽模式是怎麼工作的?

      工作在PULL模式的pad, 通常只能從實現了pull_range函數的pad那裏“拉”數據。

   .----------.             .----------.          .----------.         .----------.
  | element1 |      | element2 |      | demuxer|    |decoder|
 ...        src -> sink            src -> sink          src -> sink     ...
   '----------'             '----------'           '----------'            ----------'     

        如上圖所示,我們假設demuxer元件的sink pad工作在pull模式,並是以plugin的形式加載到工作管道中。在demuxer對象實例初始化時候,我們一般首先會新建和初始化一個sink pad,並通過調用gst_pad_set_activate_function (avi->sinkpad, gst_xx_demux_sink_activate)來設置pad激活時的函數指針。於是,當pipeline狀態變化時,尤其是demuxer元件從READY切換到PAUSE狀態時,會進行如下圖所示的函數調用:

 

在上圖的激活函數裏,會去激活demuxer 的sink pad, 它是通過遍歷所有的sink pads,調用activate_pads來激活pad的。如下圖所示:

在gst_pad_set_actie函數裏,最後會調用(GST_PAD_ACTIVATEFUNC(pad)) (pad); 被調用這個宏函數由於在demuxer元件裏設置了函數指針gst_pad_set_activate_function (avi->sinkpad, gst_xx_demux_sink_activate),於是其實對於demuxer組件來說,執行的是gst_xx_demux_sink_activate函數。

     常,這個函數會調用gst_pad_check_pull_range,並根據前面圖中的element2的pad(稱之爲peer pad)是否實現了XX_pull_range函數,來判斷demuxer 元件的sink pad應該激活在pull模式還是push模式我們這裏討論激活在pull模式的情況。

         當激活在pull模式下,我們會給demux起一個task, task函數爲gst_xx_demux_loop函數。gst_avi_demux_loop函數會從peer pad 拉拽數據。拉拽完數據後,通常會把數據push 到下一個元件的sink pad.

 

4.調度模式是如何動態決定的

      這個問題的答案可以在上面第3節找到找到。這裏總結下:當一個pad被激活時,gst_element_pad_activate()函數被調用. 這個pad隨後可以根據上游的能力(upstream capabilities,實際上爲是否實現了XX_pull_range)來決定激活在push模式還是pull模式。如果一個pad沒有activate函數,gstreamer core將默認爲它激活在push模式。

 

5.getrange函數

     當gst_pad_check_pull_range調用時,peer pad的getrange函數被調用。事實上,前面說過,一個上游pad只有實現了getrange函數,才能被下游的pad以pull的模式調用它從而拉拽數據。下游pad對getrange函數的調用,是通過以宏的形式---GST_PAD_GETRANGEFUNC (pad)來傳遞的。而該宏的賦值,是上游的可以支持pull模式的元件在初始化的時候,調用gst_pad_set_getrange_function 設置的。

6.chain函數(鏈條函數)

    當上遊元件的pad調用gst_pad_push函數時,下游元件pad的chain函數被調用

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