1. 程式人生 > 其它 >GStreamer基礎教程01 - Hello World

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

天道酬勤 循序漸進 技壓群雄