摘要
在面对一个新的软件库时, 第一步通常实现一个 "hello world" 程序, 来了解库的用法. 对于 GStreamer, 我们可以实现一个极简的播放器, 来了解 GStreamer 的使用.
环境配置
为了快速掌握 Gstreamer 相关的知识, 我们优先选择 Ubuntu 作为我们的开发环境, 其他平台的开发会在后续文章单独介绍. 如果还没有 Ubuntu 虚拟机, 可以在 OSBoxes https://www.osboxes.org/ubuntu/ 中直接下载 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 作为我们的第一个应用:
- #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
作者: John.Leng
来源: https://www.cnblogs.com/xleng/p/11008239.html