mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 21:28:45 +00:00
469 lines
18 KiB
XML
469 lines
18 KiB
XML
<!-- ############ chapter ############# -->
|
|
|
|
<chapter id="chapter-building-boiler" xreflabel="Constructing the Boilerplate">
|
|
<title>Constructing the Boilerplate</title>
|
|
<para>
|
|
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. 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.
|
|
</para>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-source" xreflabel="Getting the GStreamer Plugin Templates">
|
|
<title>Getting the GStreamer Plugin Templates</title>
|
|
<para>
|
|
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. The second method is by far
|
|
the simpler of the two, so the first method will not even be described
|
|
here. (Errm, that is, <quote>it is left as an exercise to the
|
|
reader.</quote>)
|
|
</para>
|
|
<para>
|
|
The first step is to check out a copy of the
|
|
<filename>gst-template</filename> git module to get an important tool and
|
|
the source code template for a basic &GStreamer; plugin. To check out the
|
|
<filename>gst-template</filename> module, make sure you are connected to
|
|
the internet, and type the following commands at a command console:
|
|
</para>
|
|
<screen>
|
|
<prompt>shell $ </prompt><userinput>git clone git://anongit.freedesktop.org/gstreamer/gst-template.git</userinput>
|
|
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.
|
|
</screen>
|
|
<para>
|
|
This command will check out a series of files and directories into
|
|
<filename class="directory">gst-template</filename>. The template you
|
|
will be using is in the
|
|
<filename class="directory">gst-template/gst-plugin/</filename>
|
|
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.
|
|
</para>
|
|
<para>
|
|
If for some reason you can't access the git repository, you can also
|
|
<ulink type="http"
|
|
url="http://cgit.freedesktop.org/gstreamer/gst-template/commit/">
|
|
download a snapshot of the latest revision</ulink> via the cgit web
|
|
interface.
|
|
</para>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-project-stamp" xreflabel="Using the Project Stamp">
|
|
<title>Using the Project Stamp</title>
|
|
<para>
|
|
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 <emphasis>boilerplate</emphasis>.
|
|
</para>
|
|
<para>
|
|
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
|
|
<filename class="directory">./gst-plugins/tools/</filename> directory.
|
|
This tool, <filename>make_element</filename>, is a command line utility
|
|
that creates the boilerplate code for you.
|
|
</para>
|
|
<para>
|
|
To use <command>make_element</command>, first open up a terminal window.
|
|
Change to the <filename class="directory">gst-template/gst-plugin/src</filename>
|
|
directory, and then run the <command>make_element</command> command. The
|
|
arguments to the <command>make_element</command> are:
|
|
</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>the name of the plugin, and</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
the source file that the tool will use. By default,
|
|
<filename>gstplugin</filename> is used.
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
<para>
|
|
For example, the following commands create the MyFilter plugin based on
|
|
the plugin template and put the output files in the
|
|
<filename class="directory">gst-template/gst-plugin/src</filename>
|
|
directory:
|
|
</para>
|
|
<screen>
|
|
<prompt>shell $ </prompt><userinput>cd gst-template/gst-plugin/src</userinput>
|
|
<prompt>shell $ </prompt><userinput>../tools/make_element MyFilter</userinput>
|
|
</screen>
|
|
<note>
|
|
<para>
|
|
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.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
The last command creates two files:
|
|
<filename>gstmyfilter.c</filename> and
|
|
<filename>gstmyfilter.h</filename>.
|
|
</para>
|
|
<note>
|
|
<para>
|
|
It is recommended that you create a copy of the <filename
|
|
class="directory">gst-plugin</filename>
|
|
directory before continuing.
|
|
</para>
|
|
</note>
|
|
<para>
|
|
Now one needs to adjust the <filename>Makefile.am</filename> to use the
|
|
new filenames and run <filename>autogen.sh</filename> from the parent
|
|
directory to bootstrap the build environment. After that, the project
|
|
can be built and installed using the well known
|
|
<userinput>make && sudo make install</userinput> commands.
|
|
</para>
|
|
<note>
|
|
<para>
|
|
Be aware that by default <filename>autogen.sh</filename> and
|
|
<filename>configure</filename> would choose <filename class="directory">/usr/local</filename>
|
|
as a default location. One would need to add
|
|
<filename class="directory">/usr/local/lib/gstreamer-1.0</filename>
|
|
to <symbol>GST_PLUGIN_PATH</symbol> in order to make the new plugin
|
|
show up in a gstreamer that's been installed from packages.
|
|
</para>
|
|
</note>
|
|
<note>
|
|
<para>
|
|
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.
|
|
</para>
|
|
</note>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-examine">
|
|
<title>Examining the Basic Code</title>
|
|
<para>
|
|
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.)
|
|
|
|
The code here can be found in
|
|
<filename>examples/pwg/examplefilter/boiler/gstexamplefilter.h</filename>.
|
|
</para>
|
|
|
|
<example id="ex-boiler-examine-h">
|
|
<title>Example Plugin Header File</title>
|
|
<programlisting><!-- example-begin filter.h a -->
|
|
#include <gst/gst.h>
|
|
|
|
/* Definition of structure storing data for this element. */
|
|
typedef struct _GstMyFilter {
|
|
GstElement element;
|
|
|
|
GstPad *sinkpad, *srcpad;
|
|
|
|
gboolean silent;
|
|
<!-- example-end filter.h a -->
|
|
<!-- example-begin filter.h b --><!--
|
|
gint samplerate, channels;
|
|
gint from_samplerate, to_samplerate;
|
|
gboolean passthrough;
|
|
guint64 offset;
|
|
--><!-- example-end filter.h b -->
|
|
<!-- example-begin filter.h c -->
|
|
} 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);
|
|
<!-- example-end filter.h c --></programlisting>
|
|
</example>
|
|
<para>
|
|
Using this header file, you can use the following macro to setup
|
|
the <classname>GObject</classname> basics in your source file so
|
|
that all functions will be called appropriately:
|
|
</para>
|
|
<programlisting><!-- example-begin boilerplate.c a -->
|
|
#include "filter.h"
|
|
|
|
G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
|
|
<!-- example-end boilerplate.c a --></programlisting>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-details">
|
|
<title>Element metadata</title>
|
|
<para>
|
|
The Element metadata provides extra element information. It is configured
|
|
with <function>gst_element_class_set_metadata</function> or
|
|
<function>gst_element_class_set_static_metadata</function> which takes the
|
|
following parameters:
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
A long, English, name for the element.
|
|
</para></listitem><listitem><para>
|
|
The type of the element, see the docs/design/draft-klass.txt document
|
|
in the GStreamer core source tree for details and examples.
|
|
</para></listitem><listitem><para>
|
|
A brief description of the purpose of the element.
|
|
</para></listitem><listitem><para>
|
|
The name of the author of the element, optionally followed by a contact
|
|
email address in angle brackets.
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
For example:
|
|
</para>
|
|
<programlisting>
|
|
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>");
|
|
</programlisting>
|
|
<para>
|
|
The element details are registered with the plugin during
|
|
the <function>_class_init ()</function> function, which is part of
|
|
the GObject system. The <function>_class_init ()</function> function
|
|
should be set for this GObject in the function where you register
|
|
the type with GLib.
|
|
</para>
|
|
<programlisting><!-- example-begin boilerplate.c c -->
|
|
static void
|
|
gst_my_filter_class_init (GstMyFilterClass * klass)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
<!-- example-end boilerplate.c c -->
|
|
[..]<!-- example-begin boilerplate.c d -->
|
|
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>");
|
|
<!-- example-end boilerplate.c d -->
|
|
}
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-padtemplates">
|
|
<title>GstStaticPadTemplate</title>
|
|
<para>
|
|
A GstStaticPadTemplate is a description of a pad that the element will
|
|
(or might) create and use. It contains:
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>A short name for the pad.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Pad direction.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Existence property. This indicates whether the pad exists always (an
|
|
<quote>always</quote> pad), only in some cases (a
|
|
<quote>sometimes</quote> pad) or only if the application requested
|
|
such a pad (a <quote>request</quote> pad).
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Supported types by this element (capabilities).</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
For example:
|
|
</para>
|
|
<programlisting><!-- example-begin boilerplate.c e -->
|
|
static GstStaticPadTemplate sink_factory =
|
|
GST_STATIC_PAD_TEMPLATE (
|
|
"sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("ANY")
|
|
);
|
|
<!-- example-end boilerplate.c e -->
|
|
<!-- example-begin boilerplate.c f --><!--
|
|
static GstStaticPadTemplate src_factory =
|
|
GST_STATIC_PAD_TEMPLATE (
|
|
"src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("ANY")
|
|
);
|
|
--><!-- example-end boilerplate.c f -->
|
|
</programlisting>
|
|
<para>
|
|
Those pad templates are registered during the
|
|
<function>_class_init ()</function> function with the
|
|
<function>gst_element_class_add_pad_template ()</function>. For this
|
|
function you need a handle the <classname>GstPadTemplate</classname>
|
|
which you can create from the static pad template with
|
|
<function>gst_static_pad_template_get ()</function>. See below for more
|
|
details on this.
|
|
</para>
|
|
<para>
|
|
Pads are created from these static templates in the element's
|
|
<function>_init ()</function> function using
|
|
<function>gst_pad_new_from_static_template ()</function>.
|
|
In order to create a new pad from this
|
|
template using <function>gst_pad_new_from_static_template ()</function>, you
|
|
will need to declare the pad template as a global variable. More on
|
|
this subject in <xref linkend="chapter-building-pads"/>.
|
|
</para>
|
|
<programlisting>
|
|
static GstStaticPadTemplate sink_factory = [..],
|
|
src_factory = [..];
|
|
|
|
static void
|
|
gst_my_filter_class_init (GstMyFilterClass * klass)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
[..]
|
|
<!-- example-begin boilerplate.c g -->
|
|
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));
|
|
}
|
|
<!-- example-end boilerplate.c g -->
|
|
<!-- example-begin boilerplate.c h --><!--
|
|
static void
|
|
gst_my_filter_init (GstMyFilter * filter)
|
|
{
|
|
}
|
|
|
|
#include "register.func"
|
|
--><!-- example-end boilerplate.c h --></programlisting>
|
|
<para>
|
|
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. 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:
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[
|
|
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 ]"
|
|
)
|
|
);
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
Values surrounded by curly brackets (<quote>{</quote> and
|
|
<quote>}</quote>) are lists, values surrounded by square brackets
|
|
(<quote>[</quote> and <quote>]</quote>) are ranges.
|
|
Multiple sets of types are supported too, and should be separated by
|
|
a semicolon (<quote>;</quote>). Later, in the chapter on pads, we will
|
|
see how to use types to know the exact format of a stream:
|
|
<xref linkend="chapter-building-pads"/>.
|
|
</para>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-constructors">
|
|
<title>Constructor Functions</title>
|
|
<para>
|
|
Each element has two functions which are used for construction of an
|
|
element. The <function>_class_init()</function> 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 <function>_init()</function> function, which is used to
|
|
initialise a specific instance of this type.
|
|
</para>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-plugininit">
|
|
<title>The plugin_init function</title>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<programlisting>
|
|
<!-- example-begin register.func -->
|
|
<![CDATA[
|
|
static gboolean
|
|
plugin_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/"
|
|
)
|
|
]]>
|
|
<!-- example-end register.func -->
|
|
</programlisting>
|
|
<para>
|
|
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.
|
|
</para>
|
|
</sect1>
|
|
</chapter>
|