系列文章目录



前言

在上一章中我们成功的搭建了 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

有两种方式运行它:

  1. 编译完成后使用命令行运行,例如
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 包含了三个元素分别是 simplevideomarksimplevideomarkdetectvideoanalyse;而 videotestsrc 它只包含一个元素 videotestsrc,插件和元素同名,当你使用

gst-inspect-1.0  videotestsrc

命令时它会优先输出元素的信息,当然也可以加上 --plugin 参数让它输出插件信息

gst-inspect-1.0 --plugin videotestsrc

每个插件其实对应的是一个动态库 so 文件。

1.5 总结

让我们总结下这块内容:

  1. GStreamer 中有很多插件集合,这些插件集合中包含多个插件
  2. 每个插件对应一个 so 动态库文件,每个插件包含一个或者多个元素
  3. gst-inspect-1.0 命令可以查看插件或者元素的信息

二、Basic tutorial 2: GStreamer concepts

让我们过一遍 Basic tutorial 2: GStreamer concepts中的内容。具体代码大家自己看,就不贴了。

在 GStreamer 中,元素是最基本的模块,大致可以分为这么几类:

  1. Source,负责生产数据。例如 videotestsrc 负责生成测试图像数据。
  2. Filter,负责处理数据,例如一些滤镜之类的。
  3. 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。

在教程中,我们创建了两个元素:videotestsrcautovideosink。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中。这里的sourcesink就是你要添加到pipeline(这里需要显式转换为GstBin*类型)中的元素。

gst_element_link的函数原型为:

gboolean gst_element_link (GstElement *element_1, GstElement *element_2);

它主要用来将两个元素链接起来。GStreamer中的数据流是从源元素流向目的元素的,所以这行代码是尝试将source元素链接到sink元素。链接成功返回TRUE,否则返回FALSE。

所以这段代码的流程为:

  • sourcesink添加到pipepline中
  • 尝试链接sourcesink,如果不能链接,则打印错误信息,并释放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;
  }

参考

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐