Gstreamer插件教程2.1—编写一个插件(Writing a Plugin):构建样板(Constructing the Boilerplate)

英文原文:https://gstreamer.freedesktop.org/documentation/plugin-development/basics/boiler.html

本章节中,你将学会怎样使用最小的代码量构建一个最基本的新插件。由于从零开始,所以你将需要知道如何获得Gstreamer的模板源。然后,你将学会如何运用一些基本的工具来拷贝且修改一个模板插件,以创建一个新的插件。当你阅读完此章节,了解了下面的例子,你将得到一个功能性的音频过滤器插件,你可以编译此插件并在Gstreamer应用程序中使用它。

获取Gstreamer插件模板

这里有两种开发Gstreamer新插件的方式:你可以完全自己写一个插件,或者你可以拷贝一个Gstreamer中已存在的插件模板并作相应的修改。第二种方式是比较简单的,所以,在这里将不介绍第一种方式。(也就是说,这将留给读者作为练习)

第一步是需要从git中检出gst-template模块,从而获取一个重要的工具以及Gstreamer插件模板的源代码。为了检出gst-template模块,请确保你已经连上网络,并在终端输入如下的指令:

$git clone git://anongit.freedesktop.org/gstreamer/gst-template.git

这个命令将检出一系列的文件和目录到gst-template中。你将使用的是gst-template/gst-plugin/目录下的模板。你应该整体看一下这个目录下的文件以对一个plugin所拥有的内容结构有个大体的认知。

如果由于某些原因,你不能从这个git资源中获取数据,你也可以下载最新的快照。

使用工程戳记

创建一个新element的第一步是确定此element的基本信息:它的名称是什么,谁写的,它的版本号是什么等等。我们也需要定义一个对象来表示此element并存储此element所需要的数据。这些细节都被称为boilerplate。

定义boilerplate的标准方式是简单的写一些代码,并填充一些结构体。正如前面所提到的,最简单的方式是拷贝模板,并在此基础上添加你所需要的功能。为了帮助你完成这这些,在gst-plugin/gools目录下有一个工具。这个工具,叫make_element,可以通过命令行语句来为你创建boilerplate代码。

为了使用make_element工具,先打开终端,进入gst-template/gst-plugin/src目录,然后运行make_element命令,参数的含义是:

  1. 插件的名称
  2. 工具将用到的源文件,默认使用gstplugin
例如,下面的命令将基于插件模板创建MyFilter插件,并将输出文件放至gst-tempate/gst-plugin/src目录下:
$cd gst-template/gst-plugin/src
$../tools/make_element MyFilter

:对于插件来说大写化是非常重要的。在一些操作系统下,记住大小对文件及目录的命名是很重要的。

此命令创建了两个文件:gstmyfilter.c和gstmyfilter.h

:建议在此之前,先拷贝gst-plugin目录

现在,我们需要调整Makefile.am文件以适配前面创建的文件,然后运行父目录中的autogen.sh脚本来构建环境。在此之后,工程的创建和安装就可以用我们熟知的make和sudo make install命令了

:默认情况下,autogen.sh和configure将选择/usr/local作为默认的位置。为了让手动安装的Gstreamer能够识别新创建的插件,你需要将"/usr/local/lib/gstreamer-1.0"添加到变量GST_PLUGIN_PATH中。

:此部分内容多少有些过时了。gst-template这个系统构建框架作为一个创建最小插件的例子还是非常有用的。但是,目前为止,对于创建element,推荐使用gst-plugin-bad这个插件中的gst-element-maker工具。

检查基础代码

首先,我们应该先检查头文件中的代码(虽代码的接口是完全由插件系统定义的,但这并不重要)

#include 

/* Definition of structure storing data for this element. */
typedef struct _GstMyFilter {
  GstElement element;
  GstPad *sinkpad, *srcpad;
  gboolean silent;
} GstMyFilter;

/* Standard definition defining a class for this element. */
typedef struct _GstMyFilterClass {
  GstElementClass parent_class;
} GstMyFilterClass;

/* Standard macros for defining types for this element.  */
#define GST_TYPE_MY_FILTER (gst_my_filter_get_type())
#define GST_MY_FILTER(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MY_FILTER,GstMyFilter))
#define GST_MY_FILTER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MY_FILTER,GstMyFilterClass))
#define GST_IS_MY_FILTER(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MY_FILTER))
#define GST_IS_MY_FILTER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MY_FILTER))

/* Standard function returning type information. */
GType gst_my_filter_get_type (void);
用此头文件,你可以在你的源文件中用下面的宏来设置GObject的基本要素,这样,所有函数就能够被正确的调用:

#include "filter.h"

G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);


Element元数据

element元数据提供额外的element信息。它可以通过gst_element_class_set_metadatagst_element_class_set_static_metadata来配置,它使用了下面的参数:

  • 一个长的英文的element的名称
  • element的类型。查看Gstreamer中的docs/design/draft-kclass.txt文档,获取更多的详细信息
  • 此element创建目的的简介
  • 此element创建作者的名字,可后接尖括号包含邮件地址
例如:
gst_element_class_set_static_metadata (klass,
  "An example plugin",
  "Example/FirstExample",
  "Shows the basic structure of a plugin",
  "your name ");
element细节在 _class_init()函数调用时,通过插件注册,这是GObject系统的一部分。在你用Glib注册类型的函数中,函数 _class_init()应该被设置。 
static void
gst_my_filter_class_init (GstMyFilterClass * klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

[..]
  gst_element_class_set_static_metadata (element_klass,
    "An example plugin",
    "Example/FirstExample",
    "Shows the basic structure of a plugin",
    "your name ");
}

GstStaticPadTemplate

一个GstStaticPadTemplate是element将创建的pad的一个描述,它包括:
  • 一个简短的pad名称
  • pad方向
  • 存在属性。这表明了pad是不明是一直存在的(an "always" pad)、某些时刻存在(a "sometimes" pad)或者是只有当有需求时才会存在(a "request" pad)
  • 这个element所支持的类型(capabilities)
例如:
static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
  "sink",
  GST_PAD_SINK,
  GST_PAD_ALWAYS,
  GST_STATIC_CAPS ("ANY")
);
这些pad在 _class_init()函数调用期间,通过调用 gst_element_class_add_pad_template()函数进行注册。对于这个函数,你需要一个handle,即 GstPadTemplate,你可以从静态pad模板中,通过调用 gst_static_pad_template_get()函数来创建得到。下文将展示更多的细节。
pad在element的 _init()函数调用期间,通过调用 gst_pad_new_from_static_template()函数,从这些静态模板中创建得到。为了使用 gst_pad_new_from_static_template()函数从这些静态模板中创建得到一个新的pad,你将需要声明一个pad模板作为一个全局变量。更多关于此方面的内容可参考 Specifying the Pads.
static GstStaticPadTemplate sink_factory = [..],
    src_factory = [..];

static void
gst_my_filter_class_init (GstMyFilterClass * klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
[..]

  gst_element_class_add_pad_template (element_class,
    gst_static_pad_template_get (&src_factory));
  gst_element_class_add_pad_template (element_class,
    gst_static_pad_template_get (&sink_factory));
}
模板中的最后一个参数是它的类型或所支持的类型。在这个例子中,我们用'ANY',它这个element将接受所有的输入。在实际的情况中,你将会设置一个媒体类型及可选择的一系列的性质,以保证只有支持的输入才被接受。这个的表示方法应该是一个字符串,它以一个媒体类型开头,后接的是一系列用逗号分隔的其所支持类型的值。对于一个以任意抽样率,支持整型16-bit音频、单声道或立体声的音频过滤器,正确的模板应该像下面这样:
static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
  "sink",
  GST_PAD_SINK,
  GST_PAD_ALWAYS,
  GST_STATIC_CAPS (
    "audio/x-raw, "
      "format = (string) " GST_AUDIO_NE (S16) ", "
      "channels = (int) { 1, 2 }, "
      "rate = (int) [ 8000, 96000 ]"
  )
);
用波形括号("{"和"}")包含的表示的是列表,用方形括号("["和"]")包含的表示的是范围。若支持多种类型,则它们应该通过分号(";")分隔。在讲述pad的章节中,我们将看到如何通过类型知道流的格式(教程: Specifying the pads)

构造函数

每个element有两个函数用于构造此element。 _class_init()函数只用来初始化类型一次(初始化类型中包含信号是什么、参数、虚函数及设置全局变量);而 _init()函数用于实例此类型的一个对象。

插件初始化函数

一旦我们写好定义插件各个部分的代码,我们需要写 plugin_init()(在这里即myfilter_init())函数。这是一个特殊的函数,一旦插件被加载,此函数将被调用,并根据否成功加载了它的所有依赖来返回TRUE或FALSE。同样,此插件所支持的element类型也应该被注册。

static gboolean myfilter_init (GstPlugin *plugin)
{
  return gst_element_register (plugin, "my_filter",
                   GST_RANK_NONE,
                   GST_TYPE_MY_FILTER);
}

GST_PLUGIN_DEFINE (
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  my_filter,
  "My filter plugin",
  plugin_init,
  VERSION,
  "LGPL",
  "GStreamer",
  "http://gstreamer.net/"
)
注意到, plugin_init()函数所返回的信息将会被存储在一个统一注册表中。因此,函数总是返回相同的信息是非常重要的:例如,运行时环境是禁止创建可用的element工厂的。如果一个element只能在特定的环境下工作(例如,声卡在某些情况下是不能被使用的),必须在element对象中是能够反应出来的(此时不能进入READY状态),而不是此插件试图否认插件的存在。


你可能感兴趣的:(Gstreamer,gst,gstreamer,plugin,writer,插件,教程)