现在您可以学习如何构建插件了。在本指南的这一部分中,您将学习如何应用基本的GStreamer编程概念来编写一个简单的插件。指南的前几部分没有包含明确的示例代码,这可能会使事情变得有点抽象和难以理解。相反,本节将通过开发一个名为“MyFilter”的示例音频过滤器插件来展示应用程序和代码。
示例过滤器元件将从单个输入Pag和单个输出Pad开始。过滤器首先只需将媒体和事件数据从其接收器板传递到源板,而无需修改。但是在本部分的末尾,您将学习添加一些更有趣的功能,包括属性和信号处理程序。在阅读了指南的下一部分“高级过滤器概念”之后,您将能够为插件添加更多功能。
1. 构建样板文件
在本章中,您将学习如何为一个新插件构造最简单的代码。从ground zero开始,您将看到如何获取GStreamer模板源代码。然后您将学习如何使用一些基本工具来复制和修改模板插件以创建新插件。如果您遵循这里的示例,那么在本章结束时,您将拥有一个功能强大的音频过滤器插件,可以在GStreamer应用程序中编译和使用。
1.1 获取GStreamer插件模板
目前有两种方法可以为GStreamer开发新的插件:您可以手工编写整个插件,或者您可以复制现有的插件模板并编写所需的插件代码。第二种方法是两种方法中最简单的一种,因此第一种方法在这里甚至不作描述(嗯,也就是说,“这是留给读者的练习。”)
第一步是签出 gst-template
的 git模块的副本,以获得一个重要的工具和基本GStreamer插件的源代码模板。要签出gst-template
模块,请确保已连接到internet,并在命令控制台键入以下命令:
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.
这个命令将把一系列文件和目录签出到 gst-template
中。您将使用的模板位于gst-template/gst-plugin/
目录中。您应该查看该目录中的文件,以大致了解插件的源代码树的结构。
如果由于某种原因无法访问git存储库,还可以通过gitlab web界面下载最新版本的快照。
1.2 使用项目 Stamp
实际操作说明
为了不浪费大家的时间,我要声明一下,官网的这个说明是无法在 Jetson Nano 环境下运行的。原因是,Jetson Nano 安装的 GStreamer 版本号为1.14.5
。官网的这个教程提供的gst-template
要求 GStreamer 版本要不低于1.19.0
。我不可能去升级 Jetson Nano 的系统,因为这个是厂家 SD 镜像给做好的,与其他软件模块都匹配很好。如果我升级这个版本,不知道会惹下多少麻烦。所以我去 github 下载了一个 2013 年发布的gst-template
,完美地完成了插件设计。具体参见:>
《在 Jetson Nano 上编写 GStreamer 插件:gst-template 编译方法》
1.2.1 创建样板代码
创建新元素时要做的第一件事是指定有关它的一些基本细节:它的名称、编写者、版本号等。我们还需要定义一个对象来表示元素并存储元素所需的数据。这些细节统称为样板。
定义样板文件的标准方法只是编写一些代码,并填充一些结构。如前一节所述,最简单的方法是复制模板并根据需要添加功能。为了帮助您这样做,./gst-plugin/tools/
目录中有一个工具。这个工具make_ element是一个命令行实用程序,它为您创建样板代码。
要使用make_element,首先打开一个终端窗口。切换到gst-template/gst-plugin/src
目录,然后运行make_element命令。make_element的参数是:
- 插件的名称,以及
- 工具将使用的源文件。默认情况下,使用
gstplugin
。
例如,以下命令基于插件模板创建MyFilter插件,并将输出文件放在gst-template/gst-plugin/src
目录中:
shell $ cd gst-template/gst-plugin/src
shell $ ../tools/make_element MyFilter
注意
大写对于插件的名称很重要。请记住,在某些操作系统中,通常在指定目录名和文件名时,大写也很重要。
最后一个命令创建两个文件:gstmyfilter.c
和 gstmyfilter.h
。
注意
建议您在继续之前创建gst-plugin
目录的副本。
1.2.2 meson build 构建环境
现在需要调整meson.build
以使用新的文件名(具体参见下面的补充说明),并从父目录(这个目录在我的开发环境下是 /home/jetson/gst-template
)运行meson build
以引导构建环境。之后,可以使用众所周知的ninja -C build
命令来构建和安装项目。
补充说明:在我的Jetson Nano开发环境下,运行 meson build
,系统显示:
The Meson build system
Version: 0.45.1
Source dir: /home/jetson/gst-template
Build dir: /home/jetson/gst-template/build
Build type: native build
Project name: gst-template
Native C compiler: cc (gcc 7.5.0 "cc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0")
Build machine cpu family: aarch64
Build machine cpu: aarch64
Found pkg-config: /usr/bin/pkg-config (0.29.1)
Native dependency gstreamer-1.0 found: NO found '1.14.5' but need: '>=1.19'
Also couldn't find a fallback subproject in subprojects/gstreamer for the dependency gstreamer-1.0
Reason: Subproject directory 'subprojects/gstreamer' does not exist and cannot be downloaded:
No gstreamer.wrap found for 'subprojects/gstreamer'
meson.build:11:0: ERROR: Invalid version of dependency, need 'gstreamer-1.0' ['>=1.19'] found '1.14.5'.
A full log can be found at /home/jetson/gst-template/build/meson-logs/meson-log.txt
其中关键信息是说,我的 gstreamer-1.0 版本太低。我直接把文件 /home/jetson/gst-template/meson.build
里面的版本号修改成和我的版本一致:
#project('gst-template', 'c', version : '1.19.0.1', license : 'LGPL')
project('gst-template', 'c', version : '1.14.5', license : 'LGPL')
plugins_install_dir = join_paths(get_option('libdir'), 'gstreamer-1.0')
cc = meson.get_compiler('c')
gst_version = meson.project_version()
api_version = '1.0'
#gst_dep = dependency('gstreamer-1.0', version : '>=1.19',
gst_dep = dependency('gstreamer-1.0', version : '>=1.14',
required : true, fallback : ['gstreamer', 'gst_dep'])
#gstbase_dep = dependency('gstreamer-base-1.0', version : '>=1.19',
gstbase_dep = dependency('gstreamer-base-1.0', version : '>=1.14',
fallback : ['gstreamer', 'gst_base_dep'])
subdir('gst-app')
subdir('gst-plugin')
再编译,提示下面的错误:
gst-plugin/meson.build:46:0: ERROR: File src/gstMyFilter.c does not exist.
把文件 /home/jetson/gst-template/gst-plugin/meson.build
中的文件名 gstMyFilter.c
改成 gstmyfilter.c
就没问题了。参见下面内容,修改两个位置的字符串 gstMyFiter
为 gstmyfilter
:
# The MyFilter Plugin
gstMyFilter_sources = [
'src/gstmyfilter.c',
]
gstMyFilterexample = library('gstmyfilter',
gstMyFilter_sources,
c_args: plugin_c_args,
dependencies : [gst_dep, gstbase_dep],
install : true,
install_dir : plugins_install_dir,
)
1.2.3 ninja -C build 构建并安装插件
再编译,问题仍然很多。我发现官网教程的 gst-template 的版本与 Jetson Nano 的 GStreamer 版本不一致。Nano 的版本太低了,但是我又不敢轻易提升 Nano 的 Gstreamer 的版本。于是,决定去 GitHub 下载一个就旧的 gst-template 软件包。我会专门写文章介绍这个老版本的安装。下面就对这个最新的版本的安装不多描述了,直接介绍官方发布的正常内容。
注意
请注意,默认情况下,meson
将选择/usr/local
作为默认位置。我们需要将/usr/local/lib/gstreamer-1.0
添加到GST_PLUGIN_PATH
路径中,以便使新的插件显示在从软件包安装的gstreamer中。
注意
修正:这个部分有点过时了。gst-template 作为一个最小插件构建系统框架的例子仍然很有用。然而,为了创建元素,最近推荐使用来自 gst-plugins-bad 的工具 gst-element-maker。
1.3 检查基本代码
首先,我们将检查您可能放置在头文件中的代码(尽管由于代码的接口完全由插件系统定义,并且不依赖于读取头文件,这并不重要)
#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)
使用此头文件,您可以使用以下宏来设置源文件中的元素基础,以便适当地调用所有函数:
#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);
宏GST_ELEMENT_REGISTER_DEFINE
与GST_ELEMENT_REGISTER_DECLARE
结合使用,可以通过调用GST_ELEMENT_REGISTER (my_filter)
从插件内或任何其他插件/应用程序注册元素。
1.4 元素元数据
元素元数据提供额外的元素信息。它配置有gst_element_class_set_metadata
或gst_element_class_set_static_metadata
,其参数如下:
- 元素的英文长名称。
- 元素的类型,请参阅GStreamer核心源代码树中的docs/additional/design/draft-klass.txt文档以获取详细信息和示例。
- 元素用途的简要说明。
- 元素的作者的名称,后跟尖括号中的联系人电子邮件地址(可选)。
例如:
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>");
1.5 GstStaticPadTemplate
GstStaticPadTemplate 是元素将(或可能)创建和使用的 pad 的描述。它包含:
- Pad 的简称。
- Pad 方向。
- 存在属性。这表示 pad 是否总是存在(an “always” pad)、仅在某些情况下存在(a “sometimes” pad)还是仅在应用程序请求此类pad(a “request” pad)时存在。
- 此元素支持的类型(功能)。
例如:
static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
"sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("ANY")
);
这些pad模板在使用 gst_element_class_add_pad_template()
的 _class_init ()
函数期间注册。对于此函数,您需要 GstPadTemplate
的句柄,您可以使用 gst_static_pad_template_get()
从静态 pad 模板创建该句柄。详见下文。
pad 是使用 gst_pad_new_from_static_template()
从元素的 _init()
函数中的这些静态模板创建的。为了使用 gst_pad_new_from_static_template()
从这个模板创建一个新的 pad,您需要将 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));
}
1.6 构造函数
每个元素有两个用于构造元素的函数。_class_init()
函数,仅用于初始化类一次(指定类具有哪些信号、参数和虚拟函数,并设置全局状态);以及 _init()
函数,用于初始化此类型的特定实例。
1.7 plugin_init 函数
一旦我们编写了定义插件所有部分的代码,我们就需要编写 plugin_init()
函数。这是一个特殊的函数,在加载插件后立即调用,并根据是否正确加载了任何依赖项返回 TRUE
或 FALSE
。另外,在这个函数中,插件中任何支持的元素类型都应该被注册。
参考资料