Gstreamer应用开发手册2:初始化、元件

初始化

当你准备写一个GStreamer应用程序时,你仅需要通过包含头文件gst/gst.h 来访问库函数。除此之外,不要忘记初始化 GStreamer 库。

简易初始化

在 GStreamer 库被使用前,主应用程序中应该先调用函数 gst_init,这个函数将会对 GStreamer 库做一些必要的初始化工作,同时也能够对 GStreamer 的命令行参数进行解析。一个典型的初始化 GStreamer 库的代码如下所示:

#include <stdio.h>
#include <gst/gst.h>
 
int
main (int   argc,
      char *argv[])
{
  const gchar *nano_str;
  guint major, minor, micro, nano;
 
  gst_init (&argc, &argv);
 
  gst_version (&major, &minor, &micro, &nano);
 
  if (nano == 1)
    nano_str = "(CVS)";
  else if (nano == 2)
    nano_str = "(Prerelease)";
  else
    nano_str = "";
 
  printf ("This program is linked against GStreamer %d.%d.%d %s\n",
          major, minor, micro, nano_str);
 
  return 0;
}

你可以使用 GST_VERSION_MAJOR, GST_VERSION_MINOR 以及 GST_VERSION_MICRO 三个宏得到你的GStreamer版本信息,或者使用函数gst_version得到当前你所调用的程序库的版本信息。目前 GStreamer 使用了一种保证主要版本和次要版本中 API以及 ABI 兼容的策略。

当命令行参数不需要被 GStreamer 解析的时候,你可以在调用函数 gst_init 时使用 2 个 NULL 参数。

使用GOption初始化

你同样可以使用GOption表来初始化你的参数,如下所示

#include <gst/gst.h>
 
int
main (int   argc,
      char *argv[])
{
  gboolean silent = FALSE;
  gchar *savefile = NULL;
  GOptionContext *ctx;
  GError *err = NULL;
  GOptionEntry entries[] = {
    { "silent", 's', 0, G_OPTION_ARG_NONE, &silent,
      "do not output status information", NULL },
    { "output", 'o', 0, G_OPTION_ARG_STRING, &savefile,
      "save xml representation of pipeline to FILE and exit", "FILE" },
    { NULL }
  };
 
  ctx = g_option_context_new ("- Your application");
  g_option_context_add_main_entries (ctx, entries, NULL);
  g_option_context_add_group (ctx, gst_init_get_option_group ());
  if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
    g_print ("Failed to initialize: %s\n", err->message);
    g_clear_error (&err);
    g_option_context_free (ctx);
    return 1;
  }
  g_option_context_free (ctx);
 
  printf ("Run me with --help to see the Application options appended.\n");
 
  return 0;
}

如例子中的代码所示,你可以通过 GOption 表来定义你的命令行选项。将表与由gst_init_get_option_group 函数返回的选项组一同传给 GLib 初始化函数。通过使用 GOption 表来初始化GSreamer,你的程序还可以解析除标准 GStreamer 选项以外的命令行选项。

元件(element)
对程序员来说, GStreamer 中最重要的一个概念就是 GstElement 对象。元件是构建一个媒体管道的基本块。所有上层(high-level)部件都源自 GstElement 对象。任何一个解码器编码器、分离器、视频/音频输出部件实际上都是一个 GstElement 对象。

什么是元件?
对程序员来说,元件就像一个黑盒子。你从元件的一端输入数据,元件对数据进行一些处理,然后数据从元件的另一段输出。拿一个解码元件来说,你输入一些有特定编码的数据,元件会输出相应的解码数据。在下一章 (Pads and capabilities),你将学习到更多关于对元件进行数据输入输出的知识,以及如何在你的程序中实现数据的输入输出。

源元件
源元件(Source elements)为管道产生数据,比如从磁盘或者声卡读取数据。 我们总是将源衬垫(source pad)画在元件的右端。
Gstreamer应用开发手册2:初始化、元件
源元件不接收数据,仅产生数据。你可从上图中明白这一点,因为上图仅有一个源衬垫(右端),同样的, 源衬垫也仅产生数据(对外部而言)。

过滤器(filters)、转换器(convertors)、分流器(demuxers)、整流器(muxers)以及编解码器(codecs)
过滤器(Filters)以及类过滤元件(Filter-like elements)都同时拥有输入和输出衬垫。他们对从输入衬垫得到的数据进行操作,然后将数据提供给输出衬垫。 音量元件(filter)、视频转换器 (convertor)、Ogg 分流器或者 Vorbis 解码器都是这种类型的元件。

类过滤元件可以拥有任意个的源衬垫或者接收衬垫。像一个视频分流器可能有一个接收衬垫以及多个(1-N)源衬垫,每个接收衬垫对应一种元数据流 (elementary stream)。相反地,解码器只有一个源衬垫及一个接收衬垫。
Gstreamer应用开发手册2:初始化、元件
上图形象化了类过滤元件。这个特殊的元件同时拥有源端和接收端。接收输入数据的接收衬垫在元件的左端,源衬垫在右端。
Gstreamer应用开发手册2:初始化、元件
上图显示了另一种了类过滤元件。它有多个输出衬垫(source pad)。 Ogg 分流器是个很好的实例。因为 Ogg 流包含了视频和音频。一个源衬垫可能包含视频元数据流,另一个则包含音频元数据流。当一个新的衬垫被创 建时,分流器通常会产生一个信号。程序员可以在信号处理事件中处理新的元数据流。

接收元件
接收元件是媒体管道的末端,它接收数据但不产生任何数据。写磁盘、利用声卡播放声音以及视频输出等都是由接收元件实现的。 下图显示了接收元件。
Gstreamer应用开发手册2:初始化、元件
创建一个元件
创建一个元件的最简单的方法是通过函数 gst_element_factory_make ()。 这个函数使用一个已存在的工厂对象名和一个新的元件名来创建元件。创建完之后, 你可以用新的元件名在箱柜(bin)中查询得到这个元件。这个名字同样可以用来调试程序的输 出。你可以通过传递 NULL 来得到一个默认的具有唯一性的名字。

当你不再需要一个元件时,你需要使用 gst_object_unref ()来对它进行解引用。 这会将一个元件的引用数减少 1。任何一个元件在创建时,其引用记数为 1。当其引用记数为 0 时,该元件会被销毁。

下面的例子显示了如果通过一个fakesrc工厂对象来创建一个名叫source的元件。程序会检查元件是否创建成功。检查完毕后,程序会销毁元件。

#include <gst/gst.h>
 
int
main (int   argc,
      char *argv[])
{
  GstElement *element;
 
  /* init GStreamer */
  gst_init (&argc, &argv);
 
  /* create element */
  element = gst_element_factory_make ("fakesrc", "source");
  if (!element) {
    g_print ("Failed to create element of type 'fakesrc'\n");
    return -1;
  }
 
  gst_object_unref (GST_OBJECT (element));
 
  return 0;
}

gst_element_factory_make 是 2 个函数的速记。一个 GstElement 对象由工厂对象创建而来。为了创建一个元件,你需要使用一个唯一的工厂对象名字来访问一个GstElementFactory 对象。gst_element_factory_find ()就 是做了这样的事。下面的代码段创建了一个工厂对象,这个工厂对象被用来创建一个 fakesrc 元件 —— 伪装的数据源。函数 gst_element_factory_create() 将会使用元件工厂并根据给定的名字来创建一个元件。

#include <gst/gst.h>
 
int
main (int   argc,
      char *argv[])
{
  GstElementFactory *factory;
  GstElement * element;
 
  /* init GStreamer */
  gst_init (&argc, &argv);
 
  /* create element, method #2 */
  factory = gst_element_factory_find ("fakesrc");
  if (!factory) {
    g_print ("Failed to find factory of type 'fakesrc'\n");
    return -1;
  }
  element = gst_element_factory_create (factory, "source");
  if (!element) {
    g_print ("Failed to create element, even though its factory exists!\n");
    return -1;
  }
 
  gst_object_unref (GST_OBJECT (element));
  gst_object_unref (GST_OBJECT (factory));
 
  return 0;
}

使用元件作为GObject对象
GstElement的属性大多通过标准的 GObject 对象实现的。使用 GObject 的方法可以对GstElement实行查询、设置、获取属性的值。同样 GParamSpecs 也被支持。

每个 GstElement 都从其基类 GstObject 继承了至少一个“名字”属性。这个名字属性将在函数gst_element_factory_make ()或者函数 gst_element_factory_create ()中使用到。你可通过函数gst_object_set_name 设置该属性,通过 gst_object_get_name 得到一个对象的名字属性。你也可以通过下面的方法来得到一个对象的名字属性。

#include <gst/gst.h>
 
int
main (int   argc,
      char *argv[])
{
  GstElement *element;
  gchar *name;
 
  /* init GStreamer */
  gst_init (&argc, &argv);
 
  /* create element */
  element = gst_element_factory_make ("fakesrc", "source");
 
  /* get name */
  g_object_get (G_OBJECT (element), "name", &name, NULL);
  g_print ("The name of the element is '%s'.\n", name);
  g_free (name);
 
  gst_object_unref (GST_OBJECT (element));
 
  return 0;
}

大多数的插件(plugins)都提供了一些额外的方法,这些方法给程序员提供了更多的关于该元件的注册信息或配置信息。 gst-inspect 是一个用来查询特定元件特性(properties)的实用工具。它也提供了诸如函数简短介绍,参数的类型及其支持的范围等信息。关于 gst-inspect 更详细的信息请参考附录。关于 GObject 特性更详细的信息,我们推荐你去阅读 GObject 手册 以及 Glib 对象系统介绍.GstElement 对象同样提供了许多的 GObject 信号方法来实现一个灵活的回调机制。你同样可以使用 gst-inspect 来检查一个特定元件所支持的信号。总之,信号和特性是元件与应用程序交互的最基本的方式。

深入了解元件工厂
在前面的部分,我们简要介绍过 GstElementFactory 可以用来创建一个元件的实例,但是工厂元件不仅仅只能做这件事,工厂元件作为在 GStreamer 注册系统中的一个基本类型,它可以描述所有的插件(plugins)以及由 GStreamer 创建的元件。这意味着工厂元件可以应用于一些自动元件实例, 像自动插件(autopluggers); 或者创建一个可用元件列表,像管道对应用程序的类似操作(像GStreamer Editor)

通过元件工厂得到元件的信息
像gst-inspect 这样的工具可以给出一个元件的概要: 插件(plugin)的作者、描述性的元件名称(或者简称)、元件的等级(rank)以及元件的类别(category)。类别可以用来得到一个元件的类 型,这个类型是在使用工厂元件创建该元件时做创建的。例如类别可以是 Codec/Decoder/Video(视频解码器)、Source/Video(视频发生器)、 Sink/Video(视频输出器)。音频也有类似的类别。同样还存在Codec/Demuxer 和 Codec/Muxer,甚至更多的类别。 Gst-inspect 将会列出当前所有的工厂对象,gst-inspect 将会列出特定工厂对象的所有概要信息。

#include <gst/gst.h>
 
int
main (int   argc,
      char *argv[])
{
  GstElementFactory *factory;
 
  /* init GStreamer */
  gst_init (&argc, &argv);
 
  /* get factory */
  factory = gst_element_factory_find ("fakesrc");
  if (!factory) {
    g_print ("You don't have the 'fakesrc' element installed!\n");
    return -1;
  }
 
  /* display information */
  g_print ("The '%s' element is a member of the category %s.\n"
           "Description: %s\n",
           gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
           gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS),
           gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_DESCRIPTION));
 
  gst_object_unref (GST_OBJECT (factory));
 
  return 0;
}

你可以通过gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY)得到所有在GStreamer中注册过的工厂元件。

找出元件所包含的衬垫
工厂元件最有用处的功能可能是它包含了对元件所能产生的衬垫的一个详细描述,以及这些衬垫的功能(以行外话讲: 就是指这些衬垫所支持的媒体类型),而得到这些信息是不需要将所有的插件(plugins)都装载到内存中。这可用来给一个编码器提供一个编码列表,或在多媒体播放器自动加载插件时发挥作用。目前 所有基于 GStreamer 的多媒体播放器以及自动加载器(autoplugger)都是以上述方式工作。当我们在下一章:衬垫与功能( Pads and capabilities)中学习到 GstPad 与 GstCaps 时,会对上面的特性有个更清晰的了解。

链接元件
通过将一个源元件,零个或多个类过滤元件,和一个接收元件链接在一起,你可以建立起一条媒体管道。数据将在这些元件间流过。这是 GStreamer 中处理媒体的基本概念。下图用 3 个链接的元件形象化了媒体管道
Gstreamer应用开发手册2:初始化、元件
通过链接这三个元件,我们创建了一条简单的元件链。元件链中源元件(“element1”)的输出将会是类过滤元件 (“element2”)的输入。类过滤元件将会对数据进行某些操作,然后将数据输出给最终的接收元件(“element3”)。

把上述过程想象成一个简单的 Ogg/Vorbis 音频解码器。源元件从磁盘读取文件。第二个元件就是 Ogg/Vorbis 音频解码器。最终的接收元件是你的声卡,它用来播放经过解码的音频数据。我们将在该手册的后部分用一个简单的图来构建这个 Ogg/Vorbis 播放器。上述的过程用代码表示为:

#include <gst/gst.h>
 
int
main (int   argc,
      char *argv[])
{
  GstElement *pipeline;
  GstElement *source, *filter, *sink;
 
  /* init */
  gst_init (&argc, &argv);
 
  /* create pipeline */
  pipeline = gst_pipeline_new ("my-pipeline");
 
  /* create elements */
  source = gst_element_factory_make ("fakesrc", "source");
  filter = gst_element_factory_make ("identity", "filter");
  sink = gst_element_factory_make ("fakesink", "sink");
 
  /* must add elements to pipeline before linking them */
  gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL);
 
  /* link */
  if (!gst_element_link_many (source, filter, sink, NULL)) {
    g_warning ("Failed to link elements!");
  }
 
[..]
 
}

对于一些特定的链接行为,可以通过函数 gst_element_link () 以及 gst_element_link_pads()来实现。你可以使用不同的 gst_pad_link_* ()函数来得到单个衬垫的引用并将它们链接起来。更详细的信息请参考 API 手册。注意:在链接不同的元件之前,你需要确保这些元件都被加在同一个箱柜中,因为将一个元件加载到一个箱柜中会破坏该元件已存在的一些链接关系。同时,你不能直接链接不在同一箱柜或管道中的元件。如果你想要连接处于不同层次中的元件或衬垫,你将使用到精灵衬垫(关于精灵衬垫更多的信息将在后续章节中讲到) 。

元件状态
一个元件在被创建后,它不会执行任何操作。所以你需要改变元件的状态,使得它能够做某些事情。 Gstreamer 中,元件有四种状态,每种状态都有其特定的意义。这四种状态为:

  • GST_STATE_NULL: 默认状态。该状态将会回收所有被该元件占用的资源。
  • GST_STATE_READY:
    准备状态。元件会得到所有所需的全局资源,这些全局资源将被通过该元件的数据流所使用。例如打开设备、分配缓存等。但在这种状态下,数据流仍未开始被处
    理,所以数据流的位置信息应该自动置 0。如果数据流先前被打开过,它应该被关闭,并且其位置信息、特性信息应该被重新置为初始状态。
  • GST_STATE_PAUSED:
    在这种状态下,元件已经对流开始了处理,但此刻暂停了处理。因此该状态下元件可以修改流的位置信息,读取或者处理流数据,以及一旦状态变为
    PLAYING,流可以重放数据流。这种情况下,时钟是禁止运行的。总之, PAUSED 状态除了不能运行时钟外,其它与 PLAYING
    状态一模一样。处于 PAUSED 状态的元件会很快变换到 PLAYING
    状态。举例来说,视频或音频输出元件会等待数据的到来,并将它们压入队列。一旦状态改变,元件就会处理接收到的数据。同样,视频接收元件能够播放数据的第
    一帧。 (因为这并不会影响时钟)。自动加载器(Autopluggers)可以对已经加载进管道的插件进行这种状态转换。其它更多的像
    codecs或者 filters 这种元件不需要在这个状态上做任何事情。
  • GST_STATE_PLAYING: PLAYING 状态除了当前运行时钟外,其它与 PAUSED 状态一模一样。

你可以通过函数 gst_element_set_state()来改变一个元件的状态。你如果显式地改变一个元件的状态, GStreamer 可能会 使它在内部经过一些中间状态。例如你将一个元件从 NULL 状态设置为 PLAYING 状态, GStreamer 在其内部会使得元件经历过 READY 以及 PAUSED 状态。当处于 GST_STATE_PLAYING 状态,管道会自动处理数据。它们不需要任何形式的迭代。GStreamer 会开启一个新的线程来处理数据。 GStreamer 同样可以使用 GstBus 在管道线程和应用程序现成间交互信息。

将bin或管道设置为某个目标状态时,通常会将状态更改自动传播到bin或管道中的所有元素,因此通常仅需要设置*管道的状态即可启动管道或将其关闭。但是,当动态地将元素添加到已在运行的管道中时(例如,从“添加了pad的信号”回调中),您需要使用gst_element_set_state ()或gst_element_sync_state_with_parent ()将其设置为所需的目标状态。

上一篇:kaldi Gstreamer worker 运行时报错处理


下一篇:Spring(xml方法的五种通知)