Goal
在上一个教程,我们已经知道了怎么自动创建一个pipline。现在我们将要手动实例化一些元素,并且把它们连接到一起,形成一个pipeline,我们将要学到如下
- 什么是gstream 的元素并且怎么创建一个元素
- 怎么将创建的元素连接起来
- 怎么定制元素的属性
- 怎么监视总线(bus)的错误信息,并提取出gstream messages
Manual Hello World
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 */
//创建一个元素,第一个参数是gstream中允许的插件,第二个参数是我们给他取得别名,如果为null,则自动生成一个别名
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
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;
}
编译
gcc basic-tutorial-2.c -o basic-tutorial-2 `pkg-config --cflags --libs gstreamer-1.0`
**`pkg-config --cflags --libs gstreamer-1.0`**为自动推到 gstreamer-1.0的依赖库和依赖头文件,就不需要我们添加-L -I参数了
Walkthrough
上面的代码我们创建了两个元素,并将它们连接成一个管道,那他们是什么样子的呢,如下图
中间的filter元素我们没有创建,这里做个展示,数据从左流向右侧,从source产生,到sink消费,中间流经filter
创建元素
/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
这里,我们用 gst_element_factory_make 创建了两个元素(elements),第一个参数是gstream的允许的插件,第二个参数是我们取的插件别名,你可以设置为null,如果这样,他会自动创建一个别名
videotestsrc 是一个源元素(生产数据),这个插件产生一个测试视频图案,这个插件主要在调试和教程中使用,在应用中没有用
autovideosink 是一个消费元素(消费数据),他将接收到的图片信息显示到窗口。这里存在多个视频消费元素,依赖于系统,各个元素拥有不同的能力。autovideosink 会实例化一个最好的,因此你不用担心具体细节
关于更多插件:你可以在终端执行
gst-inspect-1.0 #不跟参数,列举所有可以使用的插件
gst-inspect-1.0 [要查看的插件] # 后面跟要查看的插件名称,列举该插件的详细信息
创建管道
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");
所有元素需要包含在一个管道中,因为管道负责同步时钟和消息传递。我们使用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;
}
一个管道(pipeline)是一个特殊的容器(bin),他是一个包含其他元素的元素。我们可以用操作bin的方法来操作pipeline,因此我们用gst_bin_add_many将元素添加到管道中,使用NULL结尾。单独添加一个元素使用gst_bin_add()
我们已经将元素添加到管道中了,但是他们还不能工作,我们还得使用gst_element_link 将元素连接起来。这样才能形成数据流。但要注意的是在连接前,你要确保已经添加到管道中
设置属性
/* Modify the source's properties */
g_object_set (source, "pattern", 0, NULL);
gstream的元素有很多属性,我们可以设置这些属性,
g_object_get() 获取属性,g_object_set() 设置属性
Error checking
到这里,我们已经建立了一个完整的管道,接下来我们将启动这个管道
/* 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);
}
gst_bus_timed_pop_filtered() 等待管道结束和返回一个GstMessage 消息。我们调用这个函数,当这个管道遇到一个错误或者结束符(EOS)就会返回
GstMessage 是一个很有用的结构体,他向我们展示了各种各样的信息,当然,gstream提供了一些类的函数来解析这些消息,gst_message_parse_error()即使来解析错误的
The GStreamer bus
到这里,有必要正式介绍一下gstream的 bus。bus负责将各个元素产生的应用消息传递给上层应用和应用线程。值得注意的是,很多时候,流处理和上层应用是在不同的线程中完成的。