mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
Original commit message from CVS: Remove extraneous 'co' from cvs command in PWG, as reported on irc.
438 lines
17 KiB
XML
438 lines
17 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> CVS 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>cvs -d:pserver:anoncvs@cvs.freedesktop.org/cvs/gstreamer login</userinput>
|
|
Logging in to :pserver:anoncvs@cvs.freedesktop.org:/cvs/gstreamer
|
|
CVS password: <keycap>[ENTER]</keycap>
|
|
|
|
<prompt>shell $ </prompt><userinput>cvs -z3 -d:pserver:anoncvs@cvs.freedesktop.org:/cvs/gstreamer co gst-template</userinput>
|
|
U gst-template/README
|
|
U gst-template/gst-app/AUTHORS
|
|
U gst-template/gst-app/ChangeLog
|
|
U gst-template/gst-app/Makefile.am
|
|
U gst-template/gst-app/NEWS
|
|
U gst-template/gst-app/README
|
|
U gst-template/gst-app/autogen.sh
|
|
U gst-template/gst-app/configure.ac
|
|
U gst-template/gst-app/src/Makefile.am
|
|
...
|
|
</screen>
|
|
<para>
|
|
After the first command, you will have to press <keycap>ENTER</keycap> to
|
|
log in to the CVS server. (You might have to log in twice.) The second
|
|
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 <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>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-project-stamp" xreflabel="Using the Project Stamp">
|
|
<title>Using the Project Stamp</title>
|
|
<!-- FIXME
|
|
This section needs some fixing from someone that is aware of how this
|
|
works. The only tool that looks like the ones cited there is
|
|
<filename>gst-plugins/tools/filterstamp.sh</filename>
|
|
-->
|
|
<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 are some tools in the
|
|
<filename class="directory">./gst-plugins/tools/</filename> directory.
|
|
One tool, <filename>gst-quick-stamp</filename>, is a quick command line
|
|
tool. The other, <filename>gst-project-stamp</filename>, is a full GNOME
|
|
druid application that takes you through the steps of creating a new
|
|
project (either a plugin or an application).
|
|
</para>
|
|
<para>
|
|
To use <command>pluginstamp.sh</command>, first open up a terminal window.
|
|
Change to the <filename class="directory">gst-template</filename>
|
|
directory, and then run the <command>pluginstamp.sh</command> command. The
|
|
arguments to the <command>pluginstamp.sh</command> are:
|
|
</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>the name of the plugin, and</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
the directory that should hold a new subdirectory for the source tree
|
|
of the plugin.
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
<para>
|
|
Note that capitalization is important for the name of the plugin. Under
|
|
some operating systems, capitalization is also important when specifying
|
|
directory names. For example, the following commands create the
|
|
ExampleFilter plugin based on the plugin template and put the output files
|
|
in a new directory called <filename
|
|
class="directory">~/src/examplefilter/</filename>:
|
|
</para>
|
|
<screen>
|
|
<prompt>shell $ </prompt><userinput>cd gst-template</userinput>
|
|
<prompt>shell $ </prompt><userinput>tools/pluginstamp.sh ExampleFilter ~/src</userinput>
|
|
</screen>
|
|
</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(obj) \
|
|
(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"
|
|
|
|
GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);
|
|
<!-- example-end boilerplate.c a --></programlisting>
|
|
</sect1>
|
|
|
|
<!-- ############ sect1 ############# -->
|
|
|
|
<sect1 id="section-boiler-details">
|
|
<title>GstElementDetails</title>
|
|
<para>
|
|
The GstElementDetails structure gives a hierarchical type for the element,
|
|
a human-readable description of the element, as well as author and version
|
|
data. The entries are:
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
A long, english, name for the element.
|
|
</para></listitem><listitem><para>
|
|
The type of the element, as a hierarchy. The hierarchy is defined by
|
|
specifying the top level category, followed by a "/", followed by the
|
|
next level category, etc. The type should be defined according to the
|
|
guidelines elsewhere in this document. (FIXME: write the guidelines, and
|
|
give a better reference to them)
|
|
</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><!-- example-begin boilerplate.c b -->
|
|
static GstElementDetails my_filter_details = {
|
|
"An example plugin",
|
|
"Example/FirstExample",
|
|
"Shows the basic structure of a plugin",
|
|
"your name <your.name@your.isp>"
|
|
};
|
|
<!-- example-end boilerplate.c b --></programlisting>
|
|
<para>
|
|
The element details are registered with the plugin during
|
|
the <function>_base_init ()</function> function, which is part of
|
|
the GObject system. The <function>_base_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_base_init (gpointer klass)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
<!-- example-end boilerplate.c c -->
|
|
static GstElementDetails my_filter_details = {
|
|
[..]
|
|
};
|
|
|
|
[..]<!-- example-begin boilerplate.c d -->
|
|
gst_element_class_set_details (element_class, &my_filter_details);
|
|
<!-- 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>_base_init ()</function> function. Pads are created from these
|
|
templates in the element's <function>_init ()</function> function using
|
|
<function>gst_pad_new_from_template ()</function>. The template can be
|
|
retrieved from the element class using
|
|
<function>gst_element_class_get_pad_template ()</function>. See below
|
|
for more details on this. In order to create a new pad from this
|
|
template using <function>gst_pad_new_from_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_base_init (gpointer 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_class_init (GstMyFilterClass * klass)
|
|
{
|
|
}
|
|
|
|
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 mimetype 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 mimetype, 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>
|
|
static GstStaticPadTemplate sink_factory =
|
|
GST_STATIC_PAD_TEMPLATE (
|
|
"sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (
|
|
"audio/x-raw-int, "
|
|
"width = (int) 16, "
|
|
"depth = (int) 16, "
|
|
"endianness = (int) BYTE_ORDER, "
|
|
"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 three functions which are used for construction of an
|
|
element. These are the <function>_base_init()</function> function which
|
|
is meant to initialize class and child class properties during each new
|
|
child class creation; 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 -->
|
|
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>
|