Gstreamer应用开发手册11:自动加载
自动加载
在前边我们学习过为 Ogg/Vorbis 文件建立一个简单的媒体播放器。通过替换元件,你同样可以建立一个播放其它文件格式的媒体播放器,像 Ogg/Speex, MP3 甚至视频格式。但是你可能更希望建立一个可以自动检测数据流的媒体格式的应用程序,该应用程序可以根据系统中可用元件自动建立一个最佳的管道。这个过程叫做自动加载(autoplugging), GStreamer 拥有一个高质量的自动加载器(autopluggers)。本文将会解释一些概念:自动加载和类型检测 (typefinding)。这些能解释 GStreamer 系统采用什么机制来实现自动检测数据流的媒体格式,以及怎样产生一个管道,该管道包含了可以播放该数据流的解码器。同样的原理也用在代码转换 (transcoding)。由于这些概念的动态性, GStreamer 可以自动扩展以便支持新的媒体类型,而不必要为自动加载器添加新的应用程序。
本文首先介绍媒体类型的概念,它通过一种动态可扩展的方式来识别媒体流。然后介绍类型检测(typefinding)的概念,它可以找到媒体流的类型。最后,介绍如何实现自动加载以及如何使用GStreamer 注册中心(registry)建立一条可以将媒体从一种媒体类型转换到另一种类型的管道。
识别流的媒体类型
回忆我们先前介绍过的功能(capabilities)概念,它规定了元件所能够处理的媒体格式,为元件(或者说衬垫)间交互数据流提供了一种协商的方式。我们说过功能是一个媒体类型与一些特性集的组合。对于大多数的容器格式(你磁盘上的文件的格式,例如 ogg 是一种容器格式),仅需要一个媒体类型来描述,而不需要其它的特性来描述它。完整的媒体类型以及对应的特性集可以参考插件开发手册.
一个加载进系统的元件必须提供其源衬垫和接收衬垫支持的媒体类型。 GStreamer 通过其注册中心可以知道目前注册的不同的元件,以及他们所期望得到的与他们能够产生的媒体类型。这允许我们创建非常具有扩展性的动态元件。
让我们了解下管道中各个衬垫所关联(associated)的媒体类型。 下图显示了管道中每个衬垫所处理的媒体类型。
现在我们了解了 GStreamer 如何识别已知的媒体流,接下来我们可以了解到 GStreamer 建立管道以及检测媒体类型的方法。
媒体流类型检测
通常当加载一个新的媒体流时,媒体的类型并不明了。这意味着当我们选择一条管道来对媒体流进行解码之前,我们首先需要检测媒体流的类型。 GStreamer 使用了类型检测(typefinding)来达到此目的。类型检测是构建管道所必经的步骤。首先它会一直读取数据流,在此期间,它会把数据提供给所有的实现了类型检测器(typefinder)的插件。当其中任何一个类型检测器识别出数据流,这个类型检测器元件将会发送一个信号,并开始像一个关卡 (passthrough)模块一样工作。如果数据流的类型没有被任何类型检测器识别出来,管道会发送一个错误信息,并终止所有正在处理该数据流的动作。一旦类型检测元件找到一个类型,应用程序将会使用该元件作为管道的一部分来解码媒体流。这在下部分将有详细讨论。
如前面提到的那样, GStreamer 中的插件实现了类型检测的功能。一个实现了这样功能的插件将会提交一个媒体类型,该媒体类型将用到的可选的文件扩展,以及一个类型检测函数。一旦插件中的类型检测函数被调用,插件将检查媒体流中的数据是否匹配特定的模式。这些模式标记了媒体类型中所识别的媒体类型。如果类型检测函数被调用,它会返回给类型检测元件,告知哪种媒体类型可以被识别,以及数据流与检测结果的吻合程度。一旦所有内置类型检测函数的插件都执行完类型检测函数,类型检测元件将会告诉应用程序它对数据流格式的判断。
下面的代码解释了如何使用类型检测元件。它将打印出检测到的媒体类型,或者给出不能匹配的媒体类型的信息。下一节将介绍更有用的行为,像插件结合 (plugging together)成一条解码管道。
#include <gst/gst.h>
[.. my_bus_callback goes here ..]
static gboolean
idle_exit_loop (gpointer data)
{
g_main_loop_quit ((GMainLoop *) data);
/* once */
return FALSE;
}
static void
cb_typefound (GstElement *typefind,
guint probability,
GstCaps *caps,
gpointer data)
{
GMainLoop *loop = data;
gchar *type;
type = gst_caps_to_string (caps);
g_print ("Media type %s found, probability %d%%\n", type, probability);
g_free (type);
/* since we connect to a signal in the pipeline thread context, we need
* to set an idle handler to exit the main loop in the mainloop context.
* Normally, your app should not need to worry about such things. */
g_idle_add (idle_exit_loop, loop);
}
gint
main (gint argc,
gchar *argv[])
{
GMainLoop *loop;
GstElement *pipeline, *filesrc, *typefind, *fakesink;
GstBus *bus;
/* init GStreamer */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* check args */
if (argc != 2) {
g_print ("Usage: %s <filename>\n", argv[0]);
return -1;
}
/* create a new pipeline to hold the elements */
pipeline = gst_pipeline_new ("pipe");
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_watch (bus, my_bus_callback, NULL);
gst_object_unref (bus);
/* create file source and typefind element */
filesrc = gst_element_factory_make ("filesrc", "source");
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
typefind = gst_element_factory_make ("typefind", "typefinder");
g_signal_connect (typefind, "have-type", G_CALLBACK (cb_typefound), loop);
fakesink = gst_element_factory_make ("fakesink", "sink");
/* setup */
gst_bin_add_many (GST_BIN (pipeline), filesrc, typefind, fakesink, NULL);
gst_element_link_many (filesrc, typefind, fakesink, NULL);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
g_main_loop_run (loop);
/* unset */
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}
一旦媒体类型被检测到,你就可以加载一个元件(分流器或解码器)来作为类型检测元件的源衬垫。媒体数据流的解码将从此开始。
上一篇: 【实用开发手册】Java json解析
下一篇: Gstreamer应用开发手册10:线程