摘要
在面對一個新的軟件庫時,第一步通常實現一個“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