02-GStreamer概念
代码示例
#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;
}
编译:
gcc basic-tutorial-2.c -o basic-tutorial-2 `pkg-config --cflags --libs gstreamer-1.0`
工作流程
GStreamer的基本组成是elements,这些elements把数据从source经过filter传到sink。
/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
创建elements,新的element的建立可以使用gst_element_factory_make()。这个API的第一个参数是要创建的element的类型,第二个参数是我们想创建的element的名字,这个名字并非是必须的,但在调试中会非常有用,如果你传入的时NULL,那么GStreamer会自动创建一个名字。
我们创建了2个elements:videotestsrc和autovideosink.
vieotestsrc是一个source element(生产数据),会创建一个video模式。这个element常用在调试中,很少用于实际的应用。
autovideosink是一个sink element(消费数据),会在一个窗口显示收到的图像。在不同的操作系统中,会存在多个的video sink,autovideosink会自动选择一个最合适的,所以你不需要关心更多的细节,代码也会有很好的移植性。
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");
建立pipeline,因为要统一处理时钟和一些信息,GStreamer中的所有elements都必须在使用之前包含到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;
}
pipeline是一种特殊的bin,它可以用来包含其它的element,所有可以用于bin的方法对于pipeline也同样适用。
在这个例子中,我们调用了gst_bin_add_many()方法在pipeline中加入element。这个方法会接受一系列的element作为输入参数,最后由NULL来终止。增加单个element的方法是gst_bin_add()。
添加到pipeline中的element还需要进行连接。我们用gst_element_link()方法来把element连接起来,这个方法的第一个参数是源,第二个参数是目标,这个顺序不能搞错,因为这确定了数据的流向。注意只有在同一个bin里面的element才能连接起来,所以一定要把element在连接之前加入到pipeline中。
/* Modify the source's properties */
g_object_set (source, "pattern", 0, NULL);
绝大部分的GStreamer elements有可以定制化的属性:只读的属性会显示element的内部状态,可写的属性会影响element的行为。我们用g_object_get()方法来获得属性,用g_object_set()方法来设置属性。
g_object_set()方法接受一个用NULL结束的属性名称/属性值的组成的对,所以可以一次同时修改多项属性。
上面的代码修改了videotestsrc的“pattern”属性,这个属性控制了视频的输出,可以试试不同的值看一下效果。
Gsteramer的元素都是一种特殊的GObject,这也是为什么关于属性的方法都是使用g_作为前缀。
关于一个element所有属性的名字和取值范围可以使用gst-inspect工具查询。
/* 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()方法,但这次我们检查它的返回值。状态转换是一个很微妙的过程。
/* 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_filted()会一直等到运行结束,然后返回一个GstMessage参数。我们让gst_bus_timed_pop_filtered()方法仅在收到错误或者播放结束的消息时才返回。所以我们需要检查是哪个消息并打印出来。
GstMessage是一个非常通用的结构,它可以传递很多信息。幸好GStreamer提供了一系列的解析函数,我们一旦知道message里面包含一个错误(通过使用GST_MESSAGE_TYPE宏),我们可以使用gst_message_parse_error()方法,这个方法会返回一个GLib的GError结构。
GStreamer总线
这里稍微介绍一下GStreamer总线。GStreamer总线本身也是一个对象,是创建来传递elements生成的GstMessage的对象。消息可以在总线上用gst_bus_timed_pop_filtered()方法抓出来,你的应用需要随时注意出错的信息和播放相关的其他问题。
运行效果: