原文:Basic tutorial 2: GStreamer concepts
目标
前一个教程展示了怎么自动构建一个pipeline。现在我们通过实例化每一个element,然后把他们链接到一起来手动创建一个pipeline。这个过程中,我们会学到:
- 什么是element,怎么创建一个element
- 怎么连接elements
- 怎么定制一个element的行为
- 如何观察总线(bus)的错误情况,并从消息(message)中解析信息
手动的“Hello World”
#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;
}
说明
element是GStreamer中的基础构建块。他们处理从source element(数据生成者)流向sink element(数据消费者)的数据流,中间经过filter element。
Element创建
我们跳过GStreamer的初始化,因为和前一个教程一致。
/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
如代码所示,新的element可以使用gst_element_factory_make()创建。第一个参数是创建的element类型(基础教程14展示了一些通用类型,基础教程10展示了如何过去可用的类型列表)。第二个参数是我们给这个实例起的名字。如果没有保留指针,对element命名会很有用(而且对调试输出也很有意义)。如果名字传递一个NULL,GStreamer也会提供一个特定的名字。
该示例创建了两个element:videotestsrc和autovideosink。没有filter element。因此,pipeline如下图所示:
videotestsrc是一个source element(数据生产者),它创建了一个测试的视频模式。这个element调试有用,但不会用在实际应用中。
autovideosink是一个sink element(数据消费者),它在窗口上显示收到的图像。根据操作系统的不同,存在一些不同能力的视频sink。autovideosink自动选择和实例化最佳的sink,你不需要关注细节,并且你的代码是独立于平台的。
Pipeline创建
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");
所有的element使用前都必须加到一个pipeline中,它负责时钟和消息传递功能。gst_pipeline_new()可以创建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;
}
bin是一种特殊的element,它包含其他的element,pipeline是一种特殊的bin。因此bin的所有方法也可以用在pipeline上。
在本例中,我们调用gst_bin_add_many()把element添加到pipeline中(注意转换,GST_BIN(pipeline))。这个方法接收待添加的element,以NULL结束。单个element可以使用gst_bin_add()添加。
但是这些elements还没有彼此链接。我们需要调用gst_element_link()。它的第一个参数是源,第二个参数是目的。顺序很重要,因此链接必须根据数据流建立(从source element流向sink element)。记住只有一个bin中的element才能链接到一起,所以链接前记得把element添加到pipeline中。
Properties
GStreamer中的element都是一种GObject,它提供了property功能。
绝大多数element拥有可定制化的property:通过改变已命名的属性改变element的行为(可写property)或者查询element内部的状态(可读property)。
property的读写通过g_object_get()和g_object_set()。
g_object_set()接受一个以NULL结尾的属性名、属性值成对的列表。依次可改变多个property。
这就是为什么property处理方法都包含g_前缀。
回到上面的例子中:
/* Modify the source's properties */
g_object_set (source, "pattern", 0, NULL);
上面这行代码修改了videotestsrc的“pattern”属性。它控制了输出的类型。尝试不同的值试试。
一个element暴露出的所有属性的name和value可以通过gst-inspect-1.0 tool查询(基础教程10描述)或者在这个element的文档中。
错误检查
到这里,我们已经构建了整个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()但是这次检查了它的返回值错误情况。改变状态是一个复杂的过程,基础教程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()会在发生错误或者EOS的情况下返回,因此我们需要检查是哪一个情况,然后在屏幕上打印相应的信息(你的应用可能会承载更多的操作)。
GstMessage是一个非常通用的结构,它几乎可以传递任何类型的信息。幸运的是,GStreamer为每种类型的消息提供了一系列解析函数。
在本例中,一旦我们知道消息包含错误(通过使用GST_MESSAGE_TYPE()宏),我们就可以使用gst_message_parse_error(),它返回一个GLib GError错误结构和一个用于调试的字符串。检查代码,看看这些是如何使用和释放的。
GStreamer总线
现在,有必要更正式地介绍一下GStreamer总线。它是负责将element生成的GstMessages按顺序传递给应用程序并传递给应用程序线程的对象。最后一点很重要,因为实际的媒体流是在应用程序以外的另一个线程中完成的。
可以使用gst_bus_timed_pop_filtered()方法同步地从总线提取消息,也可以使用信号异步地从总线提取消息(将在下一个教程中展示)。您的应用程序应该始终关注总线,以便收到错误和其他播放相关问题的通知。
剩下的代码是清理流程,与基础教程1中相同。