gstreamer簡介

常用

gchar * caps_string = gst_caps_to_string (new_selected_caps);

g_free (caps_string);

 

需要弄懂的問題
tunnel tee queue

最後列一下Gstreamer中常見的時間宏,注意Gstreamer中的時間單位是:納秒
#define G_USEC_PER_SEC 1000000
#define GST_SECOND (G_USEC_PER_SEC * G_GINT64_CONSTANT (1000))
#define GST_MSECOND (GST_SECOND / G_GINT64_CONSTANT (1000))
#define GST_USECOND (GST_SECOND / G_GINT64_CONSTANT (1000000))
#define GST_NSECOND (GST_SECOND / G_GINT64_CONSTANT (1000000000))

整理之後,等價表示
GST_SECOND = 1000000000 ns
GST_MSECOND = 1000000 ns
GST_USECOND = 1000 ns
GST_NSECOND = 1 ns

框架圖

當前GStreamer主要有兩個大的版本分支:

1)0.10.x系列。這個版本系列的歷史較久,相關資源比較豐富。但目前官方已經不再發展和支持該版本。該系列有中文版的用戶手冊。

2)1.x系列。2012年以來發布的版本系列,也是官方推薦的版本系列。只有英文的用戶手冊,但手冊的內容與0.10.x相差不大,儘管API已經不再兼容舊版本。以下的描述以1.x系列爲準。1.x系列被設計爲可以和0.10.x系列在系統中共存,因此在同一臺電腦上,同時安裝0.10.x系列和1.x系列是完全沒有衝突的。

GStreamer本質上只是一個多媒體應用框架,具體的多媒體播放功能由插件來完成。
這個網頁就是gstreamer的插件列表。表中列出的插件,分屬4個不同的插件集:
    gst-plugins-base。這類插件格式規範,維護的也很好。
    gst-plugins-good。這類插件有高質量的代碼(但格式未必規範),而且許可證也符合要求(LGPL或與LGPL兼容的許可證)。
    gst-plugins-ugly。這類插件有高質量的代碼,但許可證方面有問題。
    gst-plugins-bad。這類插件尚不成熟,需要更多文檔、測試和應用。
插件安裝方法,以gst-plugins-base爲例。
sudo apt-get install gstreamer1.0-plugins-base
除了上面列出的插件之外,目前的做法,更傾向於使用ffmpeg作爲後端編解碼庫,尤其是編解碼更復雜的視頻文件。因此在0.10.x時代,提供了gstreamer0.10-ffmpeg插件,而1.x時代,則有gstreamer1.0-libav提供對avcodec、avformat等ffmpeg庫的支持。

核心插件
核心插件又稱gst-plugins-core,目前已經集成到gstreamer代碼中,沒有獨立的庫。
和上面提到的四類插件不同。它不提供具體的多媒體編解碼功能,而是配合框架,搭建完整的多媒體流水線。
這裏僅對其中一部分插件的功能描述如下:
fakesink:一個數據只進不出的“黑洞”。例如,一個視頻文件一般包括視頻流和音頻流。如果設備只能播放音頻(例如音箱),那麼視頻流對於設備來說,就是沒有意義的東西。這時可以用fakesink插件將之吃掉。否則,由於GStreamer會在視頻流和音頻流之間進行同步,如果視頻流沒有被消耗,音頻流也無法向前進。

tee:1路分成N路的分路器。

funnel:N路合成1路的合路器。

萬能插件
GStreamer除了那些完成具體功能的插件以外,還有一些抽象的高級插件,如playbin插件。該插件使用了GStreamer的自動加載(Auto plugging)機制,可以自動根據媒體類型,選擇不同的管道播放,相當於是個萬能播放插件。對於GStreamer應用開發人員來說,是個相當好用的東西。
playbin插件負責媒體播放的全過程,還有其他一些只負責某個步驟的全能插件:
decodebin:解碼插件。
autoaudiosink:音頻播放插件
autovideosink:視頻播放插件

GStreamer應用
相關工具軟件
GStreamer提供了一個工具軟件集——gstreamer-tools。其安裝方法如下:
sudo apt-get install gstreamer-tools gstreamer1.0-tools
它包括以下工具:
gst-launch:創建GStreamer管道的原型驗證工具。它是其中用的最廣泛的工具——網上關於GStreamer的問題討論,多數並不貼出代碼,而是給出gst-launch形式的命令。
下面是一個播放mp3文件的示例:
gst-launch filesrc location=1.mp3 ! mad ! audioconvert ! autoaudiosink
需要注意的是上面的命令中,!兩邊都要留空格,不然命令會執行錯誤。
gst-inspect:用於查詢GStreamer插件的相關信息,也非常常用。比如,如果某個媒體文件無法播放,首先使用gst-inspect查詢一下相關插件是否正確安裝。
其他的還有gst-typefind、gst-xmllaunch、gst-feedback。
注意1.x系列的工具名和0.10.x系列的略有不同,例如gst-launch在1.x系列中叫做gst-launch-1.0。

一些概念

 bus

 消息總線,例:爲你的管道(pipeline)添加一個GstBus的處理函數:

  /* watch for messages on the pipeline's bus (note that this will only
  * work like this when a GLib main loop is running) */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

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

    不是對每個element都創建一個線程,thread的邊界必須是queue element。

bin
    箱櫃(bin)是GStreamer框架中的容器元件,包含多個element或者bin,它通常被用來容納其它的元件對象,但由於其自身也是一個GstElement對象,因此實際上也能夠被用來容納其它的箱櫃對象。利用箱櫃可以將需要處理的多個元件組合成一個邏輯元件,由於不再需要對箱櫃中的元件逐個進行操作,因此能夠很容易地利用它來構造更加複雜的管道。在GStreamer框架中使用箱櫃還有另外一個優點,那就是它會試着對數據流進行優化,這對於多媒體應用來講是很具吸引力的。 GStreamer框架提供了兩種方法來創建箱櫃:一種是藉助工廠方法,另一種則是使用特定的函數。

    pipline是頂級的bin。

    pad
    襯墊(pad)是GStreamer框架引入的另外一個基本概念,它指的是元件(element)與外界的連接通道,對於框架中的某個特定元件來說,其能夠處理的媒體類型正是通過襯墊暴露給其它元件的。成功創建GstElement對象之後,可以通過gst_element_get_pad()獲得該元件的指定襯墊。

  1     pad可以有2種被激活的模式:PUSH和PULL。PUSH是默認的模式,souce pad通過調用sink pad的gst_pad_push來傳輸數據;PULL模式下,sink pad通過調用source pad的gst_pad_pull_range來請求數據

 2       按這種模式工作的Pad有一個loop函數,loop函數被重複調用直到返回false。Loop函數無論什麼時候需要數據時,可以阻塞。當Pad被取消激活時,loop函數應該取消阻塞。 

 3      當上遊元素調用 _push()函數,下游Pad的Chain函數被調用。

4       一個element的實現, 需要註冊一個或多個GstPadTemplate,然後才能夠通過這些template創建pad。

4   push方法:該方法的函數原型是GstFlowReturngst_pad_push (GstPad * pad, GstBuffer * buffer)。在該函數的實現裏,通過調用 GST_PAD_CHAINFUNC (peer) (peer, buffer)來把數據推送給下一個元件,同時下一個元件根據他來接受數據。downstream elements的sink pad上需要定義chain函數(gst_pad_set_chain_function ),upstream elements調用這個chain函數來完成將buffer從upstream(source pad)到downstream elements(sink pad)的傳遞 。這種scheduling方式中source elements遞歸調用downstream elements的chain函數,最後一直調用到目的elements的才能函數。

    ghost pad
它是從箱櫃裏面所有元件的襯墊中推舉出來的,通常來講會同時選出輸入襯墊和輸出襯墊。如果仔細研究一下箱櫃,會發現它沒有屬於自己的輸入襯墊和輸出襯墊,因此顯然是無法作爲一個邏輯整體與其它元件交互的。爲了解決這一問題,GStreamer引入了精靈襯墊(ghost pad)的概念,它是從箱櫃裏面所有元件的襯墊中推舉出來的,通常來講會同時選出輸入襯墊和輸出襯墊。具有精靈襯墊的箱櫃在行爲上與元件是完全相同的,所有元件具有的屬性它都具有,所有針對元件能夠進行的操作也同樣能夠針對箱櫃進行,因此在GStreamer應用程序中能夠像使用元件一樣使用這類箱櫃。下面的代碼示範瞭如何爲箱櫃添加一個精靈襯墊:
GstElement *bin;
GstElement *element;
element = gst_element_factory_create ("mad", "decoder");
bin = gst_bin_new ("bin_name");
gst_bin_add (GST_BIN (bin), element);
gst_element_add_ghost_pad (bin, gst_element_get_pad (element, "sink"), "sink");

    元件連接
在引入了元件和襯墊的概念之後,GStreamer對多媒體數據的處理過程就變得非常清晰了:通過將不同元件的襯墊依次連接起來構成一條媒體處理管道,使數據在流經管道的過程能夠被各個元件正常處理,最終實現特定的多媒體功能。
GstPad *srcpad, *sinkpad;
srcpad = gst_element_get_pad (element1, "src");
sinpad = gst_element_get_pad (element2, "sink");
// 連接
gst_pad_link (srcpad, sinkpad);
// 斷開
gst_pad_unlink (srcpad, sinkpad);
如果需要建立起連接的元件都只有一個輸入襯墊和一個輸出襯墊,那麼更簡單的做法是調用gst_element_link()函數直接在它們之間建立起連接,或者調用gst_element_unlink()函數斷開它們之間的連接:
// 連接
gst_element_link (element1, element2);
// 斷開
gst_element_unlink (element1, element2);

元件狀態
 當GStreamer框架中的元件通過管道連接好之後,它們就開始了各自的處理流程,期間一般會經歷多次狀態切換,其中每個元件在特定時刻將處於如下四種狀態之一:
    NULL 這是所有元件的默認狀態,表明它剛剛創建,還沒有開始做任何事情。
    READY 表明元件已經做好準備,隨時可以開始處理流程。
    PAUSED 表明元件因某種原因暫時停止處理數據。
    PLAYING 表明元件正在進行數據處理。
所有的元件都從NULL狀態開始,依次經歷NULL、READY、PAUSED、PLAYING等狀態間的轉換。元件當前所處的狀態可以通過調用gst_element_set_state()函數進行切換:    
GstElement *bin;
/* 創建元件,並將其連接成箱櫃bin */
gst_element_set_state (bin, GST_STATE_PLAYING);

例子:

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

與其它衆多GNOME項目一樣,GStreamer也是用C語言實現的。如果想要在程序中應用GStreamer提供的各種功能,首先必須在主函數中調用gst_init()來完成相應的初始化工作,以便將用戶從命令行輸入的參數傳遞給GStreamer函數庫。一個典型的GStreamer應用程序的初始化如下所示:

#include <gst/gst.h>
int main (int argc, char *argv[])
{
  gst_init (&argc, &argv);
  /* ... */
}

接下去需要創建三個元件並連接成管道,由於所有GStreamer元件都具有相同的基類GstElement,因此能夠採用如下方式進行定義:

  GstElement *pipeline, *filesrc, *decoder, *audiosink;

管道在GStreamer框架中是用來容納和管理元件的,下面的代碼將創建一條名爲pipeline的新管道:

  /* 創建用來容納元件的新管道 */
  pipeline = gst_pipeline_new ("pipeline");

數據源元件負責從磁盤文件中讀取數據,它具有名爲location的屬性,用來指明文件在磁盤上的位置。使用標準的GObject屬性機制可以爲元件設置相應的屬性:

/* 創建數據源元件 */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);

過濾器元件負責完成對MP3格式的數據進行解碼,最簡單的辦法是安裝mad這一插件,藉助它來完成相應的解碼工作:

/* 創建過濾器元件 */
decoder = gst_element_factory_make ("mad", "decoder");

接收器元件負責將解碼後的數據利用聲卡播放出來:

/* 創建接收器元件 */
audiosink = gst_element_factory_make ("audiosink", "play_audio");

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

/* 添加元件到管道中 */
gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, audiosink, NULL);
/* 通過襯墊連接元件 */
gst_element_link_many (filesrc, decoder, audiosink, NULL);

所有準備工作都做好之後,就可以通過將管道的狀態切換到PLAYING狀態,來啓動整個管道的數據處理流程:

/* 啓動管道 */
gst_element_set_state (pipeline, GST_STATE_PLAYING);

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

while (gst_bin_iterate (GST_BIN (pipeline)));

只要管道內還會繼續有新的事件產生,gst_bin_iterate()函數就會一直返回TRUE,只有當整個處理過程都結束的時候,該函數纔會返回FALSE,此時就該終止管道並釋放佔用的資源了:

/* 終止管道 */
gst_element_set_state (pipeline, GST_STATE_NULL);
/* 釋放資源 */
gst_object_unref (GST_OBJECT (pipeline));

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

#include <gst/gst.h>
int main (int argc, char *argv[])
{
    GstElement *pipeline, *filesrc, *decoder, *audiosink;
    gst_init(&argc, &argv);
    if (argc != 2) {
        g_print ("usage: %s <mp3 filename>\n", argv[0]);
        exit (-1);
    }
    /* 創建一條新的管道 */
    pipeline = gst_pipeline_new ("pipeline");
    /* 生成用於讀取硬盤數據的元件 */
    filesrc = gst_element_factory_make ("filesrc", "disk_source");
    g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
    /* 創建解碼器元件 */
    decoder = gst_element_factory_make ("mad", "decoder");
    /* 創建音頻回放元件 */
    audiosink = gst_element_factory_make ("osssink", "play_audio");
    /* 將生成的元件添加到管道中 */
    gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, audiosink, NULL);
    /* 連接各個元件 */
    gst_element_link_many (filesrc, decoder, audiosink, NULL);
    /* 開始播放 */
    gst_element_set_state (pipeline, GST_STATE_PLAYING);
    while (gst_bin_iterate (GST_BIN (pipeline)));
    /* 停止管道處理流程 */
    gst_element_set_state (pipeline, GST_STATE_NULL);
    /* 釋放佔用的資源 */
    gst_object_unref (GST_OBJECT (pipeline));
    exit (0);
    }

g_signal_connect

gulong g_signal_connect(gpointer instance,const gchar *detailed_signal,GCallback c_handler,gpointer data );
g_signal_connect(button, "pressed",G_CALLBACK(callback), NULL);
instance:信號發出者,可以認爲我們操作的控件,如按下按鈕,這個就爲按鈕指針
detailed_signal:信號標誌,如"pressed"
c_handler:回調函數的名稱,需要用G_CALLBACK()進行轉換
data:給回調函數傳的參數,gpointer 相當於C語言的 void *
返回值:註冊函數的標誌當按下button按鈕時,就會自動調用回調函數callback(相當於處理中斷任務),回調函數callback可以是任意函數,函數名字我們根據需要自行命名,如果不是庫函數,我們還得定義這個回調函數,這裏需要注意的是,回調函數的寫法(返回值,參數),不是我們想怎麼寫就怎麼寫,幫助文檔裏已經規定好了回調函數應該如何寫,如果不按規定來寫,可能產生意想不到的錯誤。
GSignal是GStreamer的一個重要部分。它會讓你在你感興趣的事情發生時收到通知。信號是通過名字來區分的,每個GObject都有它自己的信號。使用g_signal_connect()方法把“pad-added”信號和我們的源(uridecodebin)聯繫了起來,並且註冊了一個回調函數。GStreamer把&data這個指針的內容傳給回調函數,這樣CustomData這個數據結構中的數據也就傳遞了過去。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章