GStreamer基礎教程01 - Hello World
摘要
在面對一個新的軟體庫時,第一步通常實現一個“hello world”程式,來了解庫的用法。對於GStreamer,我們可以實現一個極簡的播放器,來了解GStreamer的使用。
環境配置
為了快速掌握Gstreamer相關的知識,我們優先選擇Ubuntu作為我們的開發環境,其他平臺的開發會在後續文章單獨介紹。如果還沒有Ubuntu虛擬機器,可以在OSBoxes中直接下載Ubuntu 18.04的VirtualBox或VMware映象檔案,節省安裝時間。
安裝編譯工具及庫
我們在基本介紹中提到,gstreamer的框架及外掛位於不同的原始碼包中,所以我們需要安裝多個軟體包:
$ sudo apt-get install gcc build-essential libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools \ gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-qt5 gstreamer1.0-pulseaudio
Hello World
我們首先使用官方的HelloWorld作為我們的第一個應用:basic-tutorial-1.c
#include <gst/gst.h> int main (int argc, char *argv[]) { GstElement *pipeline; GstBus *bus; GstMessage *msg; /* Initialize GStreamer */ gst_init (&argc, &argv); /* Build the pipeline */ pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL); /* Start playing */ gst_element_set_state (pipeline, GST_STATE_PLAYING); /* 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); /* Free resources */ if (msg != NULL) gst_message_unref (msg); gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); return 0; }
通過下面的命令編譯得到可執行程式
$ gcc basic-tutorial-1.c -o basic-tutorial-1 `pkg-config --cflags --libs gstreamer-1.0`
編譯成功後,我們可以得到可執行檔案,執行 basic-tutorial-1,會在彈出的視窗中,自動讀取伺服器上的sintel_trailer-480p.webm視訊檔案並播放。如果網路環境不理想,在播放的過程中會經常處理緩衝狀態,造成播放卡頓。也可以先下載媒體檔案,將uri的http路徑替換為本地uri(例如: uri=file:///home/john/sintel_trailer-480p.webm)避免網路的影響。
原始碼分析
通過上面的程式碼,我們達到了播放一個視訊檔案的目的,接下來通過分析這個簡短的程式來了解gstreamer應用是如何工作的。
GStreamer初始化
/* Initialize GStreamer */ gst_init (&argc, &argv);
首先我們呼叫了gstreamer的初始化函式,該初始化函式必須在其他gstreamer介面之前被呼叫,gst_init會負責以下資源的初始化:
- 初始化GStreamer庫
- 註冊內部element
- 載入外掛列表,掃描列表中及相應路徑下的外掛
- 解析並執行命令列引數
在不需要gst_init處理命令列引數時,我們可以講NULL作為其引數,例如:gst_init(NULL, NULL);
建立Pipeline
/* Build the pipeline */ pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
這一行是示例中的核心邏輯,展示瞭如何通過gst_parse_launch 建立一個playbin的pipeline,並設定播放檔案的uri。
gst_parse_launch
在基本介紹中我們瞭解了Pipeline的概念,在pipeline中,首先通過“source” element獲取媒體資料,然後通過一個或多個element對編碼資料進行解碼,最後通過“sink” element輸出聲音和畫面。通常在建立較複雜的pipeline時,我們需要通過gst_element_factory_make來建立element,然後將其加入到GStreamer Bin中,並連線起來。當pipeline比較簡單並且我們不需要對pipeline中的element進行過多的控制時,我們可以採用gst_parse_launch 來簡化pipeline的建立。
這個函式能夠巧妙的將pipeline的文字描述轉化為pipeline物件,我們也經常需要通過文字方式構建pipeline來檢視GStreamer是否支援相應的功能,因此GStreamer提供了gst-launch-1.0命令列工具,極大的方便了pipeline的測試。
playbin
我們知道pipeline中需要新增特定的element以實現相應的功能,在本例中,我們通過gst_parse_launch建立了只包含一個element的Pipeline。
我們剛提到pipeline需要有“source”、“sink” element,為什麼這裡只需要一個playbin就夠了呢?是因為playbin element內部會根據檔案的型別自動去查詢所需要的“source”,“decoder”,”sink”並將它們連線起來,同時提供了部分介面用於控制pipeline中相應的element。
在playbin後,我們跟了一個uri引數,指定了我們想要播放的媒體檔案地址,playbin會根據uri所使用的協議(“https://”,“ftp://”,“file://”等)自動選擇合適的source element(此例中通過https方式)獲取資料。
設定播放狀態
/* Start playing */ gst_element_set_state (pipeline, GST_STATE_PLAYING);
這一行程式碼引入了一個新的概念“狀態”(state)。每個GStreamer element都有相應都狀態,我們目前可以簡單的把狀態與播放器的播放/暫停按鈕聯絡起來,只有當狀態處於PLAYING時,pipeline才會播放/處理資料。
這裡gst_element_set_state通過pipeline,將playbin的狀態設定為PLAYING,使playbin開始播放視訊檔案。
等待播放結束
/* 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);
這幾行會等待pipeline播放結束或者播放出錯。我們知道GStreamer框架會通過bus,將所發生的事件通知到應用程式,因此,這裡首先取得pipeline的bus物件,通過gst_bus_timed_pop_filtered 以同步的方式等待bus上的ERROR或EOS(End of Stream)訊息,該函式收到訊息後才會返回。
我們會在下一篇文章中繼續介紹訊息相關的內容。
到目前為止,GStreamer會處理視訊播放的所有工作(資料獲取,解碼,音視訊同步,輸出)。當到達檔案末端(EOS)或出錯(直接關閉播放視窗,斷開網路)時,播放會自動停止。我們也可以在終端通過ctrl+c中斷程式的執行。
釋放資源
/* Free resources */ if (msg != NULL) gst_message_unref (msg); gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline);
這裡我們將不再使用的msg,bus物件進行銷燬,並將pipeline狀態設定為NULL(在NULL狀態時GStreamer會釋放為pipeline分配的所有資源),最後銷燬pipeline物件。由於GStreamer是繼承自GObject,所以需要通過gst_object_unref 來減少引用計數,當物件的引用計數為0時,函式內部會自動釋放為其分配的記憶體。
不同介面會對返回的物件進行不同的處理,我們需要詳細的閱讀API文件,來決定我們是否需要對返回的物件進行釋放。
總結
在本教程中,我們掌握了:
- 如何中Ubuntu下搭建GStreamer的開發環境。
- 如何通過gst_init()初始化GStreamer。
- 如何通過gst_parse_launch()快速構建一個pipeline。
- 如何使用playbin自動播放檔案。
- 如何使用gst_element_set_state()來控制pipeline開始播放。
- 如何通過bus等待播放結束。
在下一篇文章中,我們將繼續介紹GStreamer的基本概念,以及pipeline的另一種構造方式。
引用
https://gstreamer.freedesktop.org/documentation/tutorials/basic/hello-world.html
https://gstreamer.freedesktop.org/documentation/installing/on-linux.html