Constructing the Boilerplate
In this chapter you will learn how to construct the bare minimum code for a new plugin. you will learn how to use a few basic tools to copy and modify a template plugin to create a new plugin.
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.
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.
You can also download a snapshot of the latest revision 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:
-
the name of the plugin, and
-
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 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.
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
toGST_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.)
#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);
Using this header file, you can use the following macro to setup the GObject
basics in your source file so that all functions will be called appropriately:
#include "filter.h" G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
Element metadata
The Element metadata provides extra element information. It is configured withgst_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:
gst_element_class_set_static_metadata (klass, "An example plugin", "Example/FirstExample", "Shows the basic structure of a plugin", "your name <[email protected]>");
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.
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 <[email protected]>"); }
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:
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 thegst_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 usinggst_pad_new_from_static_template ()
. In order to create a new pad from this template usinggst_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.
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:
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.
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.
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/" )
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.
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:
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.).
tatic 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); }
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.
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.
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.
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.
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). 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.
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.
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.
/* 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.
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)); [..] }
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.
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.
#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; }