天天看點

gstreamer開發

(本文轉載自IBM developerworks)

本文來自技術世界www.js4j.com

一、基本概念

1.1 元件處理

對于需要應用 GStreamer 架構的程式員來講,GstElement 是一個必須了解的概念,因為它是組成管道的基本構件,也是架構中所有可用元件的基礎,這也難怪 GStreamer 架構中的大部分函數都會涉及到對 GstElement 對象的操作。從 GStreamer 自身的觀點來看,GstElement 可以描述為一個具有特定屬性的黑盒子,它通過連接配接點(link point)與外界進行互動,向架構中的其餘部分表征自己的特性或者功能。

按照各自功能上的差異,GStreamer 又将 GstElement 細分成如下幾類:

//來自www.js4j.com

Filter Element 過濾器元件 既有輸入端又有輸出端,它從輸入端獲得相應的資料,并在經過特殊處理之後傳遞給輸出端。一個典型的過濾器元件的例子是音頻編碼單元,它首先從外界獲得音頻資料,然後根據特定的壓縮算法對其進行編碼,最後再将編碼後的結果提供給其它子產品使用。

圖1

js4j.com

圖2

成功獲得工廠對象之後,接下來就可以通過gst_element_factory_create()函數來建立特定的GstElement對象了,該函數在調用時有兩個參數,分别是需要用到的工廠對象,以及即将建立的元件名稱。元件名稱可以用查詢的辦法獲得,也可以通過傳入空指針(NULL)來生成工廠對象的預設元件。下面的代碼示範了如何利用已經獲得的工廠對象,來建立名為decoder的MP3解碼器元件:

當建立的GstElement不再使用的時候,還必須調用gst_element_unref()函數釋放其占用的記憶體資源:

GStreamer使用了與GObject相同的機制來對屬性(property)進行管理,包括查詢(query)、設定(set)和讀取(get)等。所有的 GstElement對象都需要從其父對象GstObject那裡繼承名稱(name)這一最基本的屬性,這是因為像 gst_element_factory_make()和gst_element_factory_create()這樣的函數在建立工廠對象和元件對象時都會用到名稱屬性,通過調用gst_object_set_name()和gst_object_get_name()函數可以設定和讀取 GstElement對象的名稱屬性。

1.2 襯墊處理

襯墊(pad)是GStreamer架構引入的另外一個基本概念,它指的是元件(element)與外界的連接配接通道,對于架構中的某個特定元件來說,其能夠處理的媒體類型正是通過襯墊暴露給其它元件的。成功建立GstElement對象之後,可以通過gst_element_get_pad()獲得該元件的指定襯墊。例如,下面的代碼将傳回element元件中名為src的襯墊:

<a href="http://www.js4j.com/tech/software-mediaplayer/1968-gstreamer-GNOME.html">gstreamer開發</a>

<a href="http://www.js4j.com/tech/software-mediaplayer/1968-gstreamer-GNOME.html">http://www.js4j.com/tech/software-mediaplayer/1968-gstreamer-GNOME.html</a>

如果需要的話也可以通過gst_element_get_pad_list()函數,來查詢指定元件中的所有襯墊。例如,下面的代碼将輸出element元件中所有襯墊的名稱:

與元件一樣,襯墊的名稱也能夠動态設定或者讀取,這是通過調用gst_pad_get_name ()和gst_pad_set_name()函數來完成的。所有元件的襯墊都可以細分成輸入襯墊和輸出襯墊兩種,其中輸入襯墊隻能接收資料但不能産生資料,而輸出襯墊則正好相反,隻能産生資料但不能接收資料,利用函數gst_pad_get_direction()可以獲得指定襯墊的類型。 GStreamer架構中的所有襯墊都必然依附于某個元件之上,調用gst_pad_get_parent()可以獲得指定襯墊所屬的元件,該函數的傳回值是一個指向GstElement的指針。襯墊從某種程度上可以看成是元件的代言人,因為它要負責向外界描述該元件所具有的能力。GStreamer架構提供了統一的機制來讓襯墊描述元件所具有的能力(capability),這是借助資料結構_GstCaps來實作的:

内容來自js4j.com

以下是對mad元件的能力描述,不難看出該元件中實際包含sink和src兩個襯墊,并且每個襯墊都帶有特定的功能資訊。名為sink的襯墊是 mad元件的輸入端,它能夠接受 MIME類型為audio/mp3的媒體資料,此外還具有layer、bitrate和framed三種屬性。名為src的襯墊是mad元件的輸出端,它負責産生MIME類型為audio/raw媒體資料,此外還具有format、depth、rate和channels等多種屬性。

準确地說,GStreamer架構中的每個襯墊都可能對應于多個能力描述,它們能夠通過函數gst_pad_get_caps()來獲得。例如,下面的代碼将輸出pad襯墊中所有能力描述的名稱及其MIME類型:

1.3 箱櫃

圖3描述了箱櫃在GStreamer架構中的典型結構:

圖3

在GStreamer應用程式中使用的箱櫃主要有兩種類型:

GstPipeline 管道是最常用到的容器,對于一個GStreamer應用程式來講,其頂層箱櫃必須是一條管道。

GstThread 線程的作用在于能夠提供同步處理能力,如果GStreamer應用程式需要進行嚴格的音視訊同步,一般都需要用到這種類型的箱櫃。

<a href="http://www.js4j.com/tech/software-mediaplayer/1968-gstreamer-GNOME.html">gstreamer開發(2)</a>

箱櫃成功建立之後,就可以調用gst_bin_add()函數将已經存在的元件添加到其中來了: js4j.com

而要從箱櫃中找到特定的元件也很容易,可以借助gst_bin_get_by_name()函數實作:

由于GStreamer架構中的一個箱櫃能夠添加到另一個箱櫃之中,是以有可能會出現箱櫃嵌套的情況,gst_bin_get_by_name() 函數在查找元件時會對嵌套的箱櫃作遞歸查找。元件有添加到箱櫃之中以後,在需要的時候還可以從中移出,這是通過調用gst_bin_remove()函數來完成的:

圖4

具有精靈襯墊的箱櫃在行為上與元件是完全相同的,所有元件具有的屬性它都具有,所有針對元件能夠進行的操作也同樣能夠針對箱櫃進行,是以在GStreamer應用程式中能夠像使用元件一樣使用這類箱櫃。下面的代碼示範了如何為箱櫃添加一個精靈襯墊:

二、元件連接配接 js4j.com

在引入了元件和襯墊的概念之後,GStreamer對多媒體資料的處理過程就變得非常清晰了:通過将不同元件的襯墊依次連接配接起來構成一條媒體處理管道,使資料在流經管道的過程能夠被各個元件正常處理,最終實作特定的多媒體功能。 js4j.com

圖1就描述了一條很簡單的管道,它由三個基本元件構成:資料源元件隻負責産生資料,它的輸出襯墊與過濾器元件的輸入襯墊相連;過濾器元件負責從自己的輸入襯墊中擷取資料,并在經過特定的處理之後,将結果通過輸出襯墊傳給與之相連的接收器元件;接收器元件隻負責接收資料,它的輸入襯墊與過濾器元件的輸出襯墊相連,負責對最終結果進行相應的處理。

GStreamer架構中的元件是通過各自的襯墊連接配接起來的,下面的代碼示範了如何将兩個元件通過襯墊連接配接起來,以及如何在需要的時候斷開它們之間的連接配接: 内容來自js4j.com

如果需要建立起連接配接的元件都隻有一個輸入襯墊和一個輸出襯墊,那麼更簡單的做法是調用gst_element_link()函數直接在它們之間建立起連接配接,或者調用gst_element_unlink()函數斷開它們之間的連接配接:

<a href="http://www.js4j.com/tech/software-mediaplayer/1968-gstreamer-GNOME.html">gstreamer開發(3)</a>

<code>// 連接配接 gst_element_link (element1, element2); // 斷開 gst_element_unlink (element1, element2);</code>

三、元件狀态

當GStreamer架構中的元件通過管道連接配接好之後,它們就開始了各自的處理流程,期間一般會經曆多次狀态切換,其中每個元件在特定時刻将處于如下四種狀态之一:

NULL 這是所有元件的預設狀态,表明它剛剛建立,還沒有開始做任何事情。

READY 表明元件已經做好準備,随時可以開始處理流程。

PAUSED 表明元件因某種原因暫時停止處理資料。

PLAYING 表明元件正在進行資料處理。

所有的元件都從NULL狀态開始,依次經曆NULL、READY、PAUSED、PLAYING等狀态間的轉換。元件目前所處的狀态可以通過調用gst_element_set_state()函數進行切換: //來自www.js4j.com

預設情況下,管道及其包含的所有元件在建立之後将處于NULL狀态,此時它們不會進行任何操作。當管道使用完畢之後,不要忘記重新将管道的狀态切換回NULL狀态,讓其中包含的所有元件能夠有機會釋放它們正在占用的資源。 //來自www.js4j.com

處于READY狀态的管道一旦切換到PLAYING狀态,需要處理的多媒體資料就開始在整個管道中流動,并依次被管道中包含的各個元件進行處理,進而最終實作管道預先定義好的某種多媒體功能。GStreamer架構也允許将管道直接從NULL狀态切換到PLAYING狀态,而不必經過中間的 READY狀态。

正處于播放狀态的管道能夠随時切換到PAUSED狀态,暫時停止管道中所有資料的流動,并能夠在需要的時候再次切換回PLAYING狀态。如果需要插入或者更改管道中的某個元件,必須先将其切換到PAUSED或者NULL狀态,元件在處于PAUSED狀态時并不會釋放其占用的資源。

四、實作MP3播放器

在了解了一些基本概念和處理流程之後,下面來看看如何利用GStreamer架構提供的元件,來實作一個簡單的MP3播放器。在圖1中描述的結構能夠很容易地映射成MP3播放器,其中資料源元件負責從磁盤上讀取資料,過濾器元件負責對資料進行解碼,而接受器元件則負責将解碼後的資料寫入聲霸卡。 内容來自js4j.com

接下去需要建立三個元件并連接配接成管道,由于所有GStreamer元件都具有相同的基類GstElement,是以能夠采用如下方式進行定義: 内容來自js4j.com

<a href="http://www.js4j.com/tech/software-mediaplayer/1968-gstreamer-GNOME.html">gstreamer開發(4)</a>

管道在GStreamer架構中是用來容納和管理元件的,下面的代碼将建立一條名為pipeline的新管道:

資料源元件負責從磁盤檔案中讀取資料,它具有名為location的屬性,用來指明檔案在磁盤上的位置。使用标準的GObject屬性機制可以為元件設定相應的屬性:

過濾器元件負責完成對MP3格式的資料進行解碼,最簡單的辦法是安裝mad這一插件,借助它來完成相應的解碼工作: //來自www.js4j.com

接收器元件負責将解碼後的資料利用聲霸卡播放出來:

已經建立好的三個元件需要全部添加到管道中,并按順序連接配接起來:

所有準備工作都做好之後,就可以通過将管道的狀态切換到PLAYING狀态,來啟動整個管道的資料處理流程:

由于沒有用到線程,是以必須通過不斷調用gst_bin_iterate()函數的辦法,來判斷管道的處理過程會在何時結束:

隻要管道内還會繼續有新的事件産生,gst_bin_iterate()函數就會一直傳回TRUE,隻有當整個處理過程都結束的時候,該函數才會傳回FALSE,此時就該終止管道并釋放占用的資源了: //來自www.js4j.com

用GStreamer實作的MP3播放器的源代碼如下所示:

<a href="http://www.js4j.com/tech/software-mediaplayer/1968-gstreamer-GNOME.html">gstreamer開發(5)</a>

五、小結