GStreamer Tutorial 2中文翻译
文章目录
前言
由于工作原因,用的GStreamer的图像解码库,所以记录GStreamer Tutorial 的中文翻译和个人的理解以便学习。若有不足请多指教。侵删。
Basic tutorial 2: GStreamer concepts
目的
前面的教程展示了如何自动建立一个管道。现在,我们将通过实例化每个元素并将它们连接在一起,手动建立一个管道。在这一过程中,我们将学习:
- 什么是GStreamer元素以及如何创建一个元素;
- 如何将元素相互连接;
- 如何定制一个元素的行为;
- 如何观察总线上的错误情况并从GStreamer消息中提取信息。
Hello world
将代码复制到basic-tutorial-2.c
中,或在你的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;
}
代码走读
这些元素是GStreamer的基本构造块。它们在数据从source
元素(数据生产者)流向sink
元素(数据消费者)的下游时,通过过滤元素进行处理。
创建元素
我们将跳过GStreamer的初始化,因为它与前面的教程相同。
/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
从这段代码中可以看出,新的元素可以用gst_element_factory_make()
来创建。第一个参数是要创建的元素的类型(基础教程14:方便的元素显示了一些常见的类型,基础教程10:GStreamer工具显示了如何获得所有可用类型的列表)。第二个参数是我们要给这个特定实例的名称。如果你没有保留一个指针,命名你的元素对以后检索它们是很有用的(也是为了更有意义的调试输出)。然而,如果你传递NULL
作为名字,GStreamer将为你提供一个独特的名字。
在本教程中,我们创建两个元素:一个videotestsrc
和一个autovideosink
。没有过滤器元素。因此,管道将看起来像下面这样。
videotestsrc
是一个源元素(它产生数据),它创建一个测试视频模式。这个元素对于调试目的(和教程)很有用,通常不会出现在真实的应用程序中。
autovideosink
是一个汇(sink
)元素(它消耗数据),在一个窗口上显示它收到的图像。有几个视频sink,取决于操作系统,具有不同的能力。autovideosink
自动选择和实例化最好的一个,所以你不必担心细节问题,你的代码更独立于平台。
管道创建
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");
GStreamer中的所有元素在使用前通常必须包含在一个管道中,因为它负责一些时钟和消息传递功能。我们用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;
}
管道是一种特殊类型的bin,它是用来容纳其他元素的元素。因此,所有适用于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_
前缀。
再来看看上面的例子中的内容:
/* Modify the source's properties */
g_object_set (source, "pattern", 0, NULL);
上面这行代码改变了videotestsrc
的 "pattern "属性,它控制了元素输出的测试视频的类型。试试不同的值吧!
一个元素暴露的所有属性的名称和可能的值,可以使用基础教程10:GStreamer工具中描述的gst-inspect-1.0工具,或者在该元素的文档中找到(这里是 videotestsrc)。
错误检测
在这一点上,我们已经建立和设置了整个管道,其余的教程与之前的教程非常相似,但我们要增加更多的错误检查。
/* 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()
在GStreamer遇到错误条件或EOS时返回,所以我们需要检查哪一个发生了,并在屏幕上打印一个消息(你的应用程序可能想进行更复杂的操作)。
GstMessage是一个非常通用的结构,几乎可以传递任何种类的信息。幸运的是,GStreamer为每种信息提供了一系列的解析函数。
在这种情况下,一旦我们知道消息包含一个错误(通过使用GST_MESSAGE_TYPE()宏),我们可以使用gst_message_parse_error()
,它返回一个GLib GError错误结构和一个对调试有用的字符串。检查一下代码,看看这些东西是如何被使用和事后释放的。
The GStreamer 总线
在这一点上,值得更正式地介绍一下GStreamer总线。它是负责将元素产生的GstMessages按顺序传递给应用程序的对象,并传递给应用程序线程。这最后一点很重要,因为实际的媒体流是在另一个线程中完成的,而不是在应用程序中。
消息可以用gst_bus_timed_pop_filtered()
及其同级别的方法同步提取,也可以用信号异步提取(在下一个教程中展示)。你的应用程序应该始终关注总线,以获得错误和其他与播放有关的问题的通知。
剩下的代码是清理工作,与基础教程1:Hello world!中的内容相同。
练习
如果你想练习,可以试试这个练习。在这个管道的源和汇之间添加一个视频过滤元件。使用vertigotv来获得良好的效果。你需要创建它,将它添加到管道中,并将它与其他元素连接起来。
根据你的平台和可用的插件,你可能会得到一个 "协商 "(一致性)的错误,因为水槽不理解过滤器产生的东西(更多关于一致的内容见基础教程6:媒体格式和转接能力)。在这种情况下,试着在过滤器后面添加一个叫做 videoconvert
的元素(也就是说,建立一个由4个元素组成的管道。更多关于videoconvert
的内容见基础教程14:方便的元素)。
PS
#include <gst/gst.h>
int
main (int argc, char *argv[])
{
GstElement *pipeline, *source, *sink, *vertigotv, *video_convert;
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");
vertigotv = gst_element_factory_make("vertigotv", "vertigotv");
video_convert = gst_element_factory_make("videoconvert", "video_convert");
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");
if (!pipeline || !source || !sink || !video_convert || !vertigotv) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
/* Build the pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, vertigotv, video_convert, sink, NULL);
if (gst_element_link_many (source, vertigotv, video_convert, sink, NULL) != 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;
}
和源代码相比,添加了两个GStreamer元素*vertigotv
,*video_convert
并实例化
vertigotv = gst_element_factory_make("vertigotv", "vertigotv");
video_convert = gst_element_factory_make("videoconvert", "video_convert");
然后将其加入pipeline
中,使用gst_element_link_many ()
函数将它们连接起来。
最后程序跑起来有一种眩晕的效果=。=||
结论
这个例程展示了:
- 如何使用
gst_element_factory_make()
创建元素; - 如何使用
gst_pipeline_new()
创建空管道; - 如何使用
gst_bin_add_many()
向管道添加元素;
-如何使用gst_element_link()
将元素相互链接。
这是专门介绍GStreamer基本概念的两个教程中的第一个。接下来是第二个。
请记住,在本页的附件中,您应该可以找到本教程的完整源代码以及构建本教程所需的任何附件文件。