GStreamer 入门 - Hello,World
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,各种编解码库,第三方的编解码包
元数据:它们之间有着公共映射的本地容器格式
视频:各种颜色空间,支持渐进式和交错视频
音频:具有各种位深度的整型和浮点型音频数据和多通道配置
大量的开发工具
gst-launch 命令行工具可用于快速的原型和测试,与 ecasound 类似
大量 文档,包括部分完成的 手册 和 插件编写者指南
大量可选的测试程序和每个模块中的示例代码
可通过 各种各样的编程语言 访问 GStreamer API
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复制代码
仓库中包含了一些值得注意的脚本和目录:
meson.build
是顶层的构建定义,它递归地配置所有依赖。它也定义了一些辅助命令,使你可以有一个未安装的开发环境或简单地更新 GStreamer 模块的 git 仓库。subprojects/
是包含了 GStreamer 模块和一系列依赖的目录。
通过执行如下命令配置一个模块(或在 gst-build 下一次配置多个):
meson <build_directory>复制代码
其中 build_directory
是所有的构建指令和输出将放置的位置(这也被称为 “输出目录” 构建)。如果目录还没有创建,则它将在此时创建。注意调用 meson
不需要任何命令参数其实是隐式地调用了 meson setup
命令(比如执行一个工程的初始化配置)。
就 build_directory
的位置而言只有一个限制:它不能与源码目录(比如你下载你的模块的目录)相同。尽管它可以位于目录的外面或下面/里面。
一旦 meson 配置完成,你可以:
进入特定的构建目录并运行 ninja:
cd <build_directory> ninja复制代码
或不要在每次想要执行
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 的概念和操作了。
作者:hanpfei
链接:https://juejin.cn/post/7022079394126675976