基本教程2:GStreamer概念
1,目标
之前的教程展示了如何自动构建管道。现在我们将手动构建一条pipeline:初始化每一个element并将它们连接起来。在此过程中,我们将学习:
-
什么是GStreamer元素以及如何创建一个。
-
如何将elements相互连接。
-
如何自定义element的行为(属性)。
-
如何监视bus的错误条件并从GStreamer messages 中提取信息。
2,自制 Hello World
将此代码复制到名为basic-tutorial-2.c
文本文件中(或找到它 在您的GStreamer安装中)。
基础教程
#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 ((GstMainFunc) 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 gstreamer-1.0`
如果您需要帮助来运行此代码,请参阅您平台的运行教程部分:Linux、Mac OS X[2]或Windows。
本教程打开一个窗口并显示一个测试模式,没有音频
所需库:
gstreamer-1.0
3,工作流
这些 element 是GStreamer的基本构造单位。它们处理从source element(数据生产者)通过filter element流向sink element(数据消费者)的数据。
图1.示例管道
3.1,创建 element
我们将跳过GStreamer初始化,因为它与之前的教程相同:
/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
如本代码所示,如何使用gst_element_factory_make()
新建一个element 。
第一个参数是要创建的element名,即插件名(基本教程14:方便的元素显示了一个 一些常见类型和基本教程10:GStreamer工具展示了如何 获取所有可用类型的列表)。
第二个参数是给这个特殊实例起的名称,在同一条pipeline中,必须是唯一的名称。如果你没有保留指针,给 element名可以方便你在以后检索它们(并且可以得到更有意义的调试输出)。但是,假如你传递NULL
作为实例名,GStreamer也会自动为它初始化一个唯一的名称。
3.2,创建 pipeline
对于本教程,我们创建两个元素:videotestsrc 和 autovideosink。中间没有filter element,所以整个pipeline看起来就如下图:
图2.本教程中构建的管道
Videotestsrc是一个按照制定pattern
生成测试视频source element(它将生产数据),这个插件在测试和教程中很好用,但通常并不会出现在真实的应用中。
autovideosink是一个在窗口中播放它接收到的图像的sink element(它将消费数据),GStreamer包含有很多视频sink element,具体取决于操作系统,它们能够处理不同的图像格式,autovideosink
将自动选择其中一个并实例话化,所以用户不需要担心平台兼容性问题。
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");
gst_pipeline_new()创建 pipeline,GStreamer中的所有元素在使用之前通常必须包含在一条pipeline 中,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;
}
一条pipeline也是一个特殊的 bin,它是用于 包含其他elements。因此,适用于bin的所有方法也适用于pipeline。
在我们的例子中,我们调用gst_bin_add_many()来将elements添加到pipeline中(注意映射)。此函数接受一个要被添加到pipeline中的element列表,因为不确定列表长度所以需要以NULL
结尾。添加单个element可以使gst_bin_add()。
然而,这些元素还没有相互联系。为此, 我们需要使用gst_element_link()。它的第一个参数是source, 第二个是destination。顺序很重要,因为链接必须按照数据流建立(即,从source elemnt到sink element)。请记住,只有 element 驻留在同一个bin中可以链接在一起,所以记得在之前将它们添加到pipeline中试图链接它们!
3.3,属性
GStreamer元素都是一种特殊的GObject,它是提供属性设施的实体。
大多数GStreamer元素都具有可自定义的属性:可以修改的命名属性以更改元素的行为(可写属性)或查询以了解元素的内部状态(可读属性)。
属性从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”属性, 它控制元素输出的测试视频类型。尝试不同的 值!
element 公开的所有属性的名称和可能值可以使用基本教程10:GStreamer工具或该元素的文档中描述的gst-inspect-1.0工具找到 (这里指的是videotestsrc)。
3.4,Error 监听
至此,我们已经构建和设置了整个管道,教程的其余部分与上一个非常相似,但我们将添加更多错误检查:
/* 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(),但是这次将检查状态改变的返回值。假如状态修改失败,将返回一个error值并进行相关退出处理,还有更多详细信息在基础教程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。我们要求gst_bus_timed_pop_filtered() 返回时GStreamer 遇到错误条件或EOS,所以我们需要检查发生了哪一个,并在屏幕上打印一条消息(您的应用程序将可能想采取更复杂的行动)。
GstMessage是一个非常通用的结构,它可以传递几乎任何类型的信息。幸运的是,GStreamer提供了一系列消息的解析函数。
在这种情况下,一旦我们知道消息包含错误(通过使用 GST_MESSAGE_TYPE() 宏),我们可以使用 gst_message_parse_error() 返回GLib的GError结构和对调试有用的字符串。检查代码以了解如何这些被使用并在之后释放。
3.5,GStreamer bus
在这一点上,值得更正式地介绍一下GStreamer bus 。GStreamer bus它是负责将element生成的GstMessages 按顺序交付给应用程序和应用程序线程(这点很重要,因此GStreamer实际是在其他的线程中处理媒体流)的对象。
消息可以通过 gst_bus_timed_pop_filtered()及其相关函数同步地从总线中提取,或者通过信号异步地提取(如在下一个教程中所示)。你的应用程序应该始终关注总线,以便被通知错误和其他与播放相关的问题。
其余的代码是清理序列,与基础教程1: Hello world!中的相同。
4,练习
如果您想练习,请尝试此练习:尝试在这条example pipeline的source和sink之间添加视频filter element,例如vertigotv
,你需要创建它,将它加入pipeline,并将它和pipeline中的其他元素连接起来。
根据您的平台和可用插件,您可能会得到一个 “negotiation”错误,因为sink element 正在生产什么(更多关于基本教程6:媒体格式和Pad功能的协商)。在这种情况下你需要在filter 之后添加videoconvert
(即构建一个4个元素的管道。更多关于 videoconvert在基本教程14:Handy elements)。
5,总结
本教程展示了:
-
如何使用gst_element_factory_make() 创建element
-
如何使用gst_pipeline_new
()
创建一个空的pipeline。 -
如何使用gst_bin_add_many
()
向pipeline中添加element。 -
如何使用gst_element_link
()
连接element。