GStreamer基礎教程02 - 基本概念
摘要
在 Gstreamer基礎教程01 - Hello World中,我們介紹瞭如何快速的通過一個字串建立一個簡單的pipeline。為了能夠更好的控制pipline中的element,我們需要單獨建立element,然後再構造pipeline,下面將介紹GStreamer的一些基本概念並展示pipeline的另一種構造方式。
基本概念
Element
我們知道element是構建GStreamer pipeline的基礎,element在框架中的型別為GstElement,所有GStreamer提供的解碼器(decoder),編碼器(encoder), 分離器(demuxer), 音視訊輸出裝置都是派生自GstElement。
那麼element到底是什麼呢?
從應用的角度,我們可以將一個element認為是一個功能塊,他實現一個特定的功能,比如:資料讀取,音訊解碼,聲音輸出等。各個功能塊之間可以通過某種特定的資料介面(這種介面稱為pad,將在後續章節講述)進行資料傳輸。每個element有唯一的型別,還有相應的屬性,用於控制element的行為。
為了更直觀的展現element,我們通常用一個框來表示一個element,在element內部使用小框表示pad。
這些功能塊有些可以生成資料,有些只接收資料,有些先接收資料,再生成資料。為了便於區分這些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的處理流程。如下圖:
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物件之間的繼承關係:
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
出處:http://www.cnblogs.com/xleng/
本文版權歸作者所有,歡迎轉載。商業轉載請聯絡作者獲得授權,非商業轉載請在文章頁面明顯位置給出原文連線.
相關文章
- Gstreamer基礎教程01 - Hello World
- GStreamer基礎教程05 - 播放時間控制
- GStreamer基礎教程04 - 動態連線Pipeline
- GStreamer基礎教程06 - 獲取媒體資訊
- GStreamer基礎教程03 - 媒體型別與Pad型別
- RabbitMq基礎教程之基本概念MQ
- 02、HTML 基礎HTML
- JAVA基礎02Java
- 【Pandas基礎教程】第02講 Pandas讀取資料
- Docker基礎與基本概念Docker
- Python基礎(02):字串Python字串
- Java 基礎02Java程式設計基礎Java程式設計
- 02go 基礎知識Go
- Linux學習-shell基礎02Linux
- 02_FreeRTOS基礎知識
- 02_Rust基礎入門Rust
- 數學基礎講解-02
- 02 uniapp專案基礎配置APP
- 01-linu核心基礎-02運維基礎重要概念運維
- vue 基礎入門筆記 02Vue筆記
- 02-ASP.Net(基礎介紹)ASP.NET
- JavaWeb基礎-學習筆記02JavaWeb筆記
- JavaScript基礎第02天筆記JavaScript筆記
- HTML基礎知識-day02HTML
- gstreamer教程(7)——構建應用之Bus的使用
- MotionLayout 基礎教程
- jQuery基礎教程jQuery
- Git基礎教程Git
- NMAP 基礎教程
- 【Matlab】基礎教程Matlab
- typora基礎教程
- Obsidian基礎教程
- tkinter 基礎教程
- Python基礎教程Python
- Java 多執行緒基礎(一)基本概念Java執行緒
- Java基礎知識篇02——封裝Java封裝
- Python基礎02 基本資料型別Python資料型別
- lec 02 arm組合語言基礎組合語言