協商
Capabilities 協商是為 GStreamer pipeline内的資料流決定适當格式的過程。理想情況下,協商(也稱為“capsnego”)将資訊從pipeline中具有資訊的那些部分傳輸到pipeline的那些易擴充的部分,受pipeline中不易擴充部分的限制。
基本規則
必須遵循這些簡單的規則:
- 下遊建議格式
- 上遊決定格式
caps協商中使用了 4 種 查詢/事件(queries/events ):
- GST_QUERY_CAPS:擷取可能的格式
- GST_QUERY_ACCEPT_CAPS:檢查格式是否可行
- GST_EVENT_CAPS:配置格式(下遊)
- 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。