元心科技分享:GStreamer 初探 (二)

GStreamer 初探 (二)

GStreamer 初探 (二)

又一个多媒体程序感受GStreamer的概念

basic-tutorial-2.c

#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;
}

通过 gcc basic-tutorial-2.c -o basic-tutorial-2 pkg-config --cflags --libs gstreamer-1.0 编译代码进行测试

element (组件) 是 Gstreamer 基本的构造单元,数据从 source 组件 (数据的生产者),通过 filter 组件,流向 sink 组件(数据的消费者),都伴随着 element 对数据的处理,见简图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DqRWVZQo-1599807427795)(https://gstreamer.freedesktop.org/documentation/tutorials/basic/images/figure-1.png#alt=img)]

我们省略了对 Gstreamer 初始化的讲解,上一节内容已做了详述

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

新的 element (组件) 可以通过 gst_element_factory_make() 进行创建,该函数的第一个参数是我们所要创建的_element_ 的类型, 之后的教程里我们会展示一些 element 的常见类型,以及如何通过命令行获取所有可用类型的列表,那第二个参数是我们赋予由该组件类型所生成实例的名称,这个名称是可以使用_NULL_值来代替的,当然,当我们使用_NULL_值时,GStreamer会在内部为我们提供一个独一无二的名称,给所创建的组件命名的方便之处在于在之后的使用中你可以重新检索到它

gst_element_factory_make

这个例子中我们创建了两个 element 实例,一个 videotestsrc 实例 和 一个 autovideosink 实例 ,我们对这两个_element_ 类别做简单讲解

videotestsrc

videotestsrc 是一个 source 组件,之前有提到 source 组件用于生产数据,videotestsrc 即是用来生成一些测试视频图案的,很明显这个小玩意儿对我们测试音频数据是很有帮助的,在实际项目应用上倒是很少

autovideosink

同样的,autovideosink 是一个 sink 组件(消耗数据), 将它收到的图像显示到窗口上,根据操作系统的不同,audovideosink 组件会选择具有不同 capsink 组件,实例化最为匹配的一个,它使我们不需要将关注点放在一些细节(cap )的选取上,一个可观的优点是使代码更独立于平台

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

通常来说,GStreamer 中的 element 都需要放置在 pipeline 中才能使用,这是因为 pipeline 会负责 element 运作中的一些时钟和消息传递的功能,我们是通过 gst_pipeline_new() 来创建 pipeline

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

pipelinebin 的特殊类型,适用于 bin 的方法同样适用 pipeline,这里提到 binbin 是用来包含其他组件的组件,在程序中,我们调用 gst_bin_add_many 来向 pipeline 添加 element,当然注意其中类型的转化,该函数允许向 bin 添加多个 element,使用 NULL 作为这些 element 的结束,补充:单个的 element 是通过 gst_bin_add 来添加的

gst_bin_add_many

我们需要使用 gst_element_link 来连接添加在 pipeline 中的组件,需要注意组件的排列顺序,我们需要按照数据的流向(从 sourcesink)来排列组件的位置,这个顺序就是组件连接的顺序,两个组件之间,相对前面的是连接的源,相对后面的是连接的目标,注意:只有将 element 放置在同一个 bin 里面它们才能够被连接在一起,所以在尝试连接它们前先放置到同一个 pipeline

gst_element_link

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

多数的 element 都有一些可以自主设置的 properties (属性),其中的可写属性可以改变组件的行为,可读的属性可以用来查询对应组件的内部状态

我们可以通过 g_object_get 来获取组件的属性值,通过 g_object_set 来设置属性值

使用 g_object_set 可以一次性设置多个属性值, 以 NULL 作为结尾

为什么我们所见到的方法都含有 g_ 这样的前缀?这是因为GStreamer 是事实上是基于 GObject 实现的,有关GObject 的机制,我们有机会再做讨论

回到我们的程序中来,这段程序我们将 videotestsrc 的实例 source 中的 pattern 属性设置为 0,这控制了该组件所输出的测试视频的类型,我们可以改变不同的数值来做尝试

我们可以使用 gst-inspect-1.0 这一工具找到元素所有的暴露出来的属性以及这些属性可能的值,工具的使用之后的教程会做更为详细的介绍

gst-inspect-1.0 videotestsrc

到达这里,我们已经完成了整个_pipeline_ 的搭建和连接,类似的,我们将在剩下的部分设置 pipeline 的状态、获取消息,这次,我们将添加更多的错误检查进去

/* 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 来设置 pipelineGST_STATE_PLAYING 状态,但这次我们会检查它的返回值是否有错误,然后经过一个判断,出错会在命令行有输出,接着改变 pipeline 的状态,这是一个值得琢磨的过程,之后的教程会有提及

/* 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_ 我们在上一次的教程中已经提及,我们指定当遇到GST_CLOCK_TIME_NONE 或 GST_MESSAGE_ERROR 两种消息时返回,这里将其执行的返回值获取出来,判断获取到的是哪一个消息,然后在屏幕上输出不同的消息,实际应用中,我们往往会在判断返回值后做更多的处理

GstMessage 是描述 GStreamer 中消息的类,是一个很通用的结构,几乎可以传递任何类型的消息,GStreamer 同时为每种消息提供了一系列的解析功能,后面我们将一点点接触到这些

使用宏 GST_MESSAGE_TYPE() 可以获取到消息的类型,在这里我们会解析出程序发生的是我们指定的哪种类型的消息,如果是 GST_MESSAGE_ERROR 我们会进一步使用 gst_message_parse_error() 将消息转化为 GError 类型来进行处理调试,注意对使用结束的 GError 和 GstMessage 进行释放

这里我们对 GStreamer 的 bus 所负责工作做进一步介绍,它将 pipelineelement s所产生的 GstMessage 传递出来,我们需要注意很重要的一点:这些消息会按照顺序传递到应用程序所在的主线程,同时也就说明,实际媒体流处理是在主线程之外的另一个线程中进行的

我们使用 gst_bus_timed_pop_filtered() (或相近函数) 是同步的获取 bus 上的消息,而在有些情况中,我们会使用 signals 来异步的获取消息,这也就是我们接下来的教程中要尝试讲解的内容,最后强调一下,在我们的程序代码里要始终留意总线上的消息,及时获得有关播放的信息或者错误

清理的代码我们不再进行讲解

练习

尝试在 pipelinesourcesink 之间添加一个 filter ,推荐尝试 vertigotv

我们需要手动创建一个 vertigotv, 然后将它添加到 pipeline ,然后进行连接

取决于我们使用的平台和可用的插件,可能在尝试的过程中会遇到协商失败的错误(“negotiation”),这是由于 sink 不能理解 filter 所生产的数据,不需要理解,这部分内容同样会出现在之后的教程中,在这里,我们只需要在两者中间添加一个 videoconvert 组件

总结

这次教程我们进行了 pipeline 的手动组装,并尝试将 bus 上获取到的错误消息获取出来做进一步处理,下面是本次教程出现到的一些方法的概括

  1. 使用 gst_element_factory_make() 手动创建 element
  2. 使用 gst_pipeline_new() 创建一个空的 pipeline
  3. 使用 gst_bin_add_many()pipeline 中添加 element
  4. 使用 gst_element_link() 连接 element

进度提示:原计划开设两部分专门介绍GStreamer基本概念的教程,到这里为止,我们完成了第一部分

参考

https://gstreamer.freedesktop.org/documentation/tutorials

FAQ

多个线程获取多次 bus ,在 bus 上分别获取消息会出现什么现象

(此篇文章翻译自官网,旨在分享技术理念,如有不当敬请指正)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值