緩沖池
本文檔詳細介紹了如何在池中配置設定和管理緩沖區的設計。
緩沖池通過減少配置設定開銷和提高實作零拷貝記憶體傳輸的可能性來提高性能。
與 ALLOCATION 查詢一起,element可以在它們之間協商配置設定屬性和緩沖池。這也允許element在它們之間協商緩沖區中繼資料。
要求
- 提供一個 GstBufferPool 基類來幫助高效實作可重用 GstBuffer 對象清單。
- 讓上遊element發起緩沖池及其配置的協商。允許下遊element提供緩沖池屬性和/或緩沖池。這包括以下屬性:
- 具有最小和最大數量的緩沖區,并可以選擇預先配置設定緩沖區。
- 配置設定器、對齊和填充支援
- 緩沖區中繼資料
- 任意額外選項
- 與動态caps重新協商內建。
- 通知上遊element新的緩沖池可用性。當可以提供緩沖池的新element在下遊動态連結時,這一點很重要。
GstBufferPool
緩沖池對象管理具有相同屬性(例如大小、填充和對齊)的緩沖區清單。
緩沖池有兩種狀态:活動和非活動。在非活動狀态下,可以使用所需的配置設定首選項配置緩沖池。在活動狀态下,可以從池中檢索緩沖區和将其傳回到池中。
緩沖池的預設實作能夠從具有任意對齊和填充/字首的任何配置設定器配置設定緩沖區。
緩沖池的自定義實作可以覆寫緩沖池中緩沖區的配置設定和釋放算法。這應該允許不同的配置設定政策,例如使用共享記憶體或硬體映射記憶體。
協商
在兩個 pad 之間協商了特定的媒體格式後(使用 CAPS 事件),他們必須就如何配置設定緩沖區達成一緻。
srcpad 将始終主動協商配置設定屬性。首先建立一個帶有協商caps的 GST_QUERY_ALLOCATION。
srcpad 可以在查詢中将 need-pool 标志設定為 TRUE,以選擇性地使peer pad 配置設定緩沖池。隻有在能夠使用peer方提供的緩沖池時才應該這樣做。
然後它将檢查傳回的結果并在需要時配置傳回的池或使用傳回的屬性建立新池。
緩沖區然後由 srcpad 從協商池中配置設定,并像往常一樣推送到peer pad。
當緩沖區大小不同且無法從池中配置設定時,配置設定查詢還可以傳回配置設定器對象。
配置設定查詢
配置設定查詢具有以下字段:
- (in) caps, GST_TYPE_CAPS: 協商的caps
- (in) need-pool, G_TYPE_BOOLEAN: 如果請求一個 GstBufferPool
- (out) pool, G_TYPE_ARRAY 的結構:池配置數組:
struct {
GstBufferPool *pool;
guint size;
guint min_buffers;
guint max_buffers;
}
使用 gst_query_parse_nth_allocation_pool() 擷取值。
配置設定器可以包含多個池配置。 如果need-pool 為TRUE,當下遊element可以提供一個GstBufferPool 時,池成員可能包含一個GstBufferPool。
Size 包含緩沖池緩沖區的大小,并且永遠不會為 0。
min_buffers 和 max_buffers 包含應由池管理的建議的最小和最大緩沖區量。
當沒有提供或建議的池不可接受時,上遊element可以選擇使用提供的池或建立自己的池。
然後可以使用建議的最小和最大緩沖區量配置池,或者下遊element可以選擇不同的值。
- (out) allocator, G_TYPE_ARRAY of structure:可以使用的配置設定器參數數組。
struct {
GstAllocator *allocator;
GstAllocationParams params;
}
使用 gst_query_parse_nth_allocation_param() 擷取值。
執行查詢的element可以使用配置設定器及其參數為下遊element配置設定記憶體。 也可以在提供的池中配置配置設定器。
- (out) metadata, G_TYPE_ARRAY of structure:可以接受的中繼資料參數數組。
struct {
GType api;
GstStructure *params;
}
使用 gst_query_parse_nth_allocation_meta() 擷取值。
當這些中繼資料項放置在緩沖區上時,下遊element可以接受這些中繼資料項。 還有一個與包含中繼資料特定選項的中繼資料關聯的任意 GstStructure。
一些緩沖池有選項可以在池配置設定的緩沖區上啟用中繼資料。
從池中配置設定
緩沖區是從 pad 的池中配置設定的:
res = gst_buffer_pool_acquire_buffer (pool, &buffer, ¶ms);
從池中配置設定的 GstBuffer 将始終是可寫的(引用計數為 1),并且它的池成員也将指向建立緩沖區的 GstBufferPool。
緩沖區以通常的方式重新計數。當緩沖區的 refcount 達到 0 時,緩沖區會自動傳回到池中。
由于從池中配置設定的所有緩沖區都保留對池的引用,是以當沒有其他對象儲存對池的引用計數時,将在池中的所有緩沖區取消引用時結束。通過将池設定為非活動狀态,我們可以從池中耗盡所有緩沖區。
當池處于非活動狀态時, gst_buffer_pool_acquire_buffer() 将立即傳回 GST_FLOW_FLUSHING。
可以為 gst_buffer_pool_acquire_buffer() 方法提供額外的參數來影響配置設定決策。 GST_BUFFER_POOL_ACQUIRE_FLAG_KEY_UNIT 和 GST_BUFFER_POOL_ACQUIRE_FLAG_DISCONT 作為提示。
當緩沖池配置了最大數量的緩沖區時,當所有的緩沖區都已被配置設定,配置設定将會阻塞,直到有一個緩沖區傳回到池中。可以通過在參數中指定 GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT 标志來更改此行為。設定此标志後,配置設定将在池為空時傳回 GST_FLOW_EOS。
重新協商
當緩沖池的配置發生變化時,可能需要重新協商緩沖池。更改可以是緩沖區大小(由于caps 更改)、對齊方式或緩沖區數量。
下遊
當上遊element想要協商新格式時,它可能需要與下遊element重新協商新的緩沖池配置。例如,當緩沖區大小發生變化時,就會發生這種情況。
我們不能僅僅重新配置現有的緩沖池,因為pipeline中可能仍然有來自緩沖池的未完成的緩沖區。是以,我們需要為新配置建立一個新的緩沖池,同時讓舊池耗盡。
實作可以選擇重用相同的緩沖池對象,并在重新配置緩沖池之前等待耗盡完成。
想要重新協商新緩沖池的element使用與剛開始時完全相同的算法。它将首先協商caps,然後使用 ALLOCATION 查詢來擷取和配置新池。
上遊的
當下遊element想要協商新格式時,它将向上遊發送 RECONFIGURE 事件。這訓示上遊在需要時重新協商格式和緩沖池。
在pipeline中添加或删除element或pipeline拓撲發生變化時,會發生pipeline重新配置。pipeline重新配置還可能觸發緩沖池和caps的重新協商。
當需要重新配置時,RECONFIGURE事件标記它所遊曆的每個pad。然後,下一次緩沖區配置設定将需要重新協商或重新配置池。
關閉
在推模式下,source pad負責在流停止時将池設定為非活動狀态。非活動狀态将取消阻塞任何挂起的配置設定,以便element可以關閉。
在拉模式下,sink element應該在關閉時将池設定為非活動狀态,以便 peer 的_get_range() 函數可以解除阻塞。
在非活動狀态下,所有傳回到池中的緩沖區将自動被池釋放,新的配置設定将失敗。
用例
videotestsrc ! xvimagesink
- 在videotestsrc可以輸出緩沖區之前,它需要與下遊peer pad協商caps和bufferpool。
- 首先它會根據通用規則與下遊協商一個合适的格式。它将使用協商配置向下遊發送 CAPS 事件。
- 然後它執行 ALLOCATION 查詢。它将使用傳回的緩沖池或使用傳回的參數配置自己的緩沖池。緩沖池最初處于非活動狀态。
- ALLOCATION 查詢列出了下遊 xvimagesink 的所需配置,它可以具有特定的對齊方式 和/或 最小/最大緩沖區量。
- videotestsrc 更新緩沖池的配置,它可能會将最小緩沖區設定為 1 和所需緩沖區的大小。然後使用新屬性更新緩沖池配置。
- 當配置成功更新時,videotestsrc 将緩沖池設定為活動狀态。這會在池中預先配置設定緩沖區(如果需要)。當沒有足夠的可用記憶體時,此操作可能會失敗。由于緩沖池由 xvimagesink 提供,它将配置設定由 XvImage 支援并指向與 X 伺服器共享記憶體的緩沖區。
- 如果成功激活了緩沖池,videotestsrc 可以從緩沖池中擷取一個緩沖區,填充資料并将其推送到 xvimagesink。
- xvimagesink 可以通過跟蹤池成員知道緩沖區源自其池。
- 關閉時,videotestsrc 會将池設定為非活動狀态,這将導緻進一步的配置設定失敗并釋放目前配置設定的緩沖區。 videotestsrc 然後将釋放池并停止流傳輸。
videotestsrc ! queue ! myvideosink
- 在第二個用例中,我們有一個最多可以配置設定 3 個視訊緩沖區的videosink。
- 同樣 videotestsrc 将不得不與peer element協商緩沖池。為此,它将執行 ALLOCATION 查詢,并由queue将查詢代理到其下遊peer element。
- 從 myvideosink 傳回的緩沖池的 max_buffers 設定為 3。 queue 和 videotestsrc 可以在此上限下運作,因為這些element都不需要超過該數量的臨時存儲緩沖區。
- Myvideosink 的緩沖池将根據協商格式的緩沖區大小并根據填充和對齊規則進行配置。當 videotestsrc 将池設定為活動時,将在池中預配置設定 3 個視訊緩沖區。
- videotestsrc 從其 srcpad 上的配置池擷取緩沖區并将其推送到隊列中。當 videotestsrc 已擷取并推送 3 幀時,對 gst_buffer_pool_acquire_buffer() 的下一次調用将阻塞(假設未指定 GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT)。
- 當queue推出一個緩沖區并且sink已經渲染它時,緩沖區的引用計數達到 0 并且緩沖區在池中被回收。這将喚醒被阻塞的 videotestsrc,等待更多緩沖區并使其産生下一個緩沖區。
- 在此設定中,pipeline中最多有 3 個活動緩沖區,并且 videotestsrc 的速率受緩沖區在緩沖池中回收的速率的限制。
- 關閉時,videotestsrc 将首先将 srcpad 上的緩沖池設定為非活動狀态。這會導緻任何挂起(阻塞)的acquire傳回一個 FLUSHING 結果并導緻流線程暫停。
.. ! myvideodecoder ! queue ! fakesink
- 在這種情況下,myvideodecoder 需要将緩沖區對齊到 128 位元組并填充 4096 位元組。pipeline從連結到 fakesink 的decoder開始,但我們将動态地将sink更改為可以提供緩沖池的sink。
- 當 myvideodecoder 與下遊 fakesink element協商大小時,它會收到一個 NULL 緩沖池,因為 fakesink 不提供緩沖池。然後它會選擇自己的自定義緩沖池來開始資料傳輸。
- 在某些時候,我們阻塞queue 的 srcpad,取消queue 與 fakesink 的連結,連結一個新的 sink,并将新的 sink 設定為 PLAYING 狀态。連結新sink将自動向上遊發送 RECONFIGURE 事件,并通過隊列通知 myvideodecoder 它應該重新協商其緩沖池,因為下遊已重新配置。
- 在推送下一個緩沖區之前,myvideodecoder 必須重新協商一個新的緩沖池。為此,它執行通常的緩沖池協商算法。如果它可以從下遊擷取并配置一個新的緩沖池,它将自己的(舊)池設定為非活動狀态并取消引用它。這最終将耗盡并取消引用舊的緩沖池。
- 新緩沖池被設定為queue的srcpad和sinkpad的新緩沖池,并設定為活動狀态。
.. ! myvideodecoder ! queue ! myvideosink
- myvideodecoder 已與下遊 myvideosink 協商緩沖池,以處理大小為 320x240 的緩沖區。它現在檢測到視訊格式發生變化,需要重新協商到 640x480 的分辨率。這需要它協商一個具有更大緩沖區大小的新緩沖池。
- 當 myvideodecoder 需要獲得更大的緩沖區時,它開始協商新的緩沖池。它從下遊查詢緩沖池,使用新配置(包括更大的緩沖區大小)重新配置它,并将緩沖池設定為活動狀态。舊池已停用且取消引用,這會導緻舊格式的緩沖區耗盡。
- 然後它使用新的緩沖池來配置設定新尺寸的新緩沖區。
- 如果在某個時候,解碼器想要再次切換到較低的分辨率,它可以選擇使用目前池(其緩沖區大于所需大小),也可以選擇重新協商新的緩沖池。
.. ! myvideodecoder ! videoscale ! myvideosink
- myvideosink 正在為上遊element提供緩沖池,并希望更改分辨率。
- myvideosink 向上遊發送 RECONFIGURE 事件以通知上遊需要新格式。上遊element嘗試在推出新緩沖區之前協商新格式和緩沖池。舊的緩沖池以正常方式耗盡。