GStreamer 简明教程(二):基本概念介绍,Element 和 Pipeline
在上一章中我们成功的搭建了 GStreamer 的调试环境,并运行了 Hello World 程序。本章我们将介绍 GStreamer 中的基本概念,包括 Element 和 Pipeline。另外,还介绍使用 gst-inspect-1.0 程序来查看插件的基本信息。GStreamer 是基于插件的架构,它处处离不开插件,你几乎所有功能都是被封装在插件之中。因此如何查询插件信息是我们需要掌握的。
系列文章目录
文章目录
前言
在上一章中我们成功的搭建了 GStreamer 的调试环境,并运行了 Hello World 程序。本章我们将介绍 GStreamer 中的基本概念,包括 Element 和 Pipeline。另外,还介绍使用 gst-inspect-1.0 程序来查看插件的基本信息。
一、查看插件信息
1.1 gst-inspect 介绍
GStreamer 是基于插件的架构,它处处离不开插件,你几乎所有功能都是被封装在插件之中。因此如何查询插件信息是我们需要掌握的。
gst-inspect 是 GStreamer 框架提供的一个命令行工具,用于查询和观察 GStreamer 插件或元素的信息。通过使用 gst-inspect,用户可以检查一个 GStreamer 插件提供的元素类型,每个元素的属性,它们的源码位置等多种信息。
基础使用如下:
gst-inspect-1.0 [插件名|[元素名]
,这将返回具体插件的详细信息。
如果想要查找所有可用的插件和元素,可以运行 gst-inspect-1.0
命令。
执行 gst-inspect-1.0 [元素名]
可以查看某个具体元素的详细信息,包括它的功能描述,底层库的位置,已实现的接口,可用的参数以及参数的默认值等。
1.2 源码中运行 gst-inspect
我们已经搭建了源码环境,因此可以直接在源码环境中运行它,但注意调试环境变量的设置,关于调试环境的变量请参考 GStreamer 源码编译,在 Clion 下搭建调试环境。
在本人机器上,我将环境变量保存在一个 env.sh
文件中,gst-inspect 在工程编译完成后存放在
/Users/user/Documents/develop/gstreamer/buildDir/subprojects/gstreamer/tools/gst-inspect-1.0
有两种方式运行它:
- 编译完成后使用命令行运行,例如
source /Users/user/Documents/develop/gstreamer/env.sh
./gst-inspect-1.0 videotestsrc
2 在 Clion 上直接运行。设置好环境变量和相应参数即可
1.3 理解插件的基本信息
以 videosignal
这个插件为例,使用 gst-inspect 进行查看可以得到这个插件信息如下:
Plugin Details:
Name videosignal
Description Various video signal analysers
Filename /Users/user/Documents/develop/gstreamer/buildDir/subprojects/gst-plugins-bad/gst/videosignal/libgstvideosignal.dylib
Version 1.25.0.1
License LGPL
Source module gst-plugins-bad
Documentation https://gstreamer.freedesktop.org/documentation/videosignal/
Binary package GStreamer Bad Plug-ins git
Origin URL Unknown package origin
simplevideomark: Video marker
simplevideomarkdetect: Video detecter
videoanalyse: Video analyser
3 features:
+-- 3 elements
这是一个叫做 “videosignal” 的 GStreamer 插件的详细信息:
-
插件名称(Name):videosignal。这是插件的名字,是唯一的。
-
插件描述(Description):Various video signal analysers。这是对插件功能的简单描述,即该插件包含了不同的视频信号分析工具。
-
包含文件(Filename):/Users/user/Documents/develop/gstreamer/buildDir/subprojects/gst-plugins-bad/gst/videosignal/libgstvideosignal.dylib。这是插件库的具体位置。
-
版本(Version):1.25.0.1。这是插件的版本。
-
许可证(License):LGPL。这是插件的开源许可证。
-
源代码所在模块(Source module):gst-plugins-bad。这是包含源代码的模块名称。
-
文档链接(Documentation):https://gstreamer.freedesktop.org/documentation/videosignal/。这是插件的详细文档链接。
-
二进制包(Binary package):GStreamer Bad Plug-ins git。这是插件对应的二进制包名。
-
原始 URL(Origin URL):Unknown package origin。这是插件源代码的原始 URL,但在这里没有给出具体的 URL。
-
功能特性(Features):简单的视频标记器(simplevideomark)、视频检测器(simplevideomarkdetect)、视频分析器(videoanalyse)。插件的功能清单,这个插件主要提供了三个特性或者元素,它们分别是视频标记器、视频检测器和视频分析器。
1.4 插件与元素
GStreamer 框架的插件通常被分为几个集合或者类别:“base”, “good”, “ugly”, “bad” 和 “libav”。
“base” 是基础的插件,提供框架的核心功能,所有人都需要它们。
“good” 是高质量的插件,它们对于许多应用来说是有用的,而且它们的质量和稳定性得到了 GStreamer 开发团队的保证。
“ugly” 是功能有用但是由于许可证问题或者质量问题没有被包含在 “good” 类别中的插件。
“bad” 插件集合包含了那些质量不够好,或者因为没有足够的测试和文档,而无法被分类为 good 或 ugly 的插件。但是请注意,“bad” 只是暗示它们可能不稳定或者有缺陷,并不意味着它们不可用。许多 “bad” 类别的插件其实功能强大,只是还没有达到 GStreamer 团队的标准。
例如 videosignal
这个插件它是来源于 gst-plugins-bad 时,用户应该意识到这些插件可能具有一些缺陷或者是实验性的,需要谨慎使用。
一个插件包含一个或者多个元素,例如 videosignal
包含了三个元素分别是 simplevideomark
、simplevideomarkdetect
和 videoanalyse
;而 videotestsrc
它只包含一个元素 videotestsrc
,插件和元素同名,当你使用
gst-inspect-1.0 videotestsrc
命令时它会优先输出元素的信息,当然也可以加上 --plugin 参数让它输出插件信息
gst-inspect-1.0 --plugin videotestsrc
每个插件其实对应的是一个动态库 so 文件。
1.5 总结
让我们总结下这块内容:
- GStreamer 中有很多插件集合,这些插件集合中包含多个插件
- 每个插件对应一个 so 动态库文件,每个插件包含一个或者多个元素
gst-inspect-1.0
命令可以查看插件或者元素的信息
二、Basic tutorial 2: GStreamer concepts
让我们过一遍 Basic tutorial 2: GStreamer concepts中的内容。具体代码大家自己看,就不贴了。
在 GStreamer 中,元素是最基本的模块,大致可以分为这么几类:
- Source,负责生产数据。例如
videotestsrc
负责生成测试图像数据。 - Filter,负责处理数据,例如一些滤镜之类的。
- Sink,负责消费数据,例如将数据显示到屏幕上,或者保存到本地。
数据从 Source(数据生产者) 流向 Sink(数据消费者),过程中经过 FIlter 的处理。元素之间相互连接,组合一个 Pipeline。
2.1 创建元素
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
gst_element_factory_make
是一个 GStreamer 的函数,用于创建一个新的元素实例。
函数原型:
GstElement * gst_element_factory_make (const gchar * factoryname, const gchar * name);
参数:
-
factoryname
:元素工厂的名称,也就是你想要创建的 GStreamer 元素的类型名称,比如 “fakesrc”, “filesrc” 等。 -
name
:新创建的元素实例的名称。这个参数是可选的,如果传入 NULL 或者一个空字符串,GStreamer 会自动为新元素生成一个唯一的名称。
返回值:
- 这个函数返回一个指向新创建的 GStreamer 元素的指针。如果由于某种原因(比如指定的元素工厂名称不存在)无法创建元素,那么这个函数会返回 NULL。
在教程中,我们创建了两个元素:videotestsrc 和 autovideosink。videotestsrc 是 Source 类型的,因为你去查它的 pad 信息可以发现它只有一个 src 类型的 pad,而 autovideosink 是 Sink 类型的,因为它只有一个 sink 类型的 pad。这里的 pad 概念,我们后面在介绍,它是一个数据之间传输的接口。
“videotestsrc”是一个 Source 元素(它产生数据),用于创建测试视频模式。这个元素主要用于调试目的(和教学),并且通常不会在实际应用中找到。
“autovideosink”是一个 Sink 元素(它消费数据),它在窗口上显示接收到的图像。存在多种视频汇元素,取决于操作系统,具有不同程度的能力。autovideosink 会自动选择并实例化最好的一个,因此你无需关心具体细节,你的代码也更具有平台独立性。
因此在我们教程中,Pipeline 如下图:
2.2 创建 Pipeline
刚才我们只是创建了元素,并没有将它们进行关联起来。现在,我们创建一个 pipeline
pipeline = gst_pipeline_new ("test-pipeline");
gst_pipeline_new
是GStreamer库中的一个函数,其允许创建一个新的管道元素。
gst_pipeline_new
的函数原型为:
GstElement* gst_pipeline_new (const gchar *name);
该函数接收一个name
参数,该参数为管道元素的名字。该函数的返回值是一个新创建的管道元素,以GstElement*
类型的指针形式返回。
在GStreamer中,一个管道(pipeline)代表了一个数据流处理链。管道由多个处理元素(element)组成,每个元素都可以进行一种特定的数据处理任务,如解码、分析、合成等。各个元素之间通过pad进行数据的输入和输出。
通过gst_pipeline_new
创建的管道仍然是空的,需要通过添加并连接各种元素来完成数据的处理链。例如,播放一个音频文件可能需要先读取文件数据,然后解码,再进行播放,这就需要分别添加文件源元素(filesrc)、解码元素(decodebin)和音频播放元素(alsasink)到管道,并将它们连接起来。
接下来,我们将元素添加到 pipeline 上,并连接它们
/* Build the pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
if (gst_element_link (source, sink) != TRUE) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
这段代码的主要目的是构建一个GStreamer的pipeline,并将source元素和sink元素添加到pipeline中,然后尝试将这两个元素链接起来。
gst_bin_add_many
的函数原型为:
gboolean gst_bin_add_many (GstBin * bin, GstElement * element_1, ..., NULL);
它是一个变参函数,允许你添加多个元素到指定的bin中。这里的source
和sink
就是你要添加到pipeline(这里需要显式转换为GstBin*
类型)中的元素。
gst_element_link
的函数原型为:
gboolean gst_element_link (GstElement *element_1, GstElement *element_2);
它主要用来将两个元素链接起来。GStreamer中的数据流是从源元素流向目的元素的,所以这行代码是尝试将source
元素链接到sink
元素。链接成功返回TRUE,否则返回FALSE。
所以这段代码的流程为:
- 将
source
和sink
添加到pipepline中 - 尝试链接
source
和sink
,如果不能链接,则打印错误信息,并释放pipeline的引用,返回-1。
2.3 设置元素属性
GStreamer 中每种元素都是一个 GObject,而 GObject 提供了属性设置的能力。
GStreamer 元素都有或多或少的属性可以设置,以 videotestsrc 为例,通过 gst-inspect 命令来查看它的属性,例如下面是一部分属性:
Element Properties:
animation-mode : For pattern=ball, which counter defines the position of the ball.
flags: readable, writable
Enum "GstVideoTestSrcAnimationMode" Default: 0, "frames"
(0): frames - frame count
(1): wall-time - wall clock time
(2): running-time - running time
automatic-eos : Automatically EOS when the segment is done
flags: readable, writable
Boolean. Default: true
background-color : Background color to use (big-endian ARGB)
flags: readable, writable, controllable
Unsigned Integer. Range: 0 - 4294967295 Default: 4278190080
blocksize : Size in bytes to read per buffer (-1 = default)
flags: readable, writable
Unsigned Integer. Range: 0 - 4294967295 Default: 4096
pattern : Type of test pattern to generate
flags: readable, writable
Enum "GstVideoTestSrcPattern" Default: 0, "smpte"
(0): smpte - SMPTE 100% color bars
(1): snow - Random (television snow)
(2): black - 100% Black
(3): white - 100% White
(4): red - Red
(5): green - Green
(6): blue - Blue
(7): checkers-1 - Checkers 1px
(8): checkers-2 - Checkers 2px
(9): checkers-4 - Checkers 4px
(10): checkers-8 - Checkers 8px
(11): circular - Circular
(12): blink - Blink
(13): smpte75 - SMPTE 75% color bars
(14): zone-plate - Zone plate
(15): gamut - Gamut checkers
(16): chroma-zone-plate - Chroma zone plate
(17): solid-color - Solid color
(18): ball - Moving ball
(19): smpte100 - SMPTE 100% color bars
(20): bar - Bar
(21): pinwheel - Pinwheel
(22): spokes - Spokes
(23): gradient - Gradient
(24): colors - Colors
(25): smpte-rp-219 - SMPTE test pattern, RP 219 conformant
通过设置 pattern 这个属性让 videotestsrc 生成不同的画面,例如 pattern=0
生成是经典的条纹画面;pattern=1
则生成白噪声画面。
g_object_set (source, "pattern", 0, NULL);
2.4 错误检查
现在 Pipeline 建立好了,接下来就像前一章一样,修改 Pipeline 的状态,让它跑起来,但这次我们加上错误检查的逻辑
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Unable to set the pipeline to the playing state.\n");
gst_object_unref (pipeline);
return -1;
}
在这里,我们调用了gst_element_set_state()函数,但这次对其返回值进行了错误检查。
/* 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);
gst_bus_timed_pop_filtered() 会等待执行结束并返回一个 GstMessage。我们要求gst_bus_timed_pop_filtered()在GStreamer遇到错误条件或EOS时返回,因此我们需要检查到底发生了什么,并在屏幕上打印一条消息(你的应用程序可能会想采取更复杂的操作)。
/* 解析消息 */
if (msg != NULL) {
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
gst_message_parse_error (msg, &err, &debug_info);
g_printerr ("Error received from element %s: %s\n",
GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging information: %s\n",
debug_info ? debug_info : "none");
g_clear_error (&err);
g_free (debug_info);
break;
case GST_MESSAGE_EOS:
g_print ("End-Of-Stream reached.\n");
break;
default:
/* 我们不应该到达这里,因为我们只要求ERROR和EOS */
g_printerr ("Unexpected message received.\n");
break;
}
gst_message_unref (msg);
}
GstMessage是一个非常通用的结构,可以传递几乎任何类型的信息。幸运的是,GStreamer为每种类型的消息提供了一系列解析函数。
在这个例子中,一旦我们知道消息包含一个错误(通过使用GST_MESSAGE_TYPE()宏),我们就可以使用gst_message_parse_error(),它返回一个GLib的GError错误结构和一个用于调试的字符串。你可以查看代码来看看这些是如何被使用和在之后被释放的。
2.5 GStreamer 的 Bus
GStreamer总线是一个非常重要的组件,它的任务是把元素生成的消息(GstMessages)按照顺序发送到应用程序线程。之所以强调线程,因为真正的媒体流传输是在另一个和应用程序不同的线程中进行的。
GstMessages可以通过调用 gst_bus_timed_pop_filtered() 函数以及其它相关函数来同步提取,也可以通过使用信号(这将在下一个教程中详细介绍)来异步提取。重要的是,你的应用程序总是需要监控总线,以便在发生错误和其他播放相关问题时得到通知。
2.6 代码清理
C 对比 C++,清理资源是比较麻烦和啰嗦的。在 GStreamer 中哪些接口返回的对象要清理,要调用哪个接口进行清理,函数的注释往往会没有写清楚。一种简单的方案就是去源码搜索,例如搜索 gst_element_get_bus
,我在 basesrc.c 中看到它使用了 gst_object_unref (bus);
进行清理;同理还有 gst_bus_timed_pop_filtered
,搜索后在 appsrc.c 中看到使用了 gst_message_unref (msg);
进行清理
2.7 练习
在 source 和 sink 之间添加一个 vertigotv filter,效果挺酷的,如下图
重要代码如下,完整代码参考 basic-tutorial-2-exercise.c
/* Create the elements */
source = gst_element_factory_make("videotestsrc", "source");
filter = gst_element_factory_make("vertigotv", "filter");
sink = gst_element_factory_make("autovideosink", "sink");
/* Create the empty pipeline */
pipeline = gst_pipeline_new("test-pipeline");
if (!pipeline || !source || !sink) {
g_printerr("Not all elements could be created.\n");
return -1;
}
/* Build the pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL);
if (gst_element_link_many(source, filter, sink, NULL) != TRUE) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
参考
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)