一、目标
在上一篇文章中,我们介绍了如何使用gst_parse_launch
快速创建一个pipeline。本章中我们将学习播放一个test video的流程:
- 什么是GStreamer Element,以及如何创建。
- 如何连接这些元素。
- 如何自定义元素的属性。
- 如何监视总线的错误情况,以及如何从GStreamer message中提取出其信息。
二、代码
int BT02_ManualHelloWorld(int argc, char** argv)
{
GstElement *pipeline, *source, *sink;
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret;
int retval = 0;
/* 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("No Element Created.\n");
retval = -1;
goto out_return;
}
/* Build the pipeline */
gst_bin_add_many(GST_BIN(pipeline), source, sink, NULL);
if (gst_element_link(source, sink) != TRUE)
{
g_printerr("No Element Link.\n");
retval = -2;
goto out_unref;
}
/* 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");
retval = -3;
goto out_unref;
}
/* 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:
g_printerr("Unexpected message received.\n");
break;
}
gst_object_unref(msg);
}
gst_object_unref(bus);
gst_element_set_state(pipeline, GST_STATE_NULL);
out_unref:
gst_object_unref(pipeline);
out_return:
return retval;
}
三、解释
source -> filter -> sink
Elements是GStreamer最基本的构造块。它们处理从source(data producers)藉由filter到sink(data consumers)的数据流。
3.1 创建元素
source = gst_element_factory_make("videotestsrc", "source");
sink = gst_element_factory_make("autovideosink", "sink");
通过上述代码我们已知,元素可被gst_element_factory_make()
创建。其第一个参数,指创建的元素的类型(参考BT14,和BT10)。第二个参数是该Element实例化的名字。如果没有保留对应元素的指针,那么使用命名将有助于debug一类的管理。如果传递NULL作为参数,则GStreamer将自动为其命名。
在本例中,我们创建了两个元素,videotestsrc
和autovideosink
。这里没有filter元素,因此整个管线看起来是这样的:
source(videotestsrc) -> sink(autovideosink)
- videotestsrc创建一个测试视频。该视频只在测试中使用,实际项目中并不会用到。
- autovideosink在窗口上显示它所接收到的图像。该命令自动选择适合的sink实例。
3.2 创建管线
所有的元素需要在管线中才能被使用,管线负责传递元素直接的信息和时钟。我们使用pipeline = gst_pipeline_new()
来创建管线。
管线是一种特殊类型的元素,它是一个存放其他元素的容器。在我们创建一条完整的管线时,我们需要先向其中添加元素(gst_bin_add),然后再连接它们(gst_element_link),但是需要注意,在连接时有些元素的pad并不会自动实现。在上面的例子中:
gst_bin_add_many(GST_BIN(pipeline), source, sink, NULL);
gst_element_link(source, sink) != TRUE
同时,连接需要注意其顺序。
3.3 特性
GStreamer元素都是特殊的GObject,它是提供property facilities的实体。大多数的元素都有可自定义的属性:可以修改以更改元素的行为(可写属性)或查询以了解元素的内部状态(可读属性)的命名属性。
属性可以通过g_object_get()
获取;通过g_object_set()
设置。g_object_set()
接受以NULL结尾的属性名、属性值对列表,因此可以一次更改多个属性。
g_object_set(source, "pattern", 0 , NULL);
这里通过设置videotestsrc
的pattern
属性,来改变测试视频的输出。
3.4 错误检查
此时我们已经完成了整个管线的建构,下面是一些错误检查。
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");
retval = -3;
goto out_unref;
}
在上一节教程中,我们同样试了了gst_element_set_state
。但是这次我们需要检查其返回值,以确定语句是否正常运行。关于“更改状态”的更多内容将在下一篇文章中讨论。
/* 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:
g_printerr("Unexpected message received.\n");
break;
}
gst_object_unref(msg);
}
gst_bus_timed_pop_filtered
等待执行结束并返回我们之前忽略的GstMessage。我们要求gst_bus_timed_pop_filtered
在遇到错误或者到达EOS(End of Stream)时返回一条GstMessage,因此我们检查这个返回值是错误还是EOS。
GstMessage是一种非常通用的结构,它几乎可以传递所有类型的消息。并且GStreamer为其提供了一系列解析函数。
- 我们首先可以通过
GST_MESSAGE_TYPE()
来解析消息的类型。 - 如果遇到错误,则可以使用
gst_message_parse_error
提取消息到GError
中。
3.5 Gst总线
在这一点上,值得更正式地介绍一下GStreamer总线。它是负责将元素生成的GstMessages按顺序交付给应用程序并交付给应用程序线程的对象。最后一点很重要,因为实际的媒体流是在应用程序之外的另一个线程中完成的。
可以使用gst_bus_timed_pop_filtered()及其同级从总线同步提取消息,或者使用信号异步提取消息(在下一教程中显示)。 您的应用程序应始终关注总线,以便收到有关错误和其他播放相关问题的通知。
gst_object_unref(bus);
gst_element_set_state(pipeline, GST_STATE_NULL);
out_unref:
gst_object_unref(pipeline);
out_return:
return retval;
余下的这部分代码则是清理。
四、总结
本篇文章描述了:
- 如何使用
gst_element_factory_make
来创建元素。 - 如何使用
gst_pipeline_new
来创建管线。 - 如何使用
gst_bin_add_many
来为管线添加元素。 - 如何使用
gst_element_link
来连接管线中元素。