This commit is contained in:
Akkariin Meiko
2022-03-12 03:16:09 +08:00
Unverified
parent 12b76e0c7a
commit 27c4ec74a1
10075 changed files with 5122287 additions and 1 deletions
@@ -0,0 +1,153 @@
---
title: Adding Properties
...
# 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.
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.
``` c
/* 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.
``` c
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));
[..]
}
```
@@ -0,0 +1,373 @@
---
title: Constructing the Boilerplate
...
# 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. 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.
## Getting the GStreamer Plugin Templates
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, “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:
```
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.
If for some reason you can't access the git repository, you can also
[download a snapshot of the latest
revision](https://gitlab.freedesktop.org/gstreamer/gst-template)
via the gitlab web interface.
## 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.
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.
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:
```
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`.
> **Note**
>
> It is recommended that you create a copy of the `gst-plugin` directory
> before continuing.
Now one needs to 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.
> **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.
> **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.
## 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.)
``` c
#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:
``` c
#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)`.
## 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:
- 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.
- 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:
``` c
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.
``` c
static void
gst_my_filter_class_init (GstMyFilterClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
[..]
gst_element_class_set_static_metadata (element_class,
"An example plugin",
"Example/FirstExample",
"Shows the basic structure of a plugin",
"your name <your.name@your.isp>");
}
```
## GstStaticPadTemplate
A GstStaticPadTemplate is a description of a pad that the element will
(or might) create and use. It contains:
- 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).
- Supported types by this element (capabilities).
For example:
``` c
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.
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][pads].
```c
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. 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:
``` c
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]: plugin-development/basics/pads.md
## 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.
## 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.
``` c
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.
@@ -0,0 +1,114 @@
---
title: The chain function
...
# 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:
``` c
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.
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.).
```c
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.
@@ -0,0 +1,70 @@
---
title: The event function
...
# 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.
Below follows a very simple event function that we install on the sink
pad of our element.
``` c
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.
@@ -0,0 +1,23 @@
---
title: The Basics of Writing a Plugin
...
# 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. 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”.
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. 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][advanced],
you will be able to add even more functionality to your plugins.
[advanced]: plugin-development/advanced/index.md
@@ -0,0 +1,55 @@
---
title: Specifying the pads
...
# 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.
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:
``` c
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;
}
```
@@ -0,0 +1,69 @@
---
title: The query function
...
# 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.
Below follows a very simple query function that we install on the source
pad of our element.
``` c
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.
@@ -0,0 +1,13 @@
---
title: Signals
...
# 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](http://library.gnome.org/devel/gobject/stable/)
for all internals about signals.
@@ -0,0 +1,139 @@
---
title: What are states?
...
# 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:
- `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”.
`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_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). 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.
`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_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.
## Managing filter state
If at all possible, your element should derive from one of the new base
classes ([Pre-made base classes](plugin-development/element-types/base-classes.md)). 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.
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.
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.
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.
```c
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.
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.
@@ -0,0 +1,202 @@
---
title: Building a Test Application
...
# 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. 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. 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](application-development/index.md).
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.
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. 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.
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.
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.).
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.
``` c
#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;
}
```