GStreamer 入门 - Hello,World

·  阅读 501

GStreamer 是一个用于构建媒体处理组件图(也可以称为 pipeline,或管道)的库。它支持的应用非常广泛,从简单的 Ogg/Vorbis 播放,音频/视频流到复杂的音频(混音)和视频(非线性编辑)处理。

应用程序可以透明地利用编解码和过滤器技术的进步。开发者可以通过编写简单的基于一个干净、通用的接口的插件,来添加新的编解码器和过滤器。

GStreamer 可以运行于所有主要的操作系统平台,如 Linux,Android,Windows,Max OS X,iOS,以及大部分 BSDs,商业 Unixes,Solaris,和 Symbian。它已经被移植到了广泛的操作系统,处理器,和编译器平台上。它可以运行于所有主要的硬件架构上,包括 x86,ARM,MIPS,SPARC 和 PowerPC,32 位以及 64 位上,以及小尾端或大尾端。

GStreamer 可以桥接到其它多媒体框架,以复用已有的组件(比如编解码器)及使用平台的输入/输出机制:

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

GStreamer 核心框架

  • 基于图的结构允许构建任何形态的管线
  • 基于 GLib 2.0 对象模型 的面向对象设计和继承
  • 小于 500KB 的紧凑的核心库,大约有 65k 行代码
  • 构建多线程的管线是容易的且透明的
  • 对于插件和应用程序开发者,都有着干净,简单和稳定的 API
  • 极端轻量的数据处理意味着非常的高性能/低延迟。
  • 无论是对于核心架构,还是对于插件/应用程序的开发者,都有完整的调试系统
  • 具有时钟来确保全局的流间同步(a/v 同步)
  • 具有服务质量 (qos) 来确保在高 CPU 负载下最优的可能质量。

智能插件架构

  • 动态加载 的插件提供元素和媒体类型,通过一个注册表缓存按需加载,与 ld.so.cache 类似。
  • 元素接口 处理所有已知类型的 source,过滤器,和 sinks
  • 权能系统 允许使用 MIME 类型和媒体特有的属性验证元素的兼容性
  • 自动插拔使用权能系统自动完成复杂的路径
  • 可以通过把管线转储为一个 .dot 文件来将它可视化,并基于此创建一副 PNG 图像。
  • 资源友好型插件不会浪费内存

多媒体技术的广泛覆盖

GStreamers 的能力可以通过新插件来扩展。下面列出的功能只是使用 GStreamers 时可用的 GStreamer 自己的插件的粗糙的概览,不包括任何第三方提供的。

  • 容器格式:asf,avi,3gp/mp4/mov,flv,mpeg-ps/ts,mkv/webm,mxf,ogg
  • 流:http,mms,rtsp
  • 编解码器:FFmpeg,各种编解码库,第三方的编解码包
  • 元数据:它们之间有着公共映射的本地容器格式
  • 视频:各种颜色空间,支持渐进式和交错视频
  • 音频:具有各种位深度的整型和浮点型音频数据和多通道配置

大量的开发工具

GStreamer 是一个灵活,快速和多平台的多媒体框架。

GStreamer 是一个极端强大和功能丰富的用于创建流媒体应用的框架。GStreamer 框架的许多优点来自于它的模块化:GStreamer 可以与新插件模块无缝的协同工作。但由于模块化和强大常常是以更大的复杂性为代价的,因而编写新应用并不总是那么简单。

GStreamer 编译

GStreamer 是一个开源的多媒体框架,因而从源码编译也是非常方便的。GStreamer 提供了 Meson 和 Cerbero 等编译方式。这里用 Meson 在 Ubuntu Linux 上编译 GStreamer。

Meson 构建系统是一个快速的可移植的构建系统。它根据构建配置文件,生成可以被 ninja 执行的构建指令,GStreamer 项目使用它作为所有子项目的构建系统。可以使用如下命令安装 ninja:

sudo apt-get install ninja-build
复制代码

要编译最新版本的 GStreamer,Ubuntu 的软件源里的 meson 版本可能有点老,如 Ubuntu 20.04 版本通过 apt 安装的 meson 0.53.2 版,在编译 GStreamer master branch 的代码时,报错说版本太老:

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(如果已经通过 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 安装在了用户根目录下的一个隐藏目录 ~/.local/bin 下,如 /home/hanpfei/.local/bin,这个路径还需要被加进 PATH 环境变量里。

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 模块都被合入了一个单独的代码仓库,GStreamer 单独的仓库 位于主 GStreamer git 仓库,这是如今 GStreamer 版本 1.19/1.20 及之后版本的所有 GStreamer 开发将发生的地方。

在这个单独的仓库合并位于分开的 git 仓库中不同的 GStreamer 模块之前,有一个称为 gst-build 的分开的元构建工程用于下载并构建所有的子项目。如果你想要开发更老的稳定分支,比如 GStreamer 1.16 或 1.18,则你应该使用它。

如果你想要构建或开发即将到来的开发或稳定分支,你应该使用包含在单个代码仓库中的 GStreamer 模块的 main 分支。gst-build 与单个代码仓库的工作方式基本相同,仅有的不同是它将下载各种 GStreamer 子模块。

为了构建当前的 GStreamer 开发版本,它将在不远的未来变为 1.20 稳定分支,需要先 clone GStreamer 仓库:

git clone https://gitlab.freedesktop.org/gstreamer/gstreamer.git
cd gstreamer
复制代码

或者如果你具有这个仓库的开发者权限的话:

git clone git@gitlab.freedesktop.org:gstreamer/gstreamer.git
cd gstreamer
复制代码

如果你想要构建稳定的 1.18 或 1.16 分支,则 clone gst-build

git clone https://gitlab.freedesktop.org/gstreamer/gst-build.git
cd gst-build
复制代码

仓库中包含了一些值得注意的脚本和目录:

  1. meson.build 是顶层的构建定义,它递归地配置所有依赖。它也定义了一些辅助命令,使你可以有一个未安装的开发环境或简单地更新 GStreamer 模块的 git 仓库。
  2. subprojects/ 是包含了 GStreamer 模块和一系列依赖的目录。

通过执行如下命令配置一个模块(或在 gst-build 下一次配置多个):

meson <build_directory>
复制代码

其中 build_directory 是所有的构建指令和输出将放置的位置(这也被称为 “输出目录” 构建)。如果目录还没有创建,则它将在此时创建。注意调用 meson 不需要任何命令参数其实是隐式地调用了 meson setup 命令(比如执行一个工程的初始化配置)。

build_directory 的位置而言只有一个限制:它不能与源码目录(比如你下载你的模块的目录)相同。尽管它可以位于目录的外面或下面/里面。

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

  1. 进入特定的构建目录并运行 ninja:
cd <build_directory>
ninja
复制代码
  1. 或不要在每次想要执行 ninja 时都切换到构建目录,你可以只指定构建目录作为一个参数。这个选项的好处是你可以在任何地方执行(而不是切换到 ninja 目录)
ninja -C </path/to/build_directory>
复制代码

这将构建那个模块(和子工程如果构建 gst-build 或单个仓库)的所有东西。

注意:当你修改源文件时你不需要重新运行 meson,你只需要重新运行 ninja。如果构建/配置文件发生了改变,ninja 将自己判断出来 meson 需要重新运行并将自动地运行它。

Hello,World

要获得对于一个库的第一印象,再也没有比跑起来一个基于这个库开发的,在屏幕上输出 “Hello World” 的应用更好的方式了。但这里要处理的是多媒体框架,这里将用播放一个媒体文件来替代。

上面的构建过程也将一并构建出 GStreamer 工程的测试和示例应用,其中包括 GStreamer 的 helloworld 示例应用。这个应用的代码位于 gstreamer/tests/examples/helloworld,编译之后生成的二进制可执行文件位于 <build_directory>/tests/examples/helloworld/helloworld

helloworld 示例应用的源码如下:

#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;
}
复制代码

这个应用接收一个媒体资源的 URI,并播放这个媒体资源。具体用法如下:

$ build/tests/examples/helloworld/helloworld [URI]
复制代码

如播放一个 AAC 文件:

$ build/tests/examples/helloworld/helloworld file:///home/hanpfei/aac.aac
复制代码

但用这个应用播放媒体文件时,报了插件找不到的错,如:

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

helloworld 应用通过插件 "playbin" 来播放媒体文件。一般来说,插件的物理形式是一个动态链接库,GStreamer 框架在初始化过程中会到特定的目录下寻找插件。用户可以通过环境变量 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 项目本身提供的一个基础的插件,其代码位于 gst-plugins-base 项目中,这个项目的具体位置为 https://github.com/GStreamer/gst-plugins-base。对于 Ubuntu Linux,也可以通过如下命令安装编译好的二进制:

$ 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
复制代码

这个命令一次性安装 GStreamer 的各种开发库,其中 gstreamer1.0-plugins-base 里会包含 "playbin" 这个插件,这个插件一般会安装在 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstplayback.so

有了这些之后,helloworld 即可以正常运行起来。

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

分类:
前端
分类:
前端