GStreamer Basic Tutorial 2 GStreamer Concepts

"GStreamer 基本教程2: GStreamer 概念"

Posted by Stephen on March 14, 2022

前言

本文是GStreamer学习笔记,也可以看成是对原文的意译。

这些教程描述了理解其余教程所需的GStreamer主题。

GStreamer教程:

基础教程 : GStreamer 介绍

基础教程 1: Hello world!

基础教程 2: GStreamer 概念

基础教程 3: 动态管道

基础教程 4: 时间管理

基础教程 5: GUI工具包集成

基础教程 6: 媒体格式和pad功能

基础教程 7: 多线程和Pad可用性

基础教程 8: 管道短路操作

基础教程 9: 媒体信息收集

基础教程 10: GStreamer工具

基础教程 11: 调试工具

基础教程 12: 流媒体

基础教程 13: 播放速度

基础教程 14: 有用的元素

基础教程 16: 特定平台元素

环境

系统环境

Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.4 LTS
Release:	18.04
Codename:	bionic
Linux version :       5.3.0-46-generic ( buildd@lcy01-amd64-013 ) 
Gcc version:         7.5.0  ( Ubuntu 7.5.0-3ubuntu1~18.04 )

软件信息

version : 	
     GStreamer-1.0

正文

1. 目标

上一个教程展示了如何自动构建管道。现在我们将通过实例化每个元素并将它们全部链接在一起来手动构建管道。在这个过程中,我们将学习:

  • 什么是GStreamer元素以及如何创建它
  • 如何将元素相互连接
  • 如何自定义元素的行为
  • 如何观察总线的错误情况并从GStreamer消息提取信息

2. 手动改代码

   #include <gst/gst.h>
   
   int main (int argc, char *argv[])
   {
     GstElement *pipeline, *source, *sink;
     GstBus *bus;
     GstMessage *msg;
     GstStateChangeReturn ret;
   
     /* Initialize GStreamer */
     gst_init (&argc, &argv);
   
     /* Create the elements */
     source = gst_element_factory_make ("videotestsrc", "source");
     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, sink, NULL);
     if (gst_element_link (source, sink) != TRUE) {
       g_printerr ("Elements could not be linked.\n");
       gst_object_unref (pipeline);
       return -1;
     }
   
     /* Modify the source's properties */
     g_object_set (source, "pattern", 0, NULL);
   
     /* Start playing */
     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;
     }
   
     /* 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);
   
     /* Parse message */
     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:
           /* We should not reach here because we only asked for ERRORs and EOS */
           g_printerr ("Unexpected message received.\n");
           break;
       }
       gst_message_unref (msg);
     }
   
     /* Free resources */
     gst_object_unref (bus);
     gst_element_set_state (pipeline, GST_STATE_NULL);
     gst_object_unref (pipeline);
     return 0;
   }

3. 演练

这些元素是 GStreamer 的基本构造块。当数据从源元素(数据生产者)流向下游的接收器元素(数据使用者)时,它们会处理数据,并通过筛选器元素。

3.1 元素创建

我们将跳过 GStreamer 初始化,因为它与上一个教程相同:

     /* Create the elements */
     source = gst_element_factory_make ("videotestsrc", "source");
     sink = gst_element_factory_make ("autovideosink", "sink");

从这段代码中可以看出,可以使用gst_element_factory_make () 创建新元素。。第一个参数是要创建的元素的类型(基础教程 14: 有用的元素展示了几种常见类型,基础教程 10: GStreamer工具展示了如何获取所有可用类型的列表)。第二个参数是我们想给这个特定实例起的名字。如果您没有保留指针(以及更有意义的调试输出),命名您的元素对于稍后检索它们很有用。但是,如果您通过None作为名称,GStreamer 将为您提供一个唯一的名称。

在本教程中,我们创建了两个元素:videotestsrcautovideosink。没有过滤元件。因此,管道将如下所示:

videotestsrc是一个源元素(它产生数据),它创建一个测试视频模式。此元素对于调试目的(和教程)很有用,并且通常不会在实际应用程序中找到。

autovideosink是一个接收器元素(它消耗数据),它在窗口上显示它接收到的图像。存在多个视频接收器,具体取决于操作系统,具有不同的功能范围。 autovideosink 会自动选择并实例化最好的,因此您不必担心细节,而且您的代码更独立于平台。

3.2 管道创建

     /* Create the empty pipeline */
     pipeline = gst_pipeline_new ("test-pipeline");

GStreamer 中的所有元素通常必须包含在管道中才能使用,因为它负责一些时钟和消息传递功能。我们使用gst_pipeline_new () 创建管道。

     /* 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;
     }
   

管道是一种特殊类型的bin,它是用于包含其他元素的元素。因此,适用于 bin 的所有方法也适用于管道。

在我们的例子中,我们调用gst_bin_add_many () 将元素添加到管道中(注意顺序)。此函数接受要添加的元素列表,以NULL结尾。可以使用gst_bin_add () 添加单个元素。

然而,这些元素还没有相互关联。为此,我们需要使用gst_element_link ()。它的第一个参数是源,第二个参数是目标。顺序很重要,因为必须按照数据流(即从源元素到汇元素)建立链接。请记住,只有位于同一个 bin 中的元素可以链接在一起,因此请记住在尝试链接它们之前将它们添加到管道中!

3.3 属性

GStreamer 元素都是一种特殊的GObject ,它是提供属性工具的实体。

大多数 GStreamer 元素具有可自定义的属性:可以修改命名属性以更改元素的行为(可写属性)或查询以了解元素的内部状态(可读属性)。

这是python版本

为了理解如何获取和设置属性,让我们假设我们有一个source带有属性的 Gst 元素pattern

可以通过以下任一方式获取属性的当前状态:

  1. props将属性作为元素属性的属性访问。例如:在屏幕上打印_ = source.props.pattern
  2. 使用get_property元素的方法。前任:_ = source.get_property("pattern")

属性可以通过以下三种方法之一设置:

  1. 将属性设置为属性的props属性。例如:或等效source.props.pattern = 1source.props.pattern="snow"
  2. 使用set_property元素的方法。例如:或等效source.set_property("pattern", 1)source.set_property("pattern", "snow")
  3. 使用方法。此模式还允许您传递 Gst Caps 和其他结构。例如:,或等效地,Gst.util_set_object_arg()Gst.util_set_object_arg(source, "pattern", "snow")Gst.util_set_object_arg(source, "pattern", 1)

注意:在设置属性的所有三种方法中,如果将字符串作为要设置的值传递,它必须是标志或值的序列化版本(使用Gst.value_serialize ())

# Modify the source's properties
source.props.pattern = 0
# Can alternatively be done using `source.set_property("pattern",0)`
# or using `Gst.util_set_object_arg(source, "pattern", 0)`

使用g_object_get ()读取属性并使用g_object_set () 写入。

g_object_set () 接受以NULL结尾的属性名称、属性值对列表,因此可以一次性更改多个属性。

这就是属性处理方法具有g_前缀的原因。

回到上面例子中的内容,

     /* Modify the source's properties */
     g_object_set (source, "pattern", 0, NULL);

上面的代码行改变了videotestsrc的“pattern”属性,它控制元素输出的测试视频的类型。尝试不同的值!

可以使用基础教程 10: GStreamer工具中描述的 gst-inspect-1.0 工具或在该元素的文档中找到元素公开的所有属性的名称和可能值(此处以 videotestsrc 为例)。

3.4 错误检查

至此,我们已经构建并设置了整个管道,本教程的其余部分与上一个非常相似,但我们将添加更多错误检查:

     /* Start playing */
     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 (),但这次我们检查它的返回值是否有错误。更改状态是一个微妙的过程,基础教程 3: 动态管道中提供了更多细节。

    /* 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);
   
     /* Parse message */
     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:
           /* We should not reach here because we only asked for ERRORs and EOS */
           g_printerr ("Unexpected message received.\n");
           break;
       }
       gst_message_unref (msg);
     }

gst_bus_timed_pop_filtered () 等待执行结束并返回一个我们之前忽略的GstMessage 。当 GStreamer 遇到错误条件或EOS时,我们要求gst_bus_timed_pop_filtered () 返回,因此我们需要检查发生了哪一个,并在屏幕上打印一条消息(您的应用程序可能需要执行更复杂的操作)。

GstMessage是一种非常通用的结构,几乎可以传递任何类型的信息。幸运的是,GStreamer 为每种消息提供了一系列解析函数。

在这种情况下,一旦我们知道消息包含错误(通过使用 GST_MESSAGE_TYPE () 宏),我们可以使用 gst_message_parse_error () 返回 GLib GError错误结构和对调试有用的字符串。检查代码以了解它们是如何使用和释放的。

3.5 GStreamer总线

在这一点上,更正式地介绍一下 GStreamer 总线是值得的。它是负责将元素生成的GstMessage按顺序和应用程序线程传递给应用程序的对象。最后一点很重要,因为实际的媒体流是在应用程序之外的另一个线程中完成的。

可以使用gst_bus_timed_pop_filtered ()及其兄弟同步从总线中提取消息,或者使用信号异步提取消息(在下一个教程中显示)。您的应用程序应始终关注总线,以便收到错误和其他与播放相关的问题的通知。

其余代码为清理序列,与基础教程 1: Hello world!.

4. 练习

如果你想练习,试试这个练习:在这个管道的源和接收器之间添加一个视频过滤器元素。使用 vertigotv获得不错的效果。您将需要创建它,将其添加到管道中,并将其与其他元素链接。

根据您的平台和可用插件,您可能会收到“协商”错误,因为接收器不了解过滤器正在生成的内容(更多关于协商的信息,请参阅基础教程 6: 媒体格式和pad功能)。在这种情况下,尝试在过滤器之后添加一个名为videoconvert的元素(即构建一个由 4 个元素组成的管道。更多关于 videoconvert的内容请参见基础教程 14: 有用的元素)。

5. 总结

本教程学习了:

  • 如何使用gst_element_factory_make()创建元素
  • 如何使用gst_pipeline_new()创建一个空管道
  • 如何使用gst_bin_add_many将元素添加到管道
  • 如何使用gst_element_link()将元素相互连接

后记