gstreamer是一個建立流媒體應用程式的架構。其基本設計思想來自于俄勒岡(oregon)研究所學生學院有關視訊管道的創意, 同時也借鑒了directshow的設計思想。
gstreamer的程式開發架構使得編寫任意類型的流媒體應用程式成為了可能。在編寫處理音頻、視訊或者兩者皆有的應用程式時, gstreamer可以讓你的工作變得簡單。gstreamer并不受限于音頻和視訊處理, 它能夠處理任意類型的資料流。管道設計的方法對于實際應用的濾波器幾乎沒有負荷, 它甚至可以用來設計出對延時有很高要求的高端音頻應用程式。
gstreamer最顯著的用途是在建構一個播放器上。gstreamer已經支援很多格式的檔案了, 包括: mp3、ogg/vorbis、mpeg-1/2、avi、quicktime、 mod等等。從這個角度看,gstreamer更象是一個播放器。但是它主要的優點卻是在于: 它的可插入元件能夠很友善的接入到任意的管道當中。這個優點使得利用gstreamer編寫一個萬能的可編輯音視訊應用程式成為可能。
<a target="_blank"></a>
圖1将有助于你更好地了解資料源元件、過濾器元件和接收器元件三者的差別,同時也不難看出它們是如何互相配合形成管道的:

需要注意的是,過濾器元件的具體形式是非常靈活的,gstreamer并沒有嚴格規定輸入端和輸出端的數目,事實上它們都可以是一個或者多個。圖2是一個avi分離器的基本結構,它能夠将輸入資料分離成單獨的音頻資訊和視訊資訊,用于實作該功能的過濾器元件很明顯隻具有一個輸入端,但卻需要有兩個輸出端。
要想在應用程式中建立gstelement對象,唯一的辦法是借助于工廠對象gstelementfactory。由于gstreamer架構提供了多種類型的gstelement對象,是以對應地提供了多種類型的gstelementfactory對象,它們是通過特定的工廠名稱來進行區分的。例如,下面的代碼通過gst_element_factory_find()函數獲得了一個名為mad的工廠對象,它之後可以用來建立與之對應的mp3解碼器元件:
成功獲得工廠對象之後,接下來就可以通過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對象的名稱屬性。
襯墊(pad)是gstreamer架構引入的另外一個基本概念,它指的是元件(element)與外界的連接配接通道,對于架構中的某個特定元件來說,其能夠處理的媒體類型正是通過襯墊暴露給其它元件的。成功建立gstelement對象之後,可以通過gst_element_get_pad()獲得該元件的指定襯墊。例如,下面的代碼将傳回element元件中名為src的襯墊:
如果需要的話也可以通過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來實作的:
以下是對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類型:
箱櫃(bin)是gstreamer架構中的容器元件,它通常被用來容納其它的元件對象,但由于其自身也是一個gstelement對象,是以實際上也能夠被用來容納其它的箱櫃對象。利用箱櫃可以将需要處理的多個元件組合成一個邏輯元件,由于不再需要對箱櫃中的元件逐個進行操作,是以能夠很容易地利用它來構造更加複雜的管道。在gstreamer架構中使用箱櫃還有另外一個優點,那就是它會試着對資料流進行優化,這對于多媒體應用來講是很具吸引力的。
圖3描述了箱櫃在gstreamer架構中的典型結構:
在gstreamer應用程式中使用的箱櫃主要有兩種類型:
gstpipeline 管道是最常用到的容器,對于一個gstreamer應用程式來講,其頂層箱櫃必須是一條管道。
gstthread 線程的作用在于能夠提供同步處理能力,如果gstreamer應用程式需要進行嚴格的音視訊同步,一般都需要用到這種類型的箱櫃。
gstreamer架構提供了兩種方法來建立箱櫃:一種是借助工廠方法,另一種則是使用特定的函數。下面的代碼示範了如何使用工廠方法建立線程對象,以及如何使用特定函數來建立管道對象:
箱櫃成功建立之後,就可以調用gst_bin_add()函數将已經存在的元件添加到其中來了:
而要從箱櫃中找到特定的元件也很容易,可以借助gst_bin_get_by_name()函數實作:
由于gstreamer架構中的一個箱櫃能夠添加到另一個箱櫃之中,是以有可能會出現箱櫃嵌套的情況,gst_bin_get_by_name()函數在查找元件時會對嵌套的箱櫃作遞歸查找。元件有添加到箱櫃之中以後,在需要的時候還可以從中移出,這是通過調用gst_bin_remove()函數來完成的:
如果仔細研究一下圖3中描述的箱櫃,會發現它沒有屬于自己的輸入襯墊和輸出襯墊,是以顯然是無法作為一個邏輯整體與其它元件互動的。為了解決這一問題,gstreamer引入了精靈襯墊(ghost pad)的概念,它是從箱櫃裡面所有元件的襯墊中推舉出來的,通常來講會同時選出輸入襯墊和輸出襯墊,如圖4所示:
具有精靈襯墊的箱櫃在行為上與元件是完全相同的,所有元件具有的屬性它都具有,所有針對元件能夠進行的操作也同樣能夠針對箱櫃進行,是以在gstreamer應用程式中能夠像使用元件一樣使用這類箱櫃。下面的代碼示範了如何為箱櫃添加一個精靈襯墊:
<a target="_blank" href="http://www.ibm.com/developerworks/cn/linux/l-gstreamer/#ibm-pcon">回頁首</a>
在引入了元件和襯墊的概念之後,gstreamer對多媒體資料的處理過程就變得非常清晰了:通過将不同元件的襯墊依次連接配接起來構成一條媒體處理管道,使資料在流經管道的過程能夠被各個元件正常處理,最終實作特定的多媒體功能。
圖1就描述了一條很簡單的管道,它由三個基本元件構成:資料源元件隻負責産生資料,它的輸出襯墊與過濾器元件的輸入襯墊相連;過濾器元件負責從自己的輸入襯墊中擷取資料,并在經過特定的處理之後,将結果通過輸出襯墊傳給與之相連的接收器元件;接收器元件隻負責接收資料,它的輸入襯墊與過濾器元件的輸出襯墊相連,負責對最終結果進行相應的處理。
gstreamer架構中的元件是通過各自的襯墊連接配接起來的,下面的代碼示範了如何将兩個元件通過襯墊連接配接起來,以及如何在需要的時候斷開它們之間的連接配接:
如果需要建立起連接配接的元件都隻有一個輸入襯墊和一個輸出襯墊,那麼更簡單的做法是調用gst_element_link()函數直接在它們之間建立起連接配接,或者調用gst_element_unlink()函數斷開它們之間的連接配接:
當gstreamer架構中的元件通過管道連接配接好之後,它們就開始了各自的處理流程,期間一般會經曆多次狀态切換,其中每個元件在特定時刻将處于如下四種狀态之一:
null 這是所有元件的預設狀态,表明它剛剛建立,還沒有開始做任何事情。
ready 表明元件已經做好準備,随時可以開始處理流程。
paused 表明元件因某種原因暫時停止處理資料。
playing 表明元件正在進行資料處理。
所有的元件都從null狀态開始,依次經曆null、ready、paused、playing等狀态間的轉換。元件目前所處的狀态可以通過調用gst_element_set_state()函數進行切換:
預設情況下,管道及其包含的所有元件在建立之後将處于null狀态,此時它們不會進行任何操作。當管道使用完畢之後,不要忘記重新将管道的狀态切換回null狀态,讓其中包含的所有元件能夠有機會釋放它們正在占用的資源。
管道真正的處理流程是從第一次将其切換到ready狀态時開始的,此時管道及其包含的所有元件将做好相應的初始化工作,來為即将執行的資料處理過程做好準備。對于一個典型的元件來講,處于ready狀态時需要執行的操作包括打開媒體檔案和音頻裝置等,或者試圖與位于遠端的媒體伺服器建立起連接配接。
處于ready狀态的管道一旦切換到playing狀态,需要處理的多媒體資料就開始在整個管道中流動,并依次被管道中包含的各個元件進行處理,進而最終實作管道預先定義好的某種多媒體功能。gstreamer架構也允許将管道直接從null狀态切換到playing狀态,而不必經過中間的ready狀态。
正處于播放狀态的管道能夠随時切換到paused狀态,暫時停止管道中所有資料的流動,并能夠在需要的時候再次切換回playing狀态。如果需要插入或者更改管道中的某個元件,必須先将其切換到paused或者null狀态,元件在處于paused狀态時并不會釋放其占用的資源。
在了解了一些基本概念和處理流程之後,下面來看看如何利用gstreamer架構提供的元件,來實作一個簡單的mp3播放器。在圖1中描述的結構能夠很容易地映射成mp3播放器,其中資料源元件負責從磁盤上讀取資料,過濾器元件負責對資料進行解碼,而接受器元件則負責将解碼後的資料寫入聲霸卡。
與其它衆多gnome項目一樣,gstreamer也是用c語言實作的。如果想要在程式中應用gstreamer提供的各種功能,首先必須在主函數中調用gst_init()來完成相應的初始化工作,以便将使用者從指令行輸入的參數傳遞給gstreamer函數庫。一個典型的gstreamer應用程式的初始化如下所示:
接下去需要建立三個元件并連接配接成管道,由于所有gstreamer元件都具有相同的基類gstelement,是以能夠采用如下方式進行定義:
管道在gstreamer架構中是用來容納和管理元件的,下面的代碼将建立一條名為pipeline的新管道:
資料源元件負責從磁盤檔案中讀取資料,它具有名為location的屬性,用來指明檔案在磁盤上的位置。使用标準的gobject屬性機制可以為元件設定相應的屬性:
過濾器元件負責完成對mp3格式的資料進行解碼,最簡單的辦法是安裝mad這一插件,借助它來完成相應的解碼工作:
接收器元件負責将解碼後的資料利用聲霸卡播放出來:
已經建立好的三個元件需要全部添加到管道中,并按順序連接配接起來:
所有準備工作都做好之後,就可以通過将管道的狀态切換到playing狀态,來啟動整個管道的資料處理流程:
由于沒有用到線程,是以必須通過不斷調用gst_bin_iterate()函數的辦法,來判斷管道的處理過程會在何時結束:
隻要管道内還會繼續有新的事件産生,gst_bin_iterate()函數就會一直傳回true,隻有當整個處理過程都結束的時候,該函數才會傳回false,此時就該終止管道并釋放占用的資源了:
用gstreamer實作的mp3播放器的源代碼如下所示:
随着 gnome 桌面環境的不斷普及,gstreamer 作為一個強大的多媒體應用開發架構,已經開始受到越來越多人的關注。gstreamer在設計時采用了非常靈活的體系結構,并且提供了許多預定義的媒體處理子產品,是以能夠極大簡化在linux下開發多媒體應用的難度。