天天看點

Gstreamer1.0-總覽(OverView)

概述

這部分講述了 GStreamer 的設計,并參考了不同主題更詳細的說明。

本文檔适用于希望全面了解 GStreamer 内部工作原理的人員。

介紹

GStreamer 是一組庫和插件,可用于實作各種多媒體應用程式,包括桌面播放器、音頻/視訊錄像、多媒體伺服器、轉碼器等。

應用程式通過由elements組成的pipeline來建構的。一個 element是對多媒體流執行某些操作的對象,例如:

  • 讀取檔案
  • 在不同的格式之間解碼或編碼
  • 從硬體裝置捕獲
  • 渲染到硬體裝置
  • 混合或多路複用多個流

在 GStreamer 中,Elements具有稱為sink pads和 source pads的輸入和輸出pads。 應用程式通過pads将element連結在一起建構pipeline。 下面是 ogg/vorbis 播放pipeline的示例。

+-----------------------------------------------------------+
|    ----------> downstream ------------------->            |
|                                                           |
| pipeline                                                  |
| +---------+   +----------+   +-----------+   +----------+ |
| | filesrc |   | oggdemux |   | vorbisdec |   | alsasink | |
| |        src-sink       src-sink        src-sink        | |
| +---------+   +----------+   +-----------+   +----------+ |
|                                                           |
|    <---------< upstream <-------------------<             |
+-----------------------------------------------------------+
           

filesrc element 從磁盤檔案上讀取資料。 oggdemux element 對資料進行解複用并将壓縮的音頻流發送到 vorbisdec element。 vorbisdec element解碼壓縮資料并将其發送到 alsasink element。 alsasink element将采樣發送到聲霸卡進行播放。

下遊(downstream )和上遊(upstream )是用于描述pipeline中方向的術語。從源端到接收端被稱為“downstream”,“upstream ”是從接收端到源端。資料流總是向下遊傳輸。

應用程式的任務是使用現有elements建構上述pipeline。這在pipeline建構主題中進一步解釋。

應用程式不需要管理任何複雜的實際資料流/解碼/轉換/同步(dataflow/decoding/conversions/synchronisation)等,但隻調用pipeline對象上的進階函數,如PLAY/PAUSE/STOP。

該應用程式還從pipeline接收消息和通知,例如中繼資料、警告、錯誤和 EOS 消息。

如果應用程式需要對圖形進行更多控制,則可以直接通路pipeline中的elements和pads。

設計概覽

GStreamer 設計目标包括:

  • 快速處理大量資料
  • 允許完全多線程處理
  • 處理多種格式的能力
  • 同步不同的資料流
  • 處理多種裝置的能力

呈現給應用程式的功能取決于系統上安裝的element數量及其功能。

GStreamer 核心被設計為與媒體無關,但為element提供了許多功能來描述媒體格式。

Elements

Pipeline中最小的建構塊是element。一個element提供了許多pad,它們可以是source pad或 sink pad。 Sourcepad 提供資料,sinkpad 消費資料。下面是一個 ogg demuxer element的例子,它有一個接收資料的sink pad和兩個産生資料的source pad。

+-----------+
 | oggdemux  |
 |          src0
sink        src1
 +-----------+
           

一個element可以處于四種不同的狀态:NULL、READY、PAUSED、PLAYING。在 NULL 和 READY 狀态下,element不處理任何資料。在 PLAYING 狀态,它正在處理資料。中間 PAUSED 狀态用于預卷pipeline中的資料。可以使用 gst_element_set_state() 執行狀态更改。

一個element總是經曆所有的中間狀态變化。這意味着當一個element處于 READY 狀态并被置于 PLAYING 時,它将首先經曆中間 PAUSED 狀态。

Element狀态更改為 PAUSED 将激活element的pad。首先激活source pad,然後激活sink pad。當pad被激活時,pad激活功能被調用。一些 pad 會啟動一個線程 (GstTask) 或其他一些機制來開始生産或消費資料。

PAUSED 狀态很特殊,因為它用于預卷pipeline中的資料。目的是用資料填充pipeline中的所有連接配接的element,以便随後的 PLAYING 狀态更改發生得非常快。是以,某些element在接收到足夠的資料之前不會完成到PAUSED的狀态。 Sink element需要在接收到第一個資料後才完成狀态變為 PAUSED。

通常,element的狀态變化由pipeline協調,如狀态中所述。

存在不同類别的element:

  • source elements:這些element不消耗資料,隻為pipeline提供資料。
  • sink elements:這些element不産生資料但将資料呈現給輸出裝置。
  • transform elements:這些element将某種格式的輸入流轉換為另一種格式的流。編碼器/解碼器/轉換器是示例。
  • demuxer elements:這些element解析一個流并産生多個輸出流。
  • mixer/muxer elements:将多個輸入流合并為一個輸出流。

可以構造其他類别的element(參見 klass)。

Bins

bin 是element的子類,充當其他element的容器,以便可以将多個element組合為一個element。

Bin 協調它的子element的狀态更改,稍後将對此進行說明。它還向element分發事件和各種其他功能。

通過鏡像(ghostpadding)一個或多個孩子的pad,并添加到自己身上,bin可以擁有自己的source 和sink pad。

下面是一個帶有兩個element的 bin 的可視圖。一個element的pad被鏡像(ghostpadded )到bin。

+---------------------------+
 | bin                       |
 |    +--------+   +-------+ |
 |    |        |   |       | |
 |  /sink     src-sink     | |
sink  +--------+   +-------+ |
 +---------------------------+
           

pipeline

pipeline是一個特殊的 bin 子類,為其子類提供以下功能:

  • 為其所有子element選擇和管理全局時鐘。
  • 根據標明的時鐘管理 running_time。 Running_time 是pipeline在 PLAYING 狀态花費的時間,用于同步。
  • 管理pipeline中的延遲。
  • 通過 GstBus 為element提供與應用程式通信的方法。
  • 管理錯誤和流結束等element的全局狀态。

通常,應用程式會建立一個pipeline來管理應用程式中的所有element。

資料流和緩沖區

GStreamer 支援兩種可能的資料流模型,推(push)和拉(pull)模型。在推模型中,上遊element通過調用 sink pad 上的方法将資料發送到下遊element。在拉模型中,下遊element通過調用source pad上的方法從上遊element請求資料。

最常見的資料流是推模型。分流器(demuxer )element可以在特定情況下使用拉模型。拉模型也可用于低延遲音頻應用程式。

pad 之間傳遞的資料被封裝在 緩沖區(buffers )中。緩沖區包含指向實際記憶體的指針以及描述記憶體的中繼資料。該中繼資料包括:

  • 資料的時間戳,這是捕獲資料的時間執行個體或應播放資料的時間。
  • 資料的偏移量:媒體特定的偏移量,這可以是音頻的采樣或視訊的幀。
  • 資料的持續時間。
  • 描述資料特殊屬性的附加标志,例如不連續性或增量機關。
  • 額外的任意中繼資料

當一個element希望将緩沖區發送到另一個元素時,使用連結到另一個元素的一個焊盤的一個焊盤來執行此操作。在推送模型中,使用 gst_pad_push() 将緩沖區推送到對等墊。在拉模型中,使用 gst_pad_pull_range() 函數從對等方拉出緩沖區。

在元素推出緩沖區之前,它應該確定對等元素可以了解緩沖區内容。它通過查詢受支援格式的對等元素并選擇合适的通用格式來實作此目的。然後,在推送緩沖區之前,所選格式首先通過 CAPS 事件發送到對等元素(請參閱協商)。

當一個 element pad 收到一個 CAPS 事件時,它必須檢查它是否了解媒體類型。如果前面的媒體類型不被接受,該元素必須拒絕跟随緩沖區。

gst_pad_push() 和 gst_pad_pull_range() 都有一個傳回值訓示操作是否成功。錯誤代碼意味着不應向該焊盤發送更多資料。線上程中啟動資料流的源元素通常會在發生這種情況時暫停生産線程。

可以使用 gst_buffer_new() 或通過使用 gst_buffer_pool_acquire_buffer() 從緩沖池請求可用緩沖區來建立緩沖區。使用第二種方法,peer 元素可以實作自定義緩沖區配置設定算法。

選擇媒體類型的過程稱為上限協商。

Caps

媒體類型 (Caps) 使用鍵/值對的通用清單進行描述。鍵是一個字元串,值可以是 int/float/string 類型的 單個值/清單值/範圍值。

沒有範圍值/清單值或其他可變值的Caps被稱為是固定的,可以用來放在緩沖區上。

帶有變量的Caps用于描述可由pad處理的可能的媒體類型。

資料流和事件

與資料流并行的是事件流。與緩沖區不同,事件可以向上遊和下遊傳播。有些事件隻向上遊傳播,有些隻向下遊傳播。

這些事件用于表示資料流中的特殊條件,如 EOS,或通知插件的特殊事件,如重新整理(flushing )或查找 (seeking)。

有些事件必須與緩沖區流序列化,有些則不需要。序列化事件被插入到緩沖區之間。非序列化事件跳轉到目前正在處理的任何緩沖區之前。

序列化事件的一個示例是插入緩沖區之間以标記這些緩沖區的中繼資料的 TAG 事件。

非序列化事件的一個例子是 FLUSH 事件。

建構Pipeline

應用程式首先使用 gst_pipeline_new() 建立一個 Pipeline element。使用 gst_bin_add() 和 gst_bin_remove() 在pipeline中添加和删除element。

添加element後,可以使用 gst_element_get_pad() 檢索element 的 pad。然後可以使用 gst_pad_link() 将pad連結在一起。

當實際資料流在pipeline中發生時,一些element會建立新的pad。使用 g_signal_connect() 可以在element建立pad時收到通知。然後可以将這些新的pad接到其他未連結的pad。

某些element無法連結在一起,因為它們操作不同的不相容的資料類型。可以使用 gst_pad_get_caps() 檢索 pad 可以提供或使用的可能資料類型。

下面是一個簡單的

+-------------------------------------------+
| pipeline                                  |
| +---------+   +----------+   +----------+ |
| | filesrc |   | mp3dec   |   | alsasink | |
| |        src-sink       src-sink        | |
| +---------+   +----------+   +----------+ |
+-------------------------------------------+
           

Pipeline clock

Pipeline的重要功能之一是為pipeline中的所有element選擇一個全局時鐘。

時鐘的目的是以每秒一個 GST_SECOND 的速率提供一個嚴格遞增的值。時鐘值以納秒表示。element使用時鐘時間來同步資料的播放。

在pipeline設定為 PLAYING 之前,pipeline會詢問每個element是否可以提供時鐘。按以下順序選擇時鐘:

  • 如果應用程式選擇了一個時鐘,請使用該時鐘。
  • 如果source element 提供時鐘,請使用該時鐘。
  • 從提供時鐘的任何其他element中選擇一個時鐘,從sink開始。
  • 如果沒有element提供時鐘,則pipeline使用預設系統時鐘。

在典型的播放pipeline中,該算法将選擇由諸如audio sink之類的sink element提供的時鐘。

在錄制(capture )類型的pipeline中,這通常會選擇資料生成器的時鐘,在大多數情況下,它無法控制它生成資料的速率。

pipeline狀态

當所有的pad都連接配接好并且信号已經連接配接好後,pipeline可以進入 PAUSED 狀态以啟動資料流。

當 bin(以及pipeline)執行狀态更改時,它将更改其所有子element的狀态。pipeline将從sink element 到 source element 對其子element的狀态進行更改,以確定沒有上遊element向尚未準備好接收它資料的element生産資料。

在 mp3 播放pipeline中,element的狀态按照 alsasink、mp3dec、filesrc 的順序改變。

周遊每個元素的所有中間狀态,導緻以下狀态變化鍊:

  • alsasink to READY:音頻裝置被探測
  • mp3dec 到 READY:沒有任何事情發生
  • filesrc 到 READY:檔案被探測
  • alsasink 到 PAUSED:音頻裝置已打開。 alsasink 是一個sink并傳回 ASYNC 因為它還沒有收到資料
  • mp3dec to PAUSED:解碼庫初始化
  • filesrc to PAUSED:打開檔案并啟動一個線程将資料推送到 mp3dec

此時資料從 filesrc 流向 mp3dec 和 alsasink。由于 mp3dec 已暫停,它在sink pad 上接收 filesrc 的資料并開始将壓縮資料解碼為原始音頻采樣。

mp3 decoder 計算原始音頻采樣的采樣率、通道數和其他音頻屬性,并發送帶有媒體類型的 caps 事件。

然後 Alsasink 接收 caps 事件,檢查 caps 并重新配置自身以處理相關媒體類型。

mp3dec 然後将解碼後的采樣放入 Buffer 并将此緩沖區推送到下一個element。

Alsasink 接收帶有音頻采樣的緩沖區。由于它收到了第一個音頻采樣緩沖區,是以它完成了到 PAUSED 狀态的狀态更改。此時pipeline已預卷,所有element都有音頻采樣。 Alsasink 現在還能夠為pipeline提供時鐘。

由于 alsasink 現在處于 PAUSED 狀态,它在接收第一個緩沖區時會阻塞。這有效地使mp3dec 和 filesrc 阻塞在 gst_pad_push() 中的 。

由于現在所有element都從 gst_element_get_state() 函數傳回 SUCCESS,是以可以将pipeline 置于 PLAYING 狀态。

在進入 PLAYING 之前,pipeline 選擇一個時鐘并對時鐘的目前時間進行采樣。這是base_time。然後将這個時間配置設定給所有element。然後element可以使用緩沖區 running_time base_time 與時鐘同步(另請參見同步)。

然後發生以下狀态更改鍊:

  • alsasink to PLAYING:采樣播放到音頻裝置
  • mp3dec 到 PLAYING:沒有任何事情發生
  • filesrc 到 PLAYING:沒有任何事情發生

pipeline狀态

pipeline通過總線通知應用程式在pipeline中發生的任何特殊事件。總線對象由pipeline提供,可以使用 gst_pipeline_get_bus() 檢索。

總線可以被輪詢或添加到 glib 主循環。

總線配置設定給添加到pipeline的所有element。這些element使用總線來釋出消息。存在各種消息類型,例如 ERRORS、WARNINGS、EOS、STATE_CHANGED 等。

Pipeline以特殊方式處理從element接收到的 EOS 消息。隻有當所有接收器element都釋出了 EOS 消息時,它才會将消息轉發給應用程式。

擷取pipeline狀态的其他方法包括可以在pipeline上使用 gst_element_query() 執行的查詢功能。這種類型的查詢對于擷取有關pipeline目前位置和總時間的資訊很有用。它還可以用于查詢所支援的搜尋格式和範圍(seeking formats and ranges)。

流水線EOS

當source filter 遇到流的末尾時,它會向 peer element發送一個 EOS 事件。然後,此事件将向下遊傳播到所有連接配接的element,以通知它們 EOS事件。在sink接收到 EOS 事件後,該element不應再接受任何資料。

提供流線程的元素在發送 EOS 事件後停止發送資料。

EOS 事件最終會到達 sink 元素。然後接收器将在總線上釋出一條 EOS 消息,以通知管道特定流已完成。當所有接收器都報告了 EOS 時,管道将 EOS 消息轉發給應用程式。 EOS 消息隻轉發給處于 PLAYING 狀态的應用程式。

在 EOS 中,pipeline保持在 PLAYING 狀态,将pipeline設定為 PAUSE 或 READY狀态是應用程式的責任。例如,應用程式還可以發出定位(seek)。

pipeline就緒

當把正在運作的pipeline從 PLAYING 設定為 READY 狀态時,pipeline中會發生以下操作:

  • alsasink 到 PAUSED: 在下一個采樣,alsasink 阻塞并完成狀态更改。如果element接收到了 EOS,則不會等待下一采樣就完成狀态更改。
  • mp3dec 到 PAUSED:沒有事情
  • filesrc 到 PAUSED: 沒有事情

進入中間 PAUSED 狀态将所有element阻塞在_push() 函數中的。發生這種情況是因為sink element在它接收到的第一個緩沖區上阻塞。

某些element可能在 PLAYING 狀态下執行阻塞操作,當它們進入 PAUSED 狀态時必須解除阻塞。這確定狀态更改發生得非常快。

在下一個 PAUSED 到 READY 狀态更改中,pipeline必須關閉,并且所有流線程并且停止發送資料。這按以下順序發生:

  • alsasink 到 READY: alsasink解除_chain()函數的阻塞,并向peer element 傳回 FLUSHING 傳回值。 sinkpad 失效并且無法用于發送更多資料。
  • mp3dec 到 READY:pads 失效并且當 mp3dec 離開它的 _chain() 函數時完成态改變。
  • filesrc 到 READY:pads 失效并且線程被暫停。

上遊element結束了它們的 _chain() 函數,因為下遊element從 _push() 函數傳回了一個錯誤代碼 (FLUSHING)。這些錯誤代碼最終傳回到啟動流線程(filesrc)的element,該element暫停線程并完成狀态更改。

這個事件序列確定所有element都不阻塞,所有流線程都停止。

管道定位(seeking)

在pipeline中seek需要非常特定的操作順序,以確定element保持同步并且以最小的延遲執行seek。

應用程式使用pipeline element上的 gst_element_send_event() 在pipeline 上發出seek事件。該事件可以是element支援的任何格式的seek事件。

pipeline首先暫停pipeline以加速查找操作。

然後pipeline向所有sink element發出seek事件。然後sink向上遊轉發seek事件,直到某個element可以執行seek操作,這通常是source或demuxer element。例如,所有中間element都可以将請求的seek偏移量轉換為另一種格式,這樣decoder element可以将seek轉換為幀号和時間戳。

當seek事件到達将執行seek操作的element時,該element執行以下步驟。

  1. 向所有下遊和上遊 peer element發送 FLUSH_START 事件。
  2. 確定流線程沒有運作。由于步驟 1),流線程始終是停止的。
  3. 執行seek操作
  4. 向所有下遊和上遊peer element發送 FLUSH done 事件。
  5. 發送 SEGMENT 事件以通知所有element的新位置并完成seek。

在步驟 1) 中,所有下遊element都必須從任何阻塞操作中傳回,并且必須拒絕任何與 FLUSH done 不同的其他緩沖區或事件。

第一步確定流線程最終解除阻塞并且可以執行步驟 2)。此時,資料流在pipeline中完全停止。

在步驟 3) 中,element執行到請求位置的seek。

在步驟 4) 中,允許所有peer element再次接受資料,并且流可以從新位置繼續。 FLUSH done 事件被發送到所有peer element,以便它們再次接受新資料并重新啟動它們的流線程。

步驟 5) 通知所有element在流中的新位置。之後,事件函數傳回給應用程式。并且流線程開始産生新資料。

由于pipeline仍處于暫停狀态,這将在sink中預卷下一個媒體采樣。應用程式可以通過在pipeline上執行 _get_state() 來等待此預卷完成。

然後,seek操作的最後一步是将pipeline的流running_time調整為0,并将pipeline設定回PLAYING。

| a) seek on pipeline
                                   | b) PAUSE pipeline
+----------------------------------V--------+
| pipeline                         | c) seek on sink
| +---------+   +----------+   +---V------+ |
| | filesrc |   | mp3dec   |   | alsasink | |
| |        src-sink       src-sink        | |
| +---------+   +----------+   +----|-----+ |
+-----------------------------------|-------+
           <------------------------+
                 d) seek travels upstream

    --------------------------> 1) FLUSH event
    | 2) stop streaming
    | 3) perform seek
    --------------------------> 4) FLUSH done event
    --------------------------> 5) SEGMENT event

    | e) update running_time to 0
    | f) PLAY pipeline