2021 gstreamer插件编写指南中英对照 (三) 编写一个插件

Writing a Plugin写一个插件

You are now ready to learn how to build a plugin. In this part of the guide, you will learn how to apply basic GStreamer programming concepts to write a simple plugin.
现在您已经为学习如何构建插件做好准备了。在本部分指南中,您将学习如何应用基本的GStreamer编程概念来编写一个简单的插件。
The previous parts of the guide have contained no explicit example code, perhaps making things a bit abstract and difficult to understand. In contrast, this section will present both applications and code by following the development of an example audio filter plugin called “MyFilter”.
前面的部分,没有包含显式的示例代码,可能使内容有点抽象,难以理解。相反,本节将通过一个名为“MyFilter”的音频过滤器插件示例的开发,来具体地展示应用程序和代码。
The example filter element will begin with a single input pad and a single output pad. The filter will, at first, simply pass media and event data from its sink pad to its source pad without modification.
示例过滤器元素将以单个输入衬垫和单个输出衬垫开始。首先,该过滤器不经过任何改动,简单地将媒体和事件数据,从其输出sink 传递到其源source 衬垫。
But by the end of this part of the guide, you will learn to add some more interesting functionality, including properties and signal handlers. And after reading the next part of the guide, Advanced Filter Concepts, you will be able to add even more functionality to your plugins.
但是在本部分的末尾,您将学习添加一些更有趣的功能,包括属性和信号处理程序。在阅读了指南的下一部分,高级过滤概念,你将能够在插件中添加更多的功能。

Constructing the Boilerplate 创建样板

In this chapter you will learn how to construct the bare minimum code for a new plugin. Starting from ground zero, you will see how to get the GStreamer template source.
在本章中,你将学习如何构造一个新插件的最小代码。您可以从零开始,看到如何获得GStreamer模板源。Then you will learn how to use a few basic tools to copy and modify a template plugin to create a new plugin. If you follow the examples here, then by the end of this chapter you will have a functional audio filter plugin that you can compile and use in GStreamer applications.
然后你将学习如何使用一些基本的工具,通过复制和修改一个模板插件来创建一个新的插件。如果你遵循这里的示例,那么在本章结束时,你将得到一个功能强大的音频过滤器插件,你可以在GStreamer应用程序中编译和使用它。

Getting the GStreamer Plugin Templates 获取GStreamer插件模板

There are currently two ways to develop a new plugin for GStreamer: You can write the entire plugin by hand, or you can copy an existing plugin template and write the plugin code you need.
目前有两种方法开发GStreamer新插件:你可以手工编写整个插件,也可以复制一个现有的插件模板来改写成你需要的插件代码。
The second method is by far the simpler of the two, so the first method will not even be described here. (Errm, that is, “it is left as an exercise to the reader.”)
到目前为止,第二种方法是两种方法中最简单的一种,所以这里就不说第一种方法了。(嗯,也就是说,“作为练习请读者自己练习。”)
The first step is to check out a copy of the gst-template git module to get an important tool and the source code template for a basic GStreamer plugin. To check out the gst-template module, make sure you are connected to the internet, and type the following commands at a command console:
第一步是签出gst-template git模块的副本,为基本的插件获得重要工具和源代码模板。要检查gst-template模块,请确保您已连接到互联网,并在命令控制台输入以下命令:

shell $ git clone https://gitlab.freedesktop.org/gstreamer/gst-template.git
Initialized empty Git repository in /some/path/gst-template/.git/
remote: Counting objects: 373, done.
remote: Compressing objects: 100% (114/114), done.
remote: Total 373 (delta 240), reused 373 (delta 240)
Receiving objects: 100% (373/373), 75.16 KiB | 78 KiB/s, done.
Resolving deltas: 100% (240/240), done.

This command will check out a series of files and directories into gst-template. The template you will be using is in the gst-template/gst-plugin/ directory. You should look over the files in that directory to get a general idea of the structure of a source tree for a plugin.
这个命令将把一系列文件和目录签出到gst-template中。您将要使用的模板位于gst-template/gst-plugin/目录。您应该查看该目录下的文件,对插件的源代码树结构做大致了解。

If for some reason you can’t access the git repository, you can also download a snapshot of the latest revision via the gitlab web interface.
如果你由于某些原因,不能访问git仓库,也可以通过gitlab web界面下载最新版本的快照。

Using the Project Stamp 使用工程标记

The first thing to do when making a new element is to specify some basic details about it: what its name is, who wrote it, what version number it is, etc. We also need to define an object to represent the element and to store the data the element needs. These details are collectively known as the boilerplate.
创建新元素时要做的第一件事,是指定关于它的一些基本细节: 它的名字是什么,谁编写的,它的版本号是什么,等等。我们还需要定义一个对象来表示元素,并存储元素所需的数据。这些细节被统称为样板文件

The standard way of defining the boilerplate is simply to write some code, and fill in some structures. As mentioned in the previous section, the easiest way to do this is to copy a template and add functionality according to your needs. To help you do so, there is a tool in the ./gst-plugin/tools/ directory. This tool, make_element, is a command line utility that creates the boilerplate code for you.
定义样板文件的标准方法是编写代码,并填充一些结构体。如上一节所述,最简单的方法是复制模板并根据需要添加功能。为了帮助您做到这一点,在./gst-plugin/tools/目录中有一个工具叫make_element,它是一个命令行实用工具,可以直接创建样板代码。

To use make_element, first open up a terminal window. Change to the gst-template/gst-plugin/src directory, and then run the make_element command. The arguments to the make_element are:

  1. the name of the plugin, and
  2. the source file that the tool will use. By default, gstplugin is used.
    要使用make_element,首先打开一个终端窗口。切换到gst-template/gst-plugin/src目录,然后运行make_element命令。make_element的参数为:
  3. 插件的名称
  4. 工具将使用的源文件。默认使用gstplugin。
    For example, the following commands create the MyFilter plugin based on the plugin template and put the output files in the gst-template/gst-plugin/src directory:
    举个例子,下面的命令基于插件模板创建MyFilter插件,并将输出文件放在gst-template/gst-plugin/src目录下:
shell $ cd gst-template/gst-plugin/src
shell $ ../tools/make_element MyFilter

Note

Capitalization is important for the name of the plugin. Keep in mind that under some operating systems, capitalization is also important when specifying directory and file names in general.
提示:注意大小写,有的操作系统是区分大小写的,有些不分。

The last command creates two files: gstmyfilter.c and gstmyfilter.h.
最后一个命令创建两个文件:gstmyfilter.c和gstmyfilter.h。

Note
It is recommended that you create a copy of the gst-plugin directory before continuing.
Now one needs to adjust the meson.build to use the new filenames and run meson build from the parent directory to bootstrap the build environment. After that, the project can be built and installed using the well known ninja -C build commands.
提示:
建议您在继续操作之前,复制一份gst-plugin目录的副本。
现在需要的是修改meson,从父目录使用新的文件名运行meson来构建环境。之后,可以使用ninja -C build命令构建和安装项目。

Note
Be aware that by default meson will choose /usr/local as a default location. One would need to add /usr/local/lib/gstreamer-1.0 to GST_PLUGIN_PATH in order to make the new plugin show up in a gstreamer that’s been installed from packages.
请注意,默认meson 将选择/usr/local作为安装目录。我们需要将/usr/local/lib/gstreamer-1.0添加到GST_PLUGIN_PATH,使新插件从中安装。

Note
FIXME: this section is slightly outdated. gst-template is still useful as an example for a minimal plugin build system skeleton. However, for creating elements the tool gst-element-maker from gst-plugins-bad is recommended these days.
这个部分有点过时。作为一个最小插件构建系统框架的例子,Gst-template虽然很有用。但是,现在推荐使用gst-plugins-bad工具gst-element-maker来创建元素。

译者加注:

以上部生成插件模板的方式,是较老的。目前gstreamer的st-plugins-bad目录携带了一个gst-element-maker工具,可以直接从你想要的插件基类来生成模板代码。gst-element-maker位于源码库的tools目录下,实质上是一些文本处理shell程序,用来自动生成模板代码。工具的使用非常简单,输出插件名称+基类名称就可以生成模板了。通过gst-element-maker --help查看使用说明:

e0005055@ibudev01:~$ 
gstreamer/packages-debug/gst-plugins-bad-1.18.4/tools/gst-element-maker --help
Usage: element-maker [OPTIONS] ELEMENT_NAME BASE_CLASS
Create a GStreamer element that subclasses BASE_CLASS.
Options:
  --help             Print this information
  --prefix PREFIX    Use PREFIX instead of "gst"
Example: 'element-maker my_element basetransform' will create the files
  gstmyelement.c and gstmyelement.h that implement GstMyElement, a
  subclass of GstBaseTransform, as an element named myelement.

比如,我们要通过videoencoder为基类创建一个叫MyEncoder的插件模板:

e0005055@ibudev01:~$ gst-element-maker MyEncoder videoencoder
gst-element-maker: 383: gst-element-maker: gst-indent: not found
pkg is gstreamer-video-1.0
Plugin Details:
  Name                     myencoder
  Description              FIXME plugin description
  Filename                 ./gstmyencoder.so
  Version                  0.0.FIXME
  License                  LGPL
  Source module            FIXME_package
  Binary package           FIXME_package_name
  Origin URL               http://FIXME.org/

  myencoder: FIXME Long name

  1 features:
  +-- 1 elements

gst-element-maker MyEncoder videoencoder 表示用gst-element-maker 以videoencoder为基类,创建一个MyEncoder 的插件模板。执行之后,会立刻在当前目录生成以下模板:

e0005055@ibudev01:~$ ls
gstmyencoder.c
gstmyencoder.h
gstmyencoder.o
gstmyencoder.so

Examining the Basic Code 基本代码检查

First we will examine the code you would be likely to place in a header file (although since the interface to the code is entirely defined by the plugin system, and doesn’t depend on reading a header file, this is not crucial.)
首先,我们将检查可能放在头文件中的代码(尽管由于代码的接口完全由插件系统定义,并且不依赖于读取头文件,不过这并不重要)。

#include <gst/gst.h>

/* 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);

GST_ELEMENT_REGISTER_DECLARE(my_filter)

Using this header file, you can use the following macros to setup the Element basics in your source file so that all functions will be called appropriately:
使用这个头文件,你可以使用下面的宏在你的源文件中设置元素Element的基础信息,所有的函数都会被适当地调用:

#include "filter.h"

G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE(my_filter, "my-filter", GST_RANK_NONE, GST_TYPE_MY_FILTER);

The macro GST_ELEMENT_REGISTER_DEFINE in combination with GST_ELEMENT_REGISTER_DECLARE allows to register the element from within the plugin or from any other plugin/application by calling GST_ELEMENT_REGISTER (my_filter).
宏GST_ELEMENT_REGISTER_DEFINE结合宏GST_ELEMENT_REGISTER_DECLARE,通过调用GST_ELEMENT_REGISTER (my_filter)从插件或应用程序中注册元素。

Element metadata 元素的元数据

The Element metadata provides extra element information. It is configured with gst_element_class_set_metadata or gst_element_class_set_static_metadata which takes the following parameters:
Element元数据提供元素的额外信息,通过gst_element_class_set_metadata或gst_element_class_set_static_metadata来配置,有以下参数:

  • A long, English, name for the element.长英文名

  • The type of the element, see the docs/additional/design/draft-klass.txt document in the GStreamer core source tree for details and examples.
    元素类型,可以在源码的docs/additional/design/draft-klass.txt文件看到详细描述和示例。

  • A brief description of the purpose of the element.
    对元素用途的简要描述。

  • The name of the author of the element, optionally followed by a contact email address in angle brackets.
    作者的名字,跟尖括号中的联系人电子邮件地址。
    For example:

gst_element_class_set_static_metadata (klass,
  "An example plugin",
  "Example/FirstExample",
  "Shows the basic structure of a plugin",
  "your name <your.name@your.isp>");

The element details are registered with the plugin during the _class_init () function, which is part of the GObject system. The _class_init () function should be set for this GObject in the function where you register the type with GLib.
插件通过_class_init 函数来注册详细信息,该函数是GObject 的一部分,在GLib注册类型的时候,需要为GObject 设置_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 <your.name@your.isp>");

}

GstStaticPadTemplate gst静态衬垫模板

A GstStaticPadTemplate is a description of a pad that the element will (or might) create and use. It contains:
GstStaticPadTemplate是pad的描述,元素将(或可能)创建和使用这个Pad。它包含:

  • A short name for the pad. 衬垫名字
  • Pad direction. 衬垫方向
  • Existence property. This indicates whether the pad exists always (an “always” pad), only in some cases (a “sometimes” pad) or only if the application requested such a pad (a “request” pad).
    衬垫的性质,表示衬垫是否一直存在:“always”、“sometimes”和“request”。
  • Supported types by this element (capabilities).
    支持的属性Capabilities。

For example:

static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
  "sink",
  GST_PAD_SINK,
  GST_PAD_ALWAYS,
  GST_STATIC_CAPS ("ANY")
);

Those pad templates are registered during the _class_init () function with the gst_element_class_add_pad_template (). For this function you need a handle to the GstPadTemplate which you can create from the static pad template with gst_static_pad_template_get (). See below for more details on this.
这些衬垫通过gst_element_class_add_pad_template函数在_class_init 的时候注册。对于这个函数,您需要一个GstPadTemplate的句柄,可以通过gst_static_pad_template_get()从静态pad模板创建该句柄。有关这方面的更多细节见下文。
Pads are created from these static templates in the element’s _init () function using gst_pad_new_from_static_template (). In order to create a new pad from this template using gst_pad_new_from_static_template (), you will need to declare the pad template as a global variable. More on this subject in Specifying the pads.
gst_pad_new_from_static_template()从元素的_init()函数中的这些静态模板创建pad。为了使用gst_pad_new_from_static_template()从这个模板创建一个新的pad,您需要将pad模板声明为一个全局变量。关于这个主题的更多信息请参见指定垫子。

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));
}

The last argument in a template is its type or list of supported types. In this example, we use ‘ANY’, which means that this element will accept all input. In real-life situations, you would set a media type and optionally a set of properties to make sure that only supported input will come in.
模板中的最后一个参数是它的类型或支持的类型列表。在本例中,我们使用了“ANY”,这意味着该元素将接受所有输入。在实际情况中,您可以设置媒体类型和可选的一组属性,以确保只输入受支持的类型。
This representation should be a string that starts with a media type, then a set of comma-separates properties with their supported values. In case of an audio filter that supports raw integer 16-bit audio, mono or stereo at any samplerate, the correct template would look like this:
这个描述是一个以媒体类型开头的字符串,然后是一组以逗号分隔的属性及其支持的值。如果一个音频滤波器在任何采样点都支持原始整数16位音频、单声道或立体声,那么的模板应该是这样的:

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 ]"
  )
);

Values surrounded by curly brackets (“{” and “}”) are lists, values surrounded by square brackets (“[” and “]”) are ranges. Multiple sets of types are supported too, and should be separated by a semicolon (“;”). Later, in the chapter on pads, we will see how to use types to know the exact format of a stream: Specifying the pads.
用花括号("{“和”}")括起来的值是列表,用方括号("[“和”]")括起来的值是范围。当然还可以支持多个类型,不同的类型用分号(";")分隔。稍后,在关于pads的章节中,我们将看到如何使用类型来了解流的确切格式: 指定pads。

Constructor Functions 构造函数

Each element has two functions which are used for construction of an element. The _class_init() function, which is used to initialise the class only once (specifying what signals, arguments and virtual functions the class has and setting up global state); and the _init() function, which is used to initialise a specific instance of this type.
每个元素都有两个用于构造元素的函数。_class_init()函数,仅用于初始化类一次(指定类具有哪些信号、参数和虚函数,并设置全局状态); 另外就是_init()函数,它用于初始化该类型的特定实例。

The plugin_init function 插件初始化函数

Once we have written code defining all the parts of the plugin, we need to write the plugin_init() function. This is a special function, which is called as soon as the plugin is loaded, and should return TRUE or FALSE depending on whether it loaded initialized any dependencies correctly. Also, in this function, any supported element type in the plugin should be registered.
一旦我们编写了插件的所有代码,我们就需要编写plugin_init()函数。这是一个特殊的函数,一旦插件被加载就会被调用,它会根据是否正确加载初始化的依赖项,来返回TRUE或FALSE。同样,任何受插件支持的元素类型,都应该在这个函数里被注册。

static gboolean
plugin_init (GstPlugin *plugin)
{
  return GST_ELEMENT_REGISTER (my_filter, plugin);
}

GST_PLUGIN_DEFINE (
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  my_filter,
  "My filter plugin",
  plugin_init,
  VERSION,
  "LGPL",
  "GStreamer",
  "http://gstreamer.net/"
)

Note that the information returned by the plugin_init() function will be cached in a central registry. For this reason, it is important that the same information is always returned by the function: for example, it must not make element factories available based on runtime conditions. If an element can only work in certain conditions (for example, if the soundcard is not being used by some other process) this must be reflected by the element being unable to enter the READY state if unavailable, rather than the plugin attempting to deny existence of the plugin.
注意,plugin_init()函数返回的信息将缓存在中心注册表中。因此,函数总是返回相同的信息是很重要的: 例如,它不能基于运行时条件,来使能元素工厂。意思是,如果一个元素只能在特定的条件下工作(例如,如果声卡没有被其他进程使用),这应该通过元素不可用时无法进入READY状态来反馈,而不是插件在插入时就被拒绝。

Specifying the pads 描述衬垫

As explained before, pads are the port through which data goes in and out of your element, and that makes them a very important item in the process of element creation. In the boilerplate code, we have seen how static pad templates take care of registering pad templates with the element class. Here, we will see how to create actual elements, use an _event ()-function to configure for a particular format and how to register functions to let data flow through the element.
如前所述,衬垫Pad是数据进出元素的端口,所以在元素创建过程中非常重要。在样板代码中,我们已经看到了静态垫模板如何用元素类注册Pad模板。在这里,我们将看到如何创建实际的元素,如何使用_event()函数配置特定的格式,以及如何注册函数让数据流经元素。
In the element _init () function, you create the pad from the pad template that has been registered with the element class in the _class_init () function. After creating the pad, you have to set a _chain () function pointer that will receive and process the input data on the sinkpad. You can optionally also set an _event () function pointer and a _query () function pointer. Alternatively, pads can also operate in looping mode, which means that they can pull data themselves. More on this topic later. After that, you have to register the pad with the element. This happens like this:
在元素的_init()函数里,我们从模板中创建的Pad会通过元素类的_class_init()函数中进行注册。创建pad之后,你必须设置一个_chain()函数指针,用于接收和处理sink pad上的输入数据。您还可以选择设置_event()函数指针和_query()函数指针。或者,pad也可以在循环模式下工作,意思是它们可以自己提取数据,我们稍后再讨论这个话题。之后,您必须向元素注册pad。像这样:

static void
gst_my_filter_init (GstMyFilter *filter)
{
  /* pad through which data comes in to the element */
  filter->sinkpad = gst_pad_new_from_static_template (
    &sink_template, "sink");
  /* pads are configured here with gst_pad_set_*_function () */
  
  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
  /* pad through which data goes out of the element */
  filter->srcpad = gst_pad_new_from_static_template (
    &src_template, "src");
  /* pads are configured here with gst_pad_set_*_function () */
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
  /* properties initial value */
  filter->silent = FALSE;
}

The chain function 链函数

The chain function is the function in which all data processing takes place. In the case of a simple filter, _chain () functions are mostly linear functions - so for each incoming buffer, one buffer will go out, too. Below is a very simple implementation of a chain function:
链函数chain function 是进行所有数据处理的函数。对于一个简单的过滤器,_chain()函数大多是线性函数-所以对于每个传入的缓冲区,也会有一个缓冲区输出。下面是一个非常简单的chain函数的实现:

static GstFlowReturn gst_my_filter_chain (GstPad    *pad,
                                          GstObject *parent,
                                          GstBuffer *buf);

[..]

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  /* configure chain function on the pad before adding
   * the pad to the element */
  gst_pad_set_chain_function (filter->sinkpad,
      gst_my_filter_chain);
[..]
}

static GstFlowReturn
gst_my_filter_chain (GstPad    *pad,
                     GstObject *parent,
             GstBuffer *buf)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);

  if (!filter->silent)
    g_print ("Have data of size %" G_GSIZE_FORMAT" bytes!\n",
        gst_buffer_get_size (buf));

  return gst_pad_push (filter->srcpad, buf);
}

Obviously, the above doesn’t do much useful. Instead of printing that the data is in, you would normally process the data there. Remember, however, that buffers are not always writeable.
显然,上面的方法没有多大用处。您通常会在这里处理数据,而不是打印数据所在的位置。但是,请记住,缓冲区不易i的那个是可写的,操作之前先进行判断。

In more advanced elements (the ones that do event processing), you may want to additionally specify an event handling function, which will be called when stream-events are sent (such as caps, end-of-stream, newsegment, tags, etc.).
在更高级的元素(执行事件处理的元素)中,您可能想要额外指定一个事件处理函数,当流事件stream-events被发送时,会调用该函数(如caps, end- stream, newsegment, tags,等等)。

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  gst_pad_set_event_function (filter->sinkpad,
      gst_my_filter_sink_event);
[..]
}

static gboolean
gst_my_filter_sink_event (GstPad    *pad,
                  GstObject *parent,
                  GstEvent  *event)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
      /* we should handle the format here */
      break;
    case GST_EVENT_EOS:
      /* end-of-stream, we should close down all stream leftovers here */
      gst_my_filter_stop_processing (filter);
      break;
    default:
      break;
  }

  return gst_pad_event_default (pad, parent, event);
}

static GstFlowReturn
gst_my_filter_chain (GstPad    *pad,
             GstObject *parent,
             GstBuffer *buf)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);
  GstBuffer *outbuf;

  outbuf = gst_my_filter_process_data (filter, buf);
  gst_buffer_unref (buf);
  if (!outbuf) {
    /* something went wrong - signal an error */
    GST_ELEMENT_ERROR (GST_ELEMENT (filter), STREAM, FAILED, (NULL), (NULL));
    return GST_FLOW_ERROR;
  }

  return gst_pad_push (filter->srcpad, outbuf);
}

In some cases, it might be useful for an element to have control over the input data rate, too. In that case, you probably want to write a so-called loop-based element. Source elements (with only source pads) can also be get-based elements. These concepts will be explained in the advanced section of this guide, and in the section that specifically discusses source pads.
在某些情况下,元素控制输入数据速率可能也很有用。在这种情况下,您可能需要编写一个所谓的基于循环的元素。源元素(只有source pads)也可以是基于获取的元素。这些概念将在本指南的高级部分和专门讨论源文件集的部分中进行解释。

The event function 事件函数

The event function notifies you of special events that happen in the datastream (such as caps, end-of-stream, newsegment, tags, etc.). Events can travel both upstream and downstream, so you can receive them on sink pads as well as source pads.
event 函数会在数据流中发生的特殊事件(如caps, end- stream, newsegment, tags,等等)时,发出通知。它可以上游和下游传播,因此您在sink pads和source pads上都可以接收它们。
Below follows a very simple event function that we install on the sink pad of our element.
下面是一个非常简单的event 函数,我们安装在元素的sink pads上。

static gboolean gst_my_filter_sink_event (GstPad    *pad,
                                          GstObject *parent,
                                          GstEvent  *event);

[..]

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  /* configure event function on the pad before adding
   * the pad to the element */
  gst_pad_set_event_function (filter->sinkpad,
      gst_my_filter_sink_event);
[..]
}

static gboolean
gst_my_filter_sink_event (GstPad    *pad,
                  GstObject *parent,
                  GstEvent  *event)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
      /* we should handle the format here */

      /* push the event downstream */
      ret = gst_pad_push_event (filter->srcpad, event);
      break;
    case GST_EVENT_EOS:
      /* end-of-stream, we should close down all stream leftovers here */
      gst_my_filter_stop_processing (filter);

      ret = gst_pad_event_default (pad, parent, event);
      break;
    default:
      /* just call the default handler */
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }
  return ret;
}

It is a good idea to call the default event handler gst_pad_event_default () for unknown events. Depending on the event type, the default handler will forward the event or simply unref it. The CAPS event is by default not forwarded so we need to do this in the event handler ourselves.
对于未知事件,最好调用默认事件处理程序gst_pad_event_default()。根据事件类型的不同,默认处理程序将转发事件或直接将其取消引用。CAPS事件默认情况下不转发,因此我们需要在事件处理程序中自己来执行。

The query function 查询函数

Through the query function, your element will receive queries that it has to reply to. These are queries like position, duration but also about the supported formats and scheduling modes your element supports. Queries can travel both upstream and downstream, so you can receive them on sink pads as well as source pads.
通过查询函数query function,元素收到它必须应答的查询。这些查询包括位置、持续时间,以及元素支持的格式和调度模式。查询可以向上和向下移动,所以在sink pads和source pads上都可以接收它们。
Below follows a very simple query function that we install on the source pad of our element.
下面是一个非常简单的查询函数,我们将它安装在元素的source pad上。

static gboolean gst_my_filter_src_query (GstPad    *pad,
                                         GstObject *parent,
                                         GstQuery  *query);

[..]

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  /* configure event function on the pad before adding
   * the pad to the element */
  gst_pad_set_query_function (filter->srcpad,
      gst_my_filter_src_query);
[..]
}

static gboolean
gst_my_filter_src_query (GstPad    *pad,
                 GstObject *parent,
                 GstQuery  *query)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
      /* we should report the current position */
      [...]
      break;
    case GST_QUERY_DURATION:
      /* we should report the duration here */
      [...]
      break;
    case GST_QUERY_CAPS:
      /* we should report the supported caps here */
      [...]
      break;
    default:
      /* just call the default handler */
      ret = gst_pad_query_default (pad, parent, query);
      break;
  }
  return ret;
}

It is a good idea to call the default query handler gst_pad_query_default () for unknown queries. Depending on the query type, the default handler will forward the query or simply unref it.
对于未知查询,调用默认查询处理程序gst_pad_query_default()是个好主意。根据查询类型的不同,默认处理程序将转发查询或简单地取消对查询的引用。

What are states? 状态是啥?

A state describes whether the element instance is initialized, whether it is ready to transfer data and whether it is currently handling data. There are four states defined in GStreamer:
状态states描述了元素实例是否初始化、是否准备好传输数据以及当前是否正在处理数据。GStreamer中定义了四种状态:

  • GST_STATE_NULL

  • GST_STATE_READY

  • GST_STATE_PAUSED

  • GST_STATE_PLAYING

which will from now on be referred to simply as “NULL”, “READY”, “PAUSED” and “PLAYING”.
现在我们简称为 “NULL”, “READY”, “PAUSED” 和“PLAYING”.

GST_STATE_NULL is the default state of an element. In this state, it has not allocated any runtime resources, it has not loaded any runtime libraries and it can obviously not handle data.
GST_STATE_NULL是元素的默认状态。在这种状态下,它没有分配任何运行时资源,没有加载任何运行时库,显然不能处理数据。
GST_STATE_READY is the next state that an element can be in. In the READY state, an element has all default resources (runtime-libraries, runtime-memory) allocated. However, it has not yet allocated or defined anything that is stream-specific. When going from NULL to READY state (GST_STATE_CHANGE_NULL_TO_READY), an element should allocate any non-stream-specific resources and should load runtime-loadable libraries (if any).
GST_STATE_READY 在READY状态下,元素会分配所有默认资源(运行时库、运行时内存)。但是,它还没有分配或定义任何特定于流的内容。当从NULL状态变为READY状态(GST_STATE_CHANGE_NULL_TO_READY)时,一个元素应该分配任何non-stream-specific的资源,并应该加载运行时可加载的库(如果有的话)。
When going the other way around (from READY to NULL, GST_STATE_CHANGE_READY_TO_NULL), an element should unload these libraries and free all allocated resources. Examples of such resources are hardware devices. Note that files are generally streams, and these should thus be considered as stream-specific resources; therefore, they should not be allocated in this state.
反之(从READY到NULL, GST_STATE_CHANGE_READY_TO_NULL),元素应该卸载这些库并释放所有已分配的资源,例如硬件设备。请注意,文件通常是流,因此这些应该被视为stream-specific的资源;因此,它们不应该在这种状态下进行分配。
GST_STATE_PAUSED is the state in which an element is ready to accept and handle data. For most elements this state is the same as PLAYING. The only exception to this rule are sink elements. Sink elements only accept one single buffer of data and then block. At this point the pipeline is ‘prerolled’ and ready to render data immediately.
GST_STATE_PAUSED是元素准备接受和处理数据的状态。对于大多数元素来说,这种状态等同于玩游戏。此规则的唯一例外是接收器元素。Sink元素只接受一个数据缓冲区,然后阻塞。此时,管道被“预滚”,并准备立即渲染数据。
GST_STATE_PLAYING is the highest state that an element can be in. For most elements this state is exactly the same as PAUSED, they accept and process events and buffers with data. Only sink elements need to differentiate between PAUSED and PLAYING state. In PLAYING state, sink elements actually render incoming data, e.g. output audio to a sound card or render video pictures to an image sink.
GST_STATE_PLAYING是一个元素可以处于的最高状态。对于大多数元素来说,这种状态与PAUSED完全相同,它们接受并处理事件和数据缓冲区。只有接收器元素需要区分暂停和播放状态。在播放状态下,接收器元素实际上渲染传入的数据,例如将音频输出到声卡或将视频图片渲染到图像接收器。

Managing filter state管理过滤器状态

If at all possible, your element should derive from one of the new base classes (Pre-made base classes). There are ready-made general purpose base classes for different types of sources, sinks and filter/transformation elements. In addition to those, specialised base classes exist for audio and video elements and others.
如果可能的话,您的元素应该从一个新的基类(预制基类)派生。对于不同类型的sources, sinks和过滤器/转换元素,有现成的通用基类。除此之外,还有专门的音频和视频元素基类。

If you use a base class, you will rarely have to handle state changes yourself. All you have to do is override the base class’s start() and stop() virtual functions (might be called differently depending on the base class) and the base class will take care of everything for you.
如果使用基类,则很少需要自己处理状态变化。你所要做的就是重写基类的start()和stop()虚函数(根据基类的差异可能有不同的调用),基类会为你处理所有事情。
If, however, you do not derive from a ready-made base class, but from GstElement or some other class not built on top of a base class, you will most likely have to implement your own state change function to be notified of state changes. This is definitively necessary if your plugin is a demuxer or a muxer, as there are no base classes for muxers or demuxers yet.
但是,如果您不是从一个现成的基类派生,而是从GstElement或其他非基类之上构建的派生类,那么您很可能必须实现自己的状态更改函数,以便在状态更改时得到通知。如果你的插件是muxer或muxer,这是绝对必要的,因为还没有muxer或muxer的基类。
An element can be notified of state changes through a virtual function pointer. Inside this function, the element can initialize any sort of specific data needed by the element, and it can optionally fail to go from one state to another.
可以通过虚函数指针通知元素状态的的变更。在这个函数中,元素可以初始化元素所需的任何类型的特定数据,也可以选择从拒绝从一种状态切换到另一种状态。
Do not g_assert for unhandled state changes; this is taken care of by the GstElement base class.
对于未处理的状态更改不要使用g_assert;这由GstElement基类负责。

static GstStateChangeReturn
gst_my_filter_change_state (GstElement *element, GstStateChange transition);

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

  element_class->change_state = gst_my_filter_change_state;
}



static GstStateChangeReturn
gst_my_filter_change_state (GstElement *element, GstStateChange transition)
{
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
  GstMyFilter *filter = GST_MY_FILTER (element);

  switch (transition) {
	case GST_STATE_CHANGE_NULL_TO_READY:
	  if (!gst_my_filter_allocate_memory (filter))
		return GST_STATE_CHANGE_FAILURE;
	  break;
	default:
	  break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  if (ret == GST_STATE_CHANGE_FAILURE)
	return ret;

  switch (transition) {
	case GST_STATE_CHANGE_READY_TO_NULL:
	  gst_my_filter_free_memory (filter);
	  break;
	default:
	  break;
  }

  return ret;
}

Note that upwards (NULL=>READY, READY=>PAUSED, PAUSED=>PLAYING) and downwards (PLAYING=>PAUSED, PAUSED=>READY, READY=>NULL) state changes are handled in two separate blocks with the downwards state change handled only after we have chained up to the parent class’s state change function. This is necessary in order to safely handle concurrent access by multiple threads.
请注意,向上(NULL => READY,READY => PAUSED,PAUSED => PLAYING)和向下(PLAYING => PAUSED,PAUSED => READY,READY => NULL)状态更改,在两个单独的块中处理,向下状态更改只有在我们链接到父类的状态更改函数之后才会处理。这是为了安全地处理多个线程的并发访问。
The reason for this is that in the case of downwards state changes you don’t want to destroy allocated resources while your plugin’s chain function (for example) is still accessing those resources in another thread. Whether your chain function might be running or not depends on the state of your plugin’s pads, and the state of those pads is closely linked to the state of the element. Pad states are handled in the GstElement class’s state change function, including proper locking, that’s why it is essential to chain up before destroying allocated resources.
这样做的原因是,在向下状态改变的情况下,你不想破坏分配的资源,比如,你的插件的chain函数仍然在另一个线程中访问这些资源。chain函数是否运行取决于插件的pad的状态,而这些pad的状态与元素的状态紧密相连。Pad状态(包括正确的加锁),是在GstElement类的状态更改函数中处理的,这就是为什么在销毁已分配的资源之前必须将其连接起来。

Adding Properties 额外的属性

The primary and most important way of controlling how an element behaves, is through GObject properties. GObject properties are defined in the _class_init () function. The element optionally implements a _get_property () and a _set_property () function. These functions will be notified if an application changes or requests the value of a property, and can then fill in the value or take action required for that property to change value internally.
控制元素行为的主要也最重要的方法,是通过GObject属性来实现。GObject属性在_class_init()函数中定义。元素可以选择性地实现_get_property()和_set_property()函数。
You probably also want to keep an instance variable around with the currently configured value of the property that you use in the get and set functions. Note that GObject will not automatically set your instance variable to the default value, you will have to do that in the _init () function of your element.
您可能还希望将实例变量,与get和set函数中使用属性的当前配置值保持在一起。注意,GObject不会自动将实例变量设置为默认值,您必须在元素的_init()函数中来做。

/* properties */
enum {
  PROP_0,
  PROP_SILENT
  /* FILL ME */
};

static void gst_my_filter_set_property  (GObject      *object,
                         guint         prop_id,
                         const GValue *value,
                         GParamSpec   *pspec);
static void gst_my_filter_get_property  (GObject      *object,
                         guint         prop_id,
                         GValue       *value,
                         GParamSpec   *pspec);

static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  /* define virtual function pointers */
  object_class->set_property = gst_my_filter_set_property;
  object_class->get_property = gst_my_filter_get_property;

  /* define properties */
  g_object_class_install_property (object_class, PROP_SILENT,
    g_param_spec_boolean ("silent", "Silent",
              "Whether to be very verbose or not",
              FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}

static void
gst_my_filter_set_property (GObject      *object,
                guint         prop_id,
                const GValue *value,
                GParamSpec   *pspec)
{
  GstMyFilter *filter = GST_MY_FILTER (object);

  switch (prop_id) {
    case PROP_SILENT:
      filter->silent = g_value_get_boolean (value);
      g_print ("Silent argument was changed to %s\n",
           filter->silent ? "true" : "false");
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_my_filter_get_property (GObject    *object,
                guint       prop_id,
                GValue     *value,
                GParamSpec *pspec)
{
  GstMyFilter *filter = GST_MY_FILTER (object);

  switch (prop_id) {
    case PROP_SILENT:
      g_value_set_boolean (value, filter->silent);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

The above is a very simple example of how properties are used. Graphical applications will use these properties and will display a user-controllable widget with which these properties can be changed. This means that - for the property to be as user-friendly as possible - you should be as exact as possible in the definition of the property.
上面是如何使用属性的一个非常简单的例子。图形应用程序将使用这些属性,并将显示一个用户可控制的小部件,可以用它更改这些属性。这意味着——为了使属性尽可能易用——您应该在属性的定义中尽可能精确。
Not only in defining ranges in between which valid properties can be located (for integers, floats, etc.), but also in using very descriptive (better yet: internationalized) strings in the definition of the property, and if possible using enums and flags instead of integers. The GObject documentation describes these in a very complete way, but below, we’ll give a short example of where this is useful. Note that using integers here would probably completely confuse the user, because they make no sense in this context. The example is stolen from videotestsrc.
不仅是在定义有效属性之间的范围(对于整数、浮点数等),而且在属性定义中使用描述性(更好的是:国际化)的字符串,如果可能的话使用枚举和标志而不是整数。GObject文档以一种非常完整的方式描述了这些,但是下面,我们将给出一个简短的示例,说明这在哪里有用。注意,在这里使用整数可能会完全混淆用户,因为它们在这个上下文中没有意义。这个示例是从videotestsrc中窃取的。

typedef enum {
  GST_VIDEOTESTSRC_SMPTE,
  GST_VIDEOTESTSRC_SNOW,
  GST_VIDEOTESTSRC_BLACK
} GstVideotestsrcPattern;

[..]

#define GST_TYPE_VIDEOTESTSRC_PATTERN (gst_videotestsrc_pattern_get_type ())
static GType
gst_videotestsrc_pattern_get_type (void)
{
  static GType videotestsrc_pattern_type = 0;

  if (!videotestsrc_pattern_type) {
    static GEnumValue pattern_types[] = {
      { GST_VIDEOTESTSRC_SMPTE, "SMPTE 100% color bars",    "smpte" },
      { GST_VIDEOTESTSRC_SNOW,  "Random (television snow)", "snow"  },
      { GST_VIDEOTESTSRC_BLACK, "0% Black",                 "black" },
      { 0, NULL, NULL },
    };

    videotestsrc_pattern_type =
    g_enum_register_static ("GstVideotestsrcPattern",
                pattern_types);
  }

  return videotestsrc_pattern_type;
}

[..]

static void
gst_videotestsrc_class_init (GstvideotestsrcClass *klass)
{
[..]
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PATTERN,
    g_param_spec_enum ("pattern", "Pattern",
               "Type of test pattern to generate",
                       GST_TYPE_VIDEOTESTSRC_PATTERN, GST_VIDEOTESTSRC_SMPTE,
                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
[..]
}

Signals 信号

GObject signals can be used to notify applications of events specific to this object. Note, however, that the application needs to be aware of signals and their meaning, so if you’re looking for a generic way for application-element interaction, signals are probably not what you’re looking for. In many cases, however, signals can be very useful. See the GObject documentation for all internals about signals.
可以使用GObject信号,向应用程序发出此对象特定事件的通知。但是请注意,应用程序需要知道信号及其含义,因此如果您正在寻找应用程序元素交互的通用方式,那么信号可能并不适用。当然,在许多情况下,信号是非常有用的。有关信号的所有内部信息,请参见GObject文档。

Building a Test Application 编译一个测试应用

Often, you will want to test your newly written plugin in an as small setting as possible. Usually, gst-launch-1.0 is a good first step at testing a plugin. If you have not installed your plugin in a directory that GStreamer searches, then you will need to set the plugin path. Either set GST_PLUGIN_PATH to the directory containing your plugin, or use the command-line option --gst-plugin-path.
通常,您会希望在尽可能小的设置中测试新编写的插件。一般来讲,gst-launch-1.0是测试插件的第一步。如果您不是在GStreamer搜索的目录中安装的插件,那么您需要再设置插件路径。将GST_PLUGIN_PATH设置为包含插件的目录,或者使用命令行选项 --gst-plugin-path。

If you based your plugin off of the gst-plugin template, then this will look something like gst-launch-1.0 --gst-plugin-path=$HOME/gst-template/gst-plugin/src/.libs TESTPIPELINE However, you will often need more testing features than gst-launch-1.0 can provide, such as seeking, events, interactivity and more.

如果你的插件是基于gst-plugin模板的,那么它看起来就像gst-launch-1.0 --gst-plugin-path=$HOME/gst-template/gst-plugin/src/。不过,您通常需要提供比gst-launch-1.0更多的测试特性,比如搜索、事件、交互性等等。
Writing your own small testing program is the easiest way to accomplish this. This section explains - in a few words - how to do that. For a complete application development guide, see the Application Development Manual.
编写自己的小型测试程序是实现这一目标的最简单方法。本节用几句话解释了如何做到这一点。要获得完整的应用程序开发指南,请参阅《应用程序开发手册》。
At the start, you need to initialize the GStreamer core library by calling gst_init (). You can alternatively call gst_init_get_option_group (), which will return a pointer to GOptionGroup. You can then use GOption to handle the initialization, and this will finish the GStreamer initialization.
在开始,您需要通过调用gst_init()初始化GStreamer核心库。您也可以调用gst_init_get_option_group(),它将返回一个指向GOptionGroup的指针。然后可以使用GOption来完成GStreamer初始化。
You can create elements using gst_element_factory_make (), where the first argument is the element type that you want to create, and the second argument is a free-form name. The example at the end uses a simple filesource - decoder - soundcard output pipeline, but you can use specific debugging elements if that’s necessary.
您可以使用gst_element_factory_make()创建元素,其中第一个参数是您想要创建的元素类型,第二个参数是自由形式的名称。最后的示例使用了一个简单的文件源—解码器—声卡输出管道,但是如果有必要,可以使用特定的调试元素。
For example, an identity element can be used in the middle of the pipeline to act as a data-to-application transmitter. This can be used to check the data for misbehaviours or correctness in your test application. Also, you can use a fakesink element at the end of the pipeline to dump your data to the stdout (in order to do this, set the dump property to TRUE). Lastly, you can use valgrind to check for memory errors.
例如,可以在管道的中间使用标识元素作为数据到应用程序的发送器。这可以用来检查测试应用程序中数据的错误还是正确。此外,还可以在管道的末尾使用fakesink元素将数据转储到stdout(为此,将转储属性设置为TRUE)。最后,您可以使用valgrind来检查内存错误。
During linking, your test application can use filtered caps as a way to drive a specific type of data to or from your element. This is a very simple and effective way of checking multiple types of input and output in your element.
在链接期间,您的测试应用程序可以使用过滤的caps,将特定类型的数据发送到元素或从元素中取出数据。这是检查元素中多种类型输入和输出的简单方法。
Note that during running, you should listen for at least the “error” and “eos” messages on the bus and/or your plugin/element to check for correct handling of this. Also, you should add events into the pipeline and make sure your plugin handles these correctly (with respect to clocking, internal caching, etc.).
注意,在运行过程中,你至少要监听总线和/或插件/元素上的" error “和” eos "消息,以检查消息是不是正确。此外,你应该在管道中添加事件,并确保你的插件正确处理了这些事件(关于时钟,内部缓存等)。
Never forget to clean up memory in your plugin or your test application. When going to the NULL state, your element should clean up allocated memory and caches. Also, it should close down any references held to possible support libraries. Your application should unref () the pipeline and make sure it doesn’t crash.
永远不要忘记清理插件或测试应用程序中的内存。当进入NULL状态时,您的元素应该清理已分配的内存和缓存。此外,它应该关闭对相关支持库的任何引用。您的应用程序应该unref()管道,确保它不会崩溃。

#include <gst/gst.h>

static gboolean
bus_call (GstBus     *bus,
      GstMessage *msg,
      gpointer    data)
{
  GMainLoop *loop = data;

  switch (GST_MESSAGE_TYPE (msg)) {
    case GST_MESSAGE_EOS:
      g_print ("End-of-stream\n");
      g_main_loop_quit (loop);
      break;
    case GST_MESSAGE_ERROR: {
      gchar *debug = NULL;
      GError *err = NULL;

      gst_message_parse_error (msg, &err, &debug);

      g_print ("Error: %s\n", err->message);
      g_error_free (err);

      if (debug) {
        g_print ("Debug details: %s\n", debug);
        g_free (debug);
      }

      g_main_loop_quit (loop);
      break;
    }
    default:
      break;
  }

  return TRUE;
}

gint
main (gint   argc,
      gchar *argv[])
{
  GstStateChangeReturn ret;
  GstElement *pipeline, *filesrc, *decoder, *filter, *sink;
  GstElement *convert1, *convert2, *resample;
  GMainLoop *loop;
  GstBus *bus;
  guint watch_id;

  /* initialization */
  gst_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);
  if (argc != 2) {
    g_print ("Usage: %s <mp3 filename>\n", argv[0]);
    return 01;
  }

  /* create elements */
  pipeline = gst_pipeline_new ("my_pipeline");

  /* watch for messages on the pipeline's bus (note that this will only
   * work like this when a GLib main loop is running) */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  watch_id = gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

  filesrc  = gst_element_factory_make ("filesrc", "my_filesource");
  decoder  = gst_element_factory_make ("mad", "my_decoder");

  /* putting an audioconvert element here to convert the output of the
   * decoder into a format that my_filter can handle (we are assuming it
   * will handle any sample rate here though) */
  convert1 = gst_element_factory_make ("audioconvert", "audioconvert1");

  /* use "identity" here for a filter that does nothing */
  filter   = gst_element_factory_make ("my_filter", "my_filter");

  /* there should always be audioconvert and audioresample elements before
   * the audio sink, since the capabilities of the audio sink usually vary
   * depending on the environment (output used, sound card, driver etc.) */
  convert2 = gst_element_factory_make ("audioconvert", "audioconvert2");
  resample = gst_element_factory_make ("audioresample", "audioresample");
  sink     = gst_element_factory_make ("pulsesink", "audiosink");

  if (!sink || !decoder) {
    g_print ("Decoder or output could not be found - check your install\n");
    return -1;
  } else if (!convert1 || !convert2 || !resample) {
    g_print ("Could not create audioconvert or audioresample element, "
             "check your installation\n");
    return -1;
  } else if (!filter) {
    g_print ("Your self-written filter could not be found. Make sure it "
             "is installed correctly in $(libdir)/gstreamer-1.0/ or "
             "~/.gstreamer-1.0/plugins/ and that gst-inspect-1.0 lists it. "
             "If it doesn't, check with 'GST_DEBUG=*:2 gst-inspect-1.0' for "
             "the reason why it is not being loaded.");
    return -1;
  }

  g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);

  gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, convert1, filter,
                    convert2, resample, sink, NULL);

  /* link everything together */
  if (!gst_element_link_many (filesrc, decoder, convert1, filter, convert2,
                              resample, sink, NULL)) {
    g_print ("Failed to link one or more elements!\n");
    return -1;
  }

  /* run */
  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    GstMessage *msg;

    g_print ("Failed to start up pipeline!\n");

    /* check if there is an error message with details on the bus */
    msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0);
    if (msg) {
      GError *err = NULL;

      gst_message_parse_error (msg, &err, NULL);
      g_print ("ERROR: %s\n", err->message);
      g_error_free (err);
      gst_message_unref (msg);
    }
    return -1;
  }

  g_main_loop_run (loop);

  /* clean up */
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  g_source_remove (watch_id);
  g_main_loop_unref (loop);

  return 0;
}

译者加注:

关于filter element could built的错误。
在前面的篇幅中,我们使用了模板创建了一个my_filter的插件,接下来我们编写一个应用来测试my_filter插件。我们是通过gst_element_factory_make (“my_filter”, “my_filter”)导入插件,也就是动态加载这个my_filter.so库文件。加载库文件就需要让编译器能正常找到这个库文件。如果库文件不在默认的路径下,才需要设置通过 --gst-plugin-path=/xxx/my_filter来指定路径,告诉编译器这个库文件在哪里。–gst-plugin-path是在运行gst-launch-1.0时带入的命令行参数。当不是通过gst-launch-1.0,而是直接运行程序时,–gst-plugin-path不能使用。所以仍然找不到插件,于是我们容易遇到这样的错误:

filter element could built

很多时候我们没有root权限,不能将插件的so安装到系统默认路径。此时可以通过GST_PLUGIN_PATH环境变量来设定插件路径:

export GST_PLUGIN_PATH=/home/e0005055/plugin-Test/bin

再运行应用程序就可以正常运行了。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值