GStreamer 入門 - Hello,World

GStreamer 是一個(gè)用于構(gòu)建媒體處理組件圖(也可以稱為 pipeline,或管道)的庫(kù)。它支持的應(yīng)用非常廣泛,從簡(jiǎn)單的 Ogg/Vorbis 播放,音頻/視頻流到復(fù)雜的音頻(混音)和視頻(非線性編輯)處理。

應(yīng)用程序可以透明地利用編解碼和過濾器技術(shù)的進(jìn)步。開發(fā)者可以通過編寫簡(jiǎn)單的基于一個(gè)干凈、通用的接口的插件,來添加新的編解碼器和過濾器。

GStreamer 可以運(yùn)行于所有主要的操作系統(tǒng)平臺(tái),如 Linux,Android,Windows,Max OS X,iOS,以及大部分 BSDs,商業(yè) Unixes,Solaris,和 Symbian。它已經(jīng)被移植到了廣泛的操作系統(tǒng),處理器,和編譯器平臺(tái)上。它可以運(yùn)行于所有主要的硬件架構(gòu)上,包括 x86,ARM,MIPS,SPARC 和 PowerPC,32 位以及 64 位上,以及小尾端或大尾端。

GStreamer 可以橋接到其它多媒體框架,以復(fù)用已有的組件(比如編解碼器)及使用平臺(tái)的輸入/輸出機(jī)制:

  • Linux/Unix:OpenMAX-IL (via gst-omx)
  • Windows::DirectShow
  • Mac OS X:QuickTime

GStreamer 核心框架

  • 基于圖的結(jié)構(gòu)允許構(gòu)建任何形態(tài)的管線
  • 基于 GLib 2.0 對(duì)象模型 的面向?qū)ο笤O(shè)計(jì)和繼承
  • 小于 500KB 的緊湊的核心庫(kù),大約有 65k 行代碼
  • 構(gòu)建多線程的管線是容易的且透明的
  • 對(duì)于插件和應(yīng)用程序開發(fā)者,都有著干凈,簡(jiǎn)單和穩(wěn)定的 API
  • 極端輕量的數(shù)據(jù)處理意味著非常的高性能/低延遲。
  • 無論是對(duì)于核心架構(gòu),還是對(duì)于插件/應(yīng)用程序的開發(fā)者,都有完整的調(diào)試系統(tǒng)
  • 具有時(shí)鐘來確保全局的流間同步(a/v 同步)
  • 具有服務(wù)質(zhì)量 (qos) 來確保在高 CPU 負(fù)載下最優(yōu)的可能質(zhì)量。

智能插件架構(gòu)

  • 動(dòng)態(tài)加載 的插件提供元素和媒體類型,通過一個(gè)注冊(cè)表緩存按需加載,與 ld.so.cache 類似。
  • 元素接口 處理所有已知類型的 source,過濾器,和 sinks
  • 權(quán)能系統(tǒng) 允許使用 MIME 類型和媒體特有的屬性驗(yàn)證元素的兼容性
  • 自動(dòng)插拔使用權(quán)能系統(tǒng)自動(dòng)完成復(fù)雜的路徑
  • 可以通過把管線轉(zhuǎn)儲(chǔ)為一個(gè) .dot 文件來將它可視化,并基于此創(chuàng)建一副 PNG 圖像。
  • 資源友好型插件不會(huì)浪費(fèi)內(nèi)存

多媒體技術(shù)的廣泛覆蓋

GStreamers 的能力可以通過新插件來擴(kuò)展。下面列出的功能只是使用 GStreamers 時(shí)可用的 GStreamer 自己的插件的粗糙的概覽,不包括任何第三方提供的。

  • 容器格式:asf,avi,3gp/mp4/mov,flv,mpeg-ps/ts,mkv/webm,mxf,ogg
  • 流:http,mms,rtsp
  • 編解碼器:FFmpeg,各種編解碼庫(kù),第三方的編解碼包
  • 元數(shù)據(jù):它們之間有著公共映射的本地容器格式
  • 視頻:各種顏色空間,支持漸進(jìn)式和交錯(cuò)視頻
  • 音頻:具有各種位深度的整型和浮點(diǎn)型音頻數(shù)據(jù)和多通道配置

大量的開發(fā)工具

GStreamer 是一個(gè)靈活,快速和多平臺(tái)的多媒體框架。

GStreamer 是一個(gè)極端強(qiáng)大和功能豐富的用于創(chuàng)建流媒體應(yīng)用的框架。GStreamer 框架的許多優(yōu)點(diǎn)來自于它的模塊化:GStreamer 可以與新插件模塊無縫的協(xié)同工作。但由于模塊化和強(qiáng)大常常是以更大的復(fù)雜性為代價(jià)的,因而編寫新應(yīng)用并不總是那么簡(jiǎn)單。

GStreamer 編譯

GStreamer 是一個(gè)開源的多媒體框架,因而從源碼編譯也是非常方便的。GStreamer 提供了 Meson 和 Cerbero 等編譯方式。這里用 Meson 在 Ubuntu Linux 上編譯 GStreamer。

Meson 構(gòu)建系統(tǒng)是一個(gè)快速的可移植的構(gòu)建系統(tǒng)。它根據(jù)構(gòu)建配置文件,生成可以被 ninja 執(zhí)行的構(gòu)建指令,GStreamer 項(xiàng)目使用它作為所有子項(xiàng)目的構(gòu)建系統(tǒng)??梢允褂萌缦旅畎惭b ninja:

sudo apt-get install ninja-build

要編譯最新版本的 GStreamer,Ubuntu 的軟件源里的 meson 版本可能有點(diǎn)老,如 Ubuntu 20.04 版本通過 apt 安裝的 meson 0.53.2 版,在編譯 GStreamer master branch 的代碼時(shí),報(bào)錯(cuò)說版本太老:

hanpfei@ubuntu:~/Data/opensource/gstreamer$ meson build
The Meson build system
Version: 0.53.2
Source dir: /home/hanpfei/Data/opensource/gstreamer
Build dir: /home/hanpfei/Data/opensource/gstreamer/build
Build type: native build

meson.build:1:0: ERROR: Meson version is 0.53.2 but project requires >= 0.54

A full log can be found at /home/hanpfei/Data/opensource/gstreamer/build/meson-logs/meson-log.txt

最好用 Python 的 pip 工具來安裝 meson(如果已經(jīng)通過 apt 安裝了 meson,需要先把它移除掉):

hanpfei@ubuntu:~/Data/opensource/gstreamer$ sudo apt install python3-pip
hanpfei@ubuntu:~/Data/opensource/gstreamer$ python3 -m pip install meson

Python 的 pip 工具將 meson 安裝在了用戶根目錄下的一個(gè)隱藏目錄 ~/.local/bin 下,如 /home/hanpfei/.local/bin,這個(gè)路徑還需要被加進(jìn) PATH 環(huán)境變量里。

ninja 也可以用 Python 的 pip 工具來安裝:

hanpfei@ubuntu:~/Data/opensource/gstreamer$ python3 -m pip install ninja

此外,在編譯 GStreamer 之前,還需要安裝一些依賴:

hanpfei@ubuntu:~/Data/opensource/gstreamer$ sudo apt-get install flex bison

在 2021 年 9 月,所有主要的 GStreamer 模塊都被合入了一個(gè)單獨(dú)的代碼倉(cāng)庫(kù),GStreamer 單獨(dú)的倉(cāng)庫(kù) 位于主 GStreamer git 倉(cāng)庫(kù),這是如今 GStreamer 版本 1.19/1.20 及之后版本的所有 GStreamer 開發(fā)將發(fā)生的地方。

在這個(gè)單獨(dú)的倉(cāng)庫(kù)合并位于分開的 git 倉(cāng)庫(kù)中不同的 GStreamer 模塊之前,有一個(gè)稱為 gst-build 的分開的元構(gòu)建工程用于下載并構(gòu)建所有的子項(xiàng)目。如果你想要開發(fā)更老的穩(wěn)定分支,比如 GStreamer 1.16 或 1.18,則你應(yīng)該使用它。

如果你想要構(gòu)建或開發(fā)即將到來的開發(fā)或穩(wěn)定分支,你應(yīng)該使用包含在單個(gè)代碼倉(cāng)庫(kù)中的 GStreamer 模塊的 main 分支。gst-build 與單個(gè)代碼倉(cāng)庫(kù)的工作方式基本相同,僅有的不同是它將下載各種 GStreamer 子模塊。

為了構(gòu)建當(dāng)前的 GStreamer 開發(fā)版本,它將在不遠(yuǎn)的未來變?yōu)?1.20 穩(wěn)定分支,需要先 clone GStreamer 倉(cāng)庫(kù):

git clone https://gitlab.freedesktop.org/gstreamer/gstreamer.git
cd gstreamer

或者如果你具有這個(gè)倉(cāng)庫(kù)的開發(fā)者權(quán)限的話:

git clone git@gitlab.freedesktop.org:gstreamer/gstreamer.git
cd gstreamer

如果你想要構(gòu)建穩(wěn)定的 1.18 或 1.16 分支,則 clone gst-build

git clone https://gitlab.freedesktop.org/gstreamer/gst-build.git
cd gst-build

倉(cāng)庫(kù)中包含了一些值得注意的腳本和目錄:

  1. meson.build 是頂層的構(gòu)建定義,它遞歸地配置所有依賴。它也定義了一些輔助命令,使你可以有一個(gè)未安裝的開發(fā)環(huán)境或簡(jiǎn)單地更新 GStreamer 模塊的 git 倉(cāng)庫(kù)。
  2. subprojects/ 是包含了 GStreamer 模塊和一系列依賴的目錄。

通過執(zhí)行如下命令配置一個(gè)模塊(或在 gst-build 下一次配置多個(gè)):

meson <build_directory>

其中 build_directory 是所有的構(gòu)建指令和輸出將放置的位置(這也被稱為 “輸出目錄” 構(gòu)建)。如果目錄還沒有創(chuàng)建,則它將在此時(shí)創(chuàng)建。注意調(diào)用 meson 不需要任何命令參數(shù)其實(shí)是隱式地調(diào)用了 meson setup 命令(比如執(zhí)行一個(gè)工程的初始化配置)。

build_directory 的位置而言只有一個(gè)限制:它不能與源碼目錄(比如你下載你的模塊的目錄)相同。盡管它可以位于目錄的外面或下面/里面。

一旦 meson 配置完成,你可以:

  1. 進(jìn)入特定的構(gòu)建目錄并運(yùn)行 ninja:
cd <build_directory>
ninja
  1. 或不要在每次想要執(zhí)行 ninja 時(shí)都切換到構(gòu)建目錄,你可以只指定構(gòu)建目錄作為一個(gè)參數(shù)。這個(gè)選項(xiàng)的好處是你可以在任何地方執(zhí)行(而不是切換到 ninja 目錄)
ninja -C </path/to/build_directory>

這將構(gòu)建那個(gè)模塊(和子工程如果構(gòu)建 gst-build 或單個(gè)倉(cāng)庫(kù))的所有東西。

注意:當(dāng)你修改源文件時(shí)你不需要重新運(yùn)行 meson,你只需要重新運(yùn)行 ninja。如果構(gòu)建/配置文件發(fā)生了改變,ninja 將自己判斷出來 meson 需要重新運(yùn)行并將自動(dòng)地運(yùn)行它。

Hello,World

要獲得對(duì)于一個(gè)庫(kù)的第一印象,再也沒有比跑起來一個(gè)基于這個(gè)庫(kù)開發(fā)的,在屏幕上輸出 “Hello World” 的應(yīng)用更好的方式了。但這里要處理的是多媒體框架,這里將用播放一個(gè)媒體文件來替代。

上面的構(gòu)建過程也將一并構(gòu)建出 GStreamer 工程的測(cè)試和示例應(yīng)用,其中包括 GStreamer 的 helloworld 示例應(yīng)用。這個(gè)應(yīng)用的代碼位于 gstreamer/tests/examples/helloworld,編譯之后生成的二進(jìn)制可執(zhí)行文件位于 <build_directory>/tests/examples/helloworld/helloworld。

helloworld 示例應(yīng)用的源碼如下:

#include <gst/gst.h>

static gboolean
bus_call (GstBus * bus, GstMessage * msg, gpointer data)
{
  GMainLoop *loop = (GMainLoop *) data;

  switch (GST_MESSAGE_TYPE (msg)) {
    case GST_MESSAGE_EOS:{
      g_print ("End-of-stream\n");
      g_main_loop_quit (loop);
      break;
    }
    case GST_MESSAGE_ERROR:{
      gchar *debug;
      GError *err;

      gst_message_parse_error (msg, &err, &debug);
      g_printerr ("Debugging info: %s\n", (debug) ? debug : "none");
      g_free (debug);

      g_print ("Error: %s\n", err->message);
      g_error_free (err);

      g_main_loop_quit (loop);

      break;
    }
    default:
      break;
  }
  return TRUE;
}

gint
main (gint argc, gchar * argv[])
{
  GstElement *playbin;
  GMainLoop *loop;
  GstBus *bus;
  guint bus_watch_id;
  gchar *uri;

  gst_init (&argc, &argv);

  if (argc < 2) {
    g_print ("usage: %s <media file or uri>\n", argv[0]);
    return 1;
  }

  playbin = gst_element_factory_make ("playbin", NULL);
  if (!playbin) {
    g_print ("'playbin' gstreamer plugin missing\n");
    return 1;
  }

  /* take the commandline argument and ensure that it is a uri */
  if (gst_uri_is_valid (argv[1]))
    uri = g_strdup (argv[1]);
  else
    uri = gst_filename_to_uri (argv[1], NULL);
  g_object_set (playbin, "uri", uri, NULL);
  g_free (uri);

  /* create an event loop and feed gstreamer bus messages to it */
  loop = g_main_loop_new (NULL, FALSE);

  bus = gst_element_get_bus (playbin);
  bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

  /* start play back and listed to events */
  gst_element_set_state (playbin, GST_STATE_PLAYING);
  g_main_loop_run (loop);

  /* cleanup */
  gst_element_set_state (playbin, GST_STATE_NULL);
  gst_object_unref (playbin);
  g_source_remove (bus_watch_id);
  g_main_loop_unref (loop);

  return 0;
}

這個(gè)應(yīng)用接收一個(gè)媒體資源的 URI,并播放這個(gè)媒體資源。具體用法如下:

$ build/tests/examples/helloworld/helloworld [URI]

如播放一個(gè) AAC 文件:

$ build/tests/examples/helloworld/helloworld file:///home/hanpfei/aac.aac

但用這個(gè)應(yīng)用播放媒體文件時(shí),報(bào)了插件找不到的錯(cuò),如:

$ build/tests/examples/helloworld/helloworld file:///home/hanpfei/aac.aac 
'playbin' gstreamer plugin missing

helloworld 應(yīng)用通過插件 "playbin" 來播放媒體文件。一般來說,插件的物理形式是一個(gè)動(dòng)態(tài)鏈接庫(kù),GStreamer 框架在初始化過程中會(huì)到特定的目錄下尋找插件。用戶可以通過環(huán)境變量 GST_PLUGIN_PATH 來為 GStreamer 指定插件的搜索路徑,或者以編程的方式,來讓 GStreamer 在特定的目錄中搜索插件,如:

  GstRegistry *registry;

  registry = gst_registry_get();
  gst_registry_scan_path(registry, "/usr/lib/x86_64-linux-gnu/gstreamer-1.0");

上面看到的 "playbin" 插件,是 GStreamer 項(xiàng)目本身提供的一個(gè)基礎(chǔ)的插件,其代碼位于 gst-plugins-base 項(xiàng)目中,這個(gè)項(xiàng)目的具體位置為 https://github.com/GStreamer/gst-plugins-base。對(duì)于 Ubuntu Linux,也可以通過如下命令安裝編譯好的二進(jìn)制:

$ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev 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-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

這個(gè)命令一次性安裝 GStreamer 的各種開發(fā)庫(kù),其中 gstreamer1.0-plugins-base 里會(huì)包含 "playbin" 這個(gè)插件,這個(gè)插件一般會(huì)安裝在 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstplayback.so

有了這些之后,helloworld 即可以正常運(yùn)行起來。

接下來就可以開始愉快地探索 GStreamer 的概念和操作了。

參考文檔:
GStreamer Documentation
Installing GStreamer on Linux

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容