目标
上一篇教程展示了如何自动构建管道。现在,我们将通过实例化每个元素并将它们链接在一起来手动构建管道。在这个过程中,我们会学到:
什么是GStreamer元素以及如何创建它。
如何将元素相互连接。
如何自定义元素的行为。
如何观察总线的错误条件并从GStreamer消息中提取信息。
手动Hello World
将这段代码复制到一个名为basic-tutorial-2.c的文本文件中(或在GStreamer安装中找到它)。
basic-tutorial-2.c
#include <gst/gst.h>
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
int
tutorial_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;
}
int
main (int argc, char *argv[])
{
#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
return gst_macos_main (tutorial_main, argc, argv, NULL);
#else
return tutorial_main (argc, argv);
#endif
}
需要帮忙吗?
如果你需要帮助来编译这段代码,请参考构建教程部分的平台:Linux, Mac OS X或Windows,或在Linux上使用这个特定的命令:
GCC basic-tutorial-2 -c -o basic-tutorial-2 ' pkg-config——cflags——libs gstream -1.0 '
如果您需要帮助来运行此代码,请参考针对您的平台运行教程部分:Linux, Mac OS X[2]或Windows。
本教程打开一个窗口并显示一个测试模式,没有音频
所需的库:gstreamer-1.0
代码演练
元素是GStreamer的基本构造块。通过过滤器元素,它们处理从源元素(数据生产者)到接收器元素(数据消费者)的数据。
![](https://i-blog.csdnimg.cn/blog_migrate/4703d1b013a1232e6693cd083ef8ee1a.png)
图1. 管道示例
元素创建
我们将跳过GStreamer初始化,因为它和前面的教程一样:
/* Initialize GStreamer */
gst_init (&argc, &argv);
如这段代码所示,可以使用gst_element_factory_make()创建新元素。第一个参数是要创建的元素类型(基础教14:Handy elements展示了一些常见类型,基础教10:GStreamer tools展示了如何获取所有可用类型的列表)。第二个参数是我们想要赋予这个特定实例的名称。如果您没有保留指针(以及更有意义的调试输出),命名元素对于稍后检索它们很有用。但是,如果您为名称传递NULL,GStreamer将为您提供一个唯一的名称。
在本教程中,我们创建了两个元素:videotestsrc和autovideosink。由于没有过滤器元素,因此,管道看起来如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/526703196d51611054dc7acba86dc8f4.png)
图2. 本教程中构建的管道
videotestsrc是一个源元素(它生成数据),它创建一个测试视频模式。此元素用于调试(和教程),在实际应用程序中通常找不到。
autovideosink是一个接收器元素(它消耗数据),它在一个窗口中显示它接收到的图像。根据操作系统的不同,有几种不同功能的视频接收器。autovideosink自动选择并实例化最好的,因此您不必担心细节,并且您的代码更独立于平台。
管道创建
/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
GStreamer中的所有元素在使用之前通常都必须包含在管道中,因为它负责一些时钟和消息传递功能。我们使用gst_pipeline_new()创建管道。
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) {
管道是一种特殊类型的bin,它是用于包含其他元素的元素。因此,适用于容器的所有方法也适用于管道。
在本例中,我们调用gst_bin_add_many()将元素添加到管道中(注意转换)。这个函数接受一个要添加的元素列表,以NULL结尾。可以使用gst_bin_add()添加单个元素。
然而,这些元素之间还没有联系。为此,我们需要使用gst_element_link()。它的第一个参数是源元素,第二个参数是目标元素。顺序很重要,因为必须按照数据流(即从源元素到接收元素)建立链接。请记住,只有位于同一bin中的元素才能链接在一起,所以在尝试链接它们之前,请记住将它们添加到管道中!
属性
GStreamer元素都是一种特殊的GObject,它是提供属性功能的实体。
大多数GStreamer元素都有可定制的属性:可以修改命名属性以改变元素的行为(可写属性),或者查询属性以了解元素的内部状态(可读属性)。
属性从g_object_get()读取,用g_object_set()写入。
g_object_set()接受以null结束的属性名称、属性值对列表,因此可以一次性更改多个属性。
这就是属性处理方法有g_前缀的原因。
回到上面的例子,
gst_object_unref (pipeline);
return -1;
上面的代码行更改了videotestsrc的“pattern”属性,该属性控制元素输出的测试视频的类型。试试不同的值!
元素暴露的所有属性的名称和可能的值可以使用基础教程10:GStreamer工具中描述的gst-inspect-1.0工具找到,也可以在该元素的文档中找到(这里是videotestsrc)。
错误检查
在这一点上,我们已经构建和设置了整个管道,教程的其余部分与前面的非常相似,但我们将添加更多的错误检查:
/* 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) {
我们调用gst_element_set_state(),但是这次我们检查它的返回值是否有错误。改变状态是一个微妙的过程,在基础教程3:动态管道中给出了更多细节。
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");
gst_bus_timed_pop_filtered()等待执行结束并返回前面忽略的GstMessage。我们要求gst_bus_timed_pop_filtered()在GStreamer遇到错误条件或EOS时返回,因此我们需要检查发生了哪一种情况,并在屏幕上打印一条消息(您的应用程序可能希望执行更复杂的操作)。
GstMessage是一种非常通用的结构,几乎可以传递任何类型的信息。幸运的是,GStreamer为每种消息提供了一系列解析函数。
在这种情况下,一旦我们知道消息包含错误(通过使用GST_MESSAGE_TYPE()宏),我们就可以使用gst_message_parse_error(),它返回一个GLib GError错误结构和一个用于调试的字符串。检查代码,看看这些代码是如何被使用和释放的。
GStreamer总线
此时,有必要更正式地介绍一下GStreamer总线。这个对象负责将元素生成的gstmessage按顺序传递给应用程序并传递给应用程序线程。最后一点很重要,因为实际的媒体流是在应用程序之外的另一个线程中完成的。
可以使用gst_bus_timed_pop_filtered()及其兄弟姐妹同步地从总线提取消息,也可以使用信号异步地从总线提取消息(将在下一期教程中展示)。您的应用程序应该始终关注总线,以便在错误和其他与回放相关的问题时得到通知。
其余的代码是清理序列,这与基本教程1:Hello world!相同。
练习
如果您想练习一下,可以试试这个练习:在这个管道的源和接收器之间添加一个视频过滤器元素。使用vertigotv效果不错。您需要创建它,将它添加到管道中,并将它与其他元素链接起来。
取决于您的平台和可用的插件,您可能会得到一个“协商”错误,因为接收器不理解过滤器正在生成什么(关于协商的更多信息,请参阅基础教程6:媒体格式和Pad功能)。在本例中,尝试在过滤器之后添加一个名为videoconvert的元素(也就是说,构建一个包含4个元素的管道。关于videoconvert的更多信息,请参见基础教程14:方便的元素)。
结论
本教程展示了:
如何使用gst_element_factory_make()创建元素
如何使用gst_pipeline_new()创建一个空管道
如何使用gst_bin_add_many()向管道添加元素
如何使用gst_element_link()将元素相互链接
介绍GStreamer基本概念的两个教程中的第一个教程至此结束。下一个是第二个。
请记住,您应该可以在此页面上找到教程的完整源代码和构建教程所需的任何附件文件。
很高兴你能来这里,希望很快与你再见!