GStreamer基礎教程02 - 基本概念

摘要

Gstreamer基礎教程01 - Hello World中,咱們介紹瞭如何快速的經過一個字符串建立一個簡單的pipeline。爲了可以更好的控制pipline中的element,咱們須要單首創建element,而後再構造pipeline,下面將介紹GStreamer的一些基本概念並展現pipeline的另外一種構造方式。html

 

基本概念

Element

咱們知道element是構建GStreamer pipeline的基礎,element在框架中的類型爲GstElement,全部GStreamer提供的解碼器(decoder),編碼器(encoder), 分離器(demuxer), 音視頻輸出設備都是派生自GstElement。網絡

那麼element究竟是什麼呢?
從應用的角度,咱們能夠將一個element認爲是一個功能塊,他實現一個特定的功能,好比:數據讀取,音頻解碼,聲音輸出等。各個功能塊之間能夠經過某種特定的數據接口(這種接口稱爲pad,將在後續章節講述)進行數據傳輸。每一個element有惟一的類型,還有相應的屬性,用於控制element的行爲。多線程

 

爲了更直觀的展示element,咱們一般用一個框來表示一個element,在element內部使用小框表示pad。app


這些功能塊有些能夠生成數據,有些只接收數據,有些先接收數據,再生成數據。爲了便於區分這些element,咱們把他們分爲三類:
1. source element
只能生成數據,不能接收數據的element稱爲source element。例如用於文件讀取的filesrc等。
對於source element,咱們一般用src pad表示element能產生數據,並將其放在element的右邊。source element只有src pad,經過設備、文件、網絡等方式讀取數據後,經過src pad向pipeline發送數據,開始pipeline的處理流程。以下圖:框架

2. sink element
只能接收數據,不能產生數據的element稱爲sink element。例如播放聲音的alsasink等。
對於sink element,咱們一般用sink pad表示element能接收處理數據,並將其放在element的左邊。sink element只有sink pad,從sink pad讀取數據後,將數據發送到指定設備或位置,結束pipeline的處理流程。以下圖:ide

3. filter-like element
既能接收數據,又能生成數據的element稱爲filter-like element。例如分離器,解碼器,音量控制器等。
對於filter-like element,既包含位於element左邊的sink pad,又包含位於element右邊的src pad。Element首先從sink pad讀取數據,而後對數據進行處理,最後在src pad產生新的數據。以下圖:函數

對於這些的element,可能包含多個src pad,也可能包含多個sink pad,例如mp4的demuxer(qtdemux)會將mp4文件中的音頻和視頻的分離到audio src pad和video src pad。而mp4的muxer(mp4mux)則相反,會將audio sink pad和video sink pad的數據合併到一個src pad,再經其餘element將數據寫入文件或發送到網絡。demuxer以下圖:源碼分析

 

鏈接element
當咱們有不少element時,咱們須要將他們按數據的傳輸路徑將其串聯起來,src pad只能聯接到sink pad,這樣纔可以實現相應的功能。測試

 

 

 

Bin和Pipeline

咱們將element串聯起來後就能實現相應的功能,爲何咱們還須要bin和pipline呢?咱們首先來看看在GStreamer框架中element,bin,pipeline對象之間的繼承關係:ui

GObject
    ╰──GInitiallyUnowned
        ╰──GstObject
            ╰──GstElement
                ╰──GstBin
                    ╰──GstPipeline

 

這裏bin和pipeline都是一個element,那麼bin和pipeline都在element的基礎上實現了什麼功能,解決了什麼問題呢?
咱們在建立了element多個element後,咱們須要對element進行狀態/資源管理,若是每次狀態改變時,都須要依次去操做每一個element,這樣每次編寫一個應用都會有大量的重複工做,這時就有了bin。
Bin繼承自element後,實現了容器的功能,能夠將多個element添加到bin,當操做bin時,bin會將相應的操做轉發到內部全部的element中,咱們能夠將bin認爲認爲是一個新的邏輯element,由bin來管理其內部element的狀態及資源,同事轉發其產生的消息。常見的bin有decodebin,autovideoconvert等。

 

Bin實現了容器的功能,那pipeline又有什麼功能呢?
在多媒體應用中,音視頻同步是一個基本的功能,須要支持這樣的功能,全部的element必需要有一個相同的時鐘,這樣才能保證各個音頻和視頻在同一時間輸出。pipeline就會爲其內部全部的element選擇一個相同的時鐘,同時還爲應用提供了bus系統,用於消息的接收。

 

Bus

剛纔咱們提到pipeline會提供一個bus,這個pipeline上全部的element均可以使用這個bus嚮應用程序發送消息。Bus主要是爲了解決多線程之間消息處理的問題。因爲GStreamer內部可能會建立多個線程,若是沒有bus,應用程序可能同時收到從多個線程的消息,若是應用程序在發送線程中經過回調去處理消息,應用程序有可能阻塞播放線程,形成播放卡頓,死鎖等其餘問題。爲了解決這類問題,GStreamer一般是將多個線程的消息發送到bus系統,由應用程序從bus中取出消息,而後進行處理。Bus在這裏扮演了消息隊列的角色,經過bus解耦了GStreamer框架和應用程序對消息的處理,下降了應用程序的複雜度。

 

Element Hello World

在有上面的知識後,咱們經過一個示例來看看element是如何建立及使用的。

#include <gst/gst.h>

int main (int argc, char *argv[]) { GstElement *pipeline, *source, *filter, *sink; GstBus *bus; GstMessage *msg; GstStateChangeReturn ret; /* Initialize GStreamer */ gst_init (&argc, &argv); /* Create the elements */ source = gst_element_factory_make ("videotestsrc", "source"); filter = gst_element_factory_make ("timeoverlay", "filter"); sink = gst_element_factory_make ("autovideosink", "sink"); /* Create the empty pipeline */ pipeline = gst_pipeline_new ("test-pipeline"); if (!pipeline || !source || !filter || !sink) { g_printerr ("Not all elements could be created.\n"); return -1; } /* Build the pipeline */ gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL); if (gst_element_link_many (source, filter, sink, NULL) != TRUE) { g_printerr ("Elements could not be linked.\n"); gst_object_unref (pipeline); return -1; } /* Modify the source's properties */ g_object_set (source, "pattern", 0, NULL); /* Start playing */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { g_printerr ("Unable to set the pipeline to the playing state.\n"); gst_object_unref (pipeline); return -1; } /* Wait until error or EOS */ bus = gst_element_get_bus (pipeline); msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS); /* Parse message */
  if (msg != NULL) { GError *err; gchar *debug_info; switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_ERROR: gst_message_parse_error (msg, &err, &debug_info); g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message); g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none"); g_clear_error (&err); g_free (debug_info); break; case GST_MESSAGE_EOS: g_print ("End-Of-Stream reached.\n"); break; default: /* We should not reach here because we only asked for ERRORs and EOS */ g_printerr ("Unexpected message received.\n"); break; } gst_message_unref (msg); } /* Free resources */ gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); return 0; }

編譯並運行示例,能夠看到彈出的窗口中播放着測試視頻,而且還顯示着播放時間。

$ gcc basic-tutorial-2.c -o basic-tutorial-2 `pkg-config --cflags --libs gstreamer-1.0`
$ ./basic-tutorial-2

 

源碼分析

建立Element

/* Create the elements */ source = gst_element_factory_make ("videotestsrc", "source"); filter = gst_element_factory_make ("timeoverlay", "filter"); sink = gst_element_factory_make ("autovideosink", "sink");

在對GStreamer進行初始化後,咱們能夠經過gst_element_factory_make建立element。第一個參數是element的類型,能夠經過這個字符串,找到對應的類型,從而建立element對象。第二個參數指定了建立element的名字,當咱們沒有保存建立element的對象指針時,咱們能夠經過gst_bin_get_by_name從pipeline中取得該element的對象指針。若是第二個參數爲NULL,則GStreamer內部會爲該element自動生成一個惟一的名字。
咱們在當前示例中建立了3個element:videotestsrc,timeoverlay,autovideosink,其做用分別爲:

  • videotestsrc是一個source element,用於產生視頻數據,一般用於調試。
  • timeoverlay是一個filter-like element,能夠在視頻數據中疊加一個時間字符串。
  • autovideosink上一個sink element,用於自動選擇視頻輸出設備,建立視頻顯示窗口,並顯示其收到的數據。

 

建立Pipeline

 /* Create the empty pipeline */ pipeline = gst_pipeline_new ("test-pipeline");

Pipeline經過gst_pipeline_new建立,參數爲pipeline的名字。


咱們知道pipeline會提供播放所必須的時鐘以及對消息的處理,因此咱們須要把咱們建立的element添加到pipeline中。

/* Build the pipeline */ gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL); if (gst_element_link_many (source, filter, sink, NULL) != TRUE) { g_printerr ("Elements could not be linked.\n"); gst_object_unref (pipeline); return -1; }

從上面的講解咱們知道pipeline是繼承自bin,因此全部bin的方法均可以應用於pipeline,須要注意的是,咱們須要經過相應的宏(這裏是GST_BIN)來將子類轉換爲父類,宏內部會對其作類型檢查。在這裏咱們使用gst_bin_add_many將多個element加入到pipeline中,這個函數接受任意多個參數,最後以NULL表示參數列表的結束。若是一次只須要加入一個,可使用gst_bin_add函數。


在將element加入bin後,咱們須要將其鏈接起來才能完成相應的功能,因爲有多個element,因此咱們這裏使用gst_element_link_many,element會根據參數的順序依次將element鏈接起來。

須要注意的是,只有被加入到同一個bin的element纔可以被鏈接在一塊兒,因此咱們須要在鏈接前,將所須要的element加入到pipeline/bin中。

 

至此,咱們已經完成了pipeline的建立,test-pipeline能夠表示爲:

 

 設置element屬性

/* Modify the source's properties */ g_object_set (source, "pattern", 0, NULL);

大部分的element都有本身的屬性。有的屬性只能被讀取,這種屬性經常使用於查詢element的狀態。有的屬性同時支持修改,這種屬性經常使用於控制element的行爲。


因爲GstElement繼承於GObject,同時GObject對象系統提供了 g_object_get()用於讀取屬性,g_object_set()用於修改屬性,g_object_set()支持以NULL結束的屬性-值的鍵值對,因此能夠一次修改element的多個屬性。

咱們這裏經過g_object_set()來修改videotestsrc的pattern屬性。pattern屬性能夠控制測試圖像的類型,能夠嘗試將0修改成1,查看輸出結果有何不一樣。

咱們能夠經過gst-inspect-1.0 videotestsrc命令來查看pattern所支持的全部值。

 

設置播放狀態

  /* Start playing */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { g_printerr ("Unable to set the pipeline to the playing state.\n"); gst_object_unref (pipeline); return -1; }

在完成pipeline的建立以及屬性的修改後,咱們將pipeline的狀態設置爲PLAYING,這裏與上一文章中的示例相同,只增長來錯誤處理,其餘的返回值處理講在後續章節講述。

 

等待播放結束與釋放資源

這部份內容與上一文章中的示例相同,這裏只增長了消息類型的判斷。更多關於消息的內容將在後續章節講述。
因爲videotestsrc會持續輸出視頻數據,因此咱們在這個例子中不會受到EOS消息,只有當咱們關閉視頻窗口時會收到error消息,發送消息的element名和具體的消息內容會經過終端輸出。

 

總結

在本教程中,咱們掌握了:

  • GStreamer element,bin,pipeline,bus的基本概念。
  • 如何使用gst_element_factory_make ()建立element。
  • 如何使用gst_pipeline_new ()建立pipeline。
  • 如何使用gst_bin_add_many ()將多個element加入pipeline。
  • 如何使用gst_element_link_many ()將多個element鏈接起來。

在這兩篇文章中,咱們介紹了pipeline的兩種建立方式,下一篇文章咱們將介紹GStreamer是如何保證element能夠正確的鏈接在一塊兒。


引用

https://gstreamer.freedesktop.org/documentation/tutorials/basic/concepts.html?gi-language=c
https://gstreamer.freedesktop.org/documentation/application-development/basics/elements.html?gi-language=c
https://gstreamer.freedesktop.org/documentation/application-development/basics/bins.html?gi-language=c
https://gstreamer.freedesktop.org/documentation/gstreamer/gstpipeline.html?gi-language=c

 

做者: John.Leng
本文版權歸做者全部,歡迎轉載。商業轉載請聯繫做者得到受權,非商業轉載請在文章頁面明顯位置給出原文鏈接.
相關文章
相關標籤/搜索