天天看點

Gstreamer- 協商(Negotiation)

協商

Capabilities 協商是為 GStreamer pipeline内的資料流決定适當格式的過程。理想情況下,協商(也稱為“capsnego”)将資訊從pipeline中具有資訊的那些部分傳輸到pipeline的那些易擴充的部分,受pipeline中不易擴充部分的限制。

基本規則

必須遵循這些簡單的規則:

  1. 下遊建議格式
  2. 上遊決定格式

caps協商中使用了 4 種 查詢/事件(queries/events ):

  1. GST_QUERY_CAPS:擷取可能的格式
  2. GST_QUERY_ACCEPT_CAPS:檢查格式是否可行
  3. GST_EVENT_CAPS:配置格式(下遊)
  4. GST_EVENT_RECONFIGURE:通知上遊可能的新caps

查詢

pad可以向peer pad詢問其支援的 GstCaps。它通過 CAPS 查詢完成此操作。支援的caps清單可用于為資料傳輸選擇合适的 GstCap。 CAPS 查詢以遞歸方式工作,element在建構可能的caps時應考慮其 peer element。由于結果caps可能非常大,是以可以使用過濾器來限制caps。隻有與過濾器比對的caps才會作為結果caps傳回。過濾器caps的順序給出了調用者的優先順序,并且應該考慮到傳回的caps。

  • filter (in) GST_TYPE_CAPS (default NULL): - 一個用于過濾結果的 GstCaps
  • caps (out) GST_TYPE_CAPS (default NULL): - 結果caps

pad可以詢問peer pad 是否支援給定的caps。它通過 ACCEPT_CAPS 查詢完成此操作。caps必須固定。 ACCEPT_CAPS 查詢不需要遞歸工作,如果帶有這些caps的後續 CAPS 事件傳回成功,它可以簡單地傳回 TRUE。

  • caps (in) GST_TYPE_CAPS: - 要檢查的 GstCaps,必須固定
  • result (輸出)G_TYPE_BOOLEAN(預設為 FALSE): - 如果接受caps則為 TRUE

事件

當協商媒體格式時,通過 CAPS 事件将 GstCaps 通知給peer element。caps必須固定。

  • caps GST_TYPE_CAPS: - 協商的 GstCaps,必須是固定的

操作

GStreamer 的兩種排程模式,push 模式和 pull 模式,适用于不同的機制來實作這個目标。由于更常見,我們首先描述推模式協商。

推模式協商

當element想要推送緩沖區并需要決定格式時,會發生推模式協商。這稱為下遊協商,因為上遊element決定下遊element的格式。這是最常見的情況。

當下遊element想要從上遊element接收另一種資料格式時,也會發生協商。這稱為上遊協商。

協商的基本原則如下:

  • GstCaps(參見 caps)在它們作為事件推送之前被引用以描述随後的緩沖區的内容。
  • 在處理随後的緩沖區之前,element應将自身重新配置為作為 CAPS 事件接收的新格式。如果 caps 事件中的資料類型不可接受,則element應拒絕該事件。該element還應通過從鍊函數傳回适當的 GST_FLOW_NOT_NEGOTIATED 傳回值來拒絕下一個緩沖區。
  • 下遊element可以通過向上遊發送 RECONFIGURE 事件來請求流的格式更改。上遊element在收到 RECONFIGURE 事件時将重新協商新格式。

source pad開始協商的一般流程。

src              sink
             |                 |
             |  querycaps?     |
             |---------------->|
             |     caps        |
select caps  |< - - - - - - - -|
from the     |                 |
candidates   |                 |
             |                 |-.
             |  accepts?       | |
 type A      |---------------->| | optional
             |      yes        | |
             |< - - - - - - - -| |
             |                 |-'
             |  send_event()   |
send CAPS    |---------------->| Receive type A, reconfigure to
event A      |                 | process type A.
             |                 |
             |  push           |
push buffer  |---------------->| Process buffer of type A
             |                 |
           

一種可能的實作方式的僞代碼:

[element wants to create a buffer]
    if not format
      # see what we can do
      ourcaps = gst_pad_query_caps (srcpad)
      # see what the peer can do filtered against our caps
      candidates = gst_pad_peer_query_caps (srcpad, ourcaps)

    foreach candidate in candidates
      # make sure the caps is fixed
      fixedcaps = gst_pad_fixate_caps (srcpad, candidate)

    # see if the peer accepts it
    if gst_pad_peer_accept_caps (srcpad, fixedcaps)
      # store the caps as the negotiated caps, this will
      # call the setcaps function on the pad
      gst_pad_push_event (srcpad, gst_event_new_caps (fixedcaps))
      break
    endif
  done
endif
           

使用ALLOCATION 查詢 協商 allocator/bufferpool

buffer = gst_buffer_new_allocate (NULL, size, 0);
    # fill buffer and push
           

sink pad 開始重新協商的通用流程.

src              sink
             |                 |
             |  accepts?       |
             |<----------------| type B
             |      yes        |
             |- - - - - - - - >|-.
             |                 | | suggest B caps next
             |                 |<'
             |                 |
             |   push_event()  |
 mark      .-|<----------------| send RECONFIGURE event
renegotiate| |                 |
           '>|                 |
             |  querycaps()    |
renegotiate  |---------------->|
             |  suggest B      |
             |< - - - - - - - -|
             |                 |
             |  send_event()   |
send CAPS    |---------------->| Receive type B, reconfigure to
event B      |                 | process type B.
             |                 |
             |  push           |
push buffer  |---------------->| Process buffer of type B
             |                 |
           

用例:

videotestsrc  ! xvimagesink
           
  • 誰決定使用什麼格式?
    • 按照慣例, 總是由 src pad 決定。 sinkpad 可以通過将其放在 caps 查詢結果 GstCaps 的高位來建議格式。
    • 由于由src 決定,它總是可以選擇它可以做到的事情,是以隻有當 sinkpad 聲明它可以接受某些東西而稍後它又不能時,這一步才會失敗。
  • 什麼時候進行協商?
    • 在 srcpad 執行推送之前,它會計算出 1) 中所述的類型,然後推送具有該類型的 caps 事件。sink 檢查媒體類型并為此類型配置自己。
    • 然後source通常會執行 ALLOCATION 查詢以與sink協商緩沖池。然後它從池中配置設定一個緩沖區并将其推送到sink。由于sink接受了caps,是以它可以為對應的格式建立一個池。
    • 由于 1) 中所述的sink可以接受該類型,是以它将能夠處理它。
  • sink如何請求另一種格式?
    • sink 詢問source是否可以使用新格式。
    • sink 向上遊推送 RECONFIGURE 事件
    • src 接收 RECONFIGURE 事件并标記重新協商
    • 在下一次緩沖區推送時,source重新協商caps和緩沖池。接收器會将新的首選格式放在它從其 caps 查詢傳回的 caps 清單中的高位。
videotestsrc ! queue ! xvimagesink
           
  • queue 代理所有的accept和caps 查詢到另一個peer pad。
  • queue 代理緩沖池
  • queue 代理 RECONFIGURE 事件
  • queue 将 CAPS 事件存儲在queue中。這意味着queue可以包含不同類型的緩沖區。

拉模式協商

拉模式下的pipeline與推模式下激活的pipeline具有不同的協商需求。推模式針對兩個用例進行了優化:

  • 媒體檔案的播放,其中分流器和解碼器是應該将格式資訊應傳播到pipeline其餘部分的點;和
  • 從實時源錄制,其中使用者習慣于在source element之後直接放置一個 capsfilter;是以,caps資訊流從使用者開始,通過source的潛在caps,到達pipeline的sink。

相比之下,拉模式還有其他典型用例:

  • 從有損源(例如 RTP)播放,在這種源中,更多地了解pipeline的延遲可以提高品質;或者
  • 音頻合成,其中音頻 API 被調整為僅産生必要數量的采樣,通常由硬體中斷驅動以填充 DMA 緩沖區或 Jack[0] 端口緩沖區。
  • 低延遲效果處理,當資料從環形緩沖區傳輸到sink應該應用過濾器,而不是傳輸之前。例如, wavsrc ! volume ! alsasink 不應在推模式 中使用内部 alsasink ringbuffer 線程,而是通過 wavsrc ! audioringbuffer ! volume ! alsasink 将volume 放在聲霸卡寫入器線程中 。

[0] http://jackit.sf.net

拉模式的問題是sink必須知道格式才能知道通過 gst_pad_pull_range() 拉多少位元組。這意味着在拉取之前,sink必須發起協商以決定格式。

回顧 capsnego 的原則,即資訊必須從擁有它的部分流向沒有它的部分,我們看到三個具名的用例具有不同的協商要求:

  • RTP 和低延遲播放都類似于正常播放情況,其中資訊流向下遊。
  • 在音頻合成中,擁有最多資訊的pipeline部分是sink,受提供給它的能力圖的限制(constrained by the capabilities of the graph that feeds it)。然而,沒有完全指定caps;在某些時候,使用者必須至少進行幹預以選擇采樣率。這可以在 gstreamer 外部完成,就像在 jack element中一樣,也可以通過 capsfilter 在内部完成,就像實時源一樣。

鑒于sink可能需要source的輸入,就像在RTP的情況下一樣,至少在合成的情況下作為過濾器,在拉線程被激活之前必須有一個協商階段。此外,考慮到拉模式提供的低延遲,我們希望避免從拉線程内進行 capsnego,以防它導緻我們錯過我們的排程的最後期限。

拉線程一般是在PAUSED→PLAYING狀态變化下開始的。我們必須能夠在這種狀态改變發生之前完成協商。

那麼,執行 capsnego 的時間是在 SCHEDULING 查詢成功之後,但在 sink 産生拉線程之前。

機制

sink通過執行 SCHEDULING 查詢來确定上遊element支援基于拉的排程。

sink通過将gst_pad_query_caps()從sink pad和 peer src pad獲得的結果進行相交來啟動協商過程。這個操作是由gst_pad_get_allowed_caps()執行。在簡單的傳遞情況下,peer pad的caps查詢應該傳回在其所有sink pad上調用get_allowed_caps()的交集。通過這種方式,sink element知道整個pipeline的能力。

如有必要,sink element然後将結果caps固定,進而産生流的caps。從現在開始,sinkpad 的 caps 查詢将隻傳回這些固定的 caps,這意味着上遊element隻能生成這種格式的資料。

如果sink element 無法在其sink pad上設定caps,則它應該在總線上釋出一條錯誤消息,訓示無法進行協商。

當協商成功時,sinkpad 和所有上遊内部連結的pad 在pull 模式下被激活。通常,此操作将觸發下遊element的協商,現在将被迫協商到sink pad的最終固定所需caps。

在這些步驟之後,sink element從狀态改變函數傳回 ASYNC。當sink中接收到第一個緩沖區時,狀态将送出到 PAUSED。這需要為期望從sink傳回ASYNC值的應用程式提供一緻的API ,但它也允許我們在拉動線程的上下文之外執行其餘的協商。

模式

我們可以在協商中确定 3 種模式:

  • Fixed :無法選擇輸出格式
    • caps在流中已編碼
    • 視訊/音頻解碼器
    • 通常使用 gst_pad_use_fixed_caps()
  • Transform
    • 未修改的caps(直通(passthrough))
    • 可以根據element屬性做caps變換
    • 固定的caps轉變為固定的caps
    • videobox
  • Dynamic :可以選擇輸出格式
    • 一個轉換器element
    • 取決于下遊caps,需要做一個caps查詢來找到轉換。
    • 通常更喜歡使用恒等變換
    • 固定的caps可以轉換為非固定的caps。