Building a Test Application
Often, you will want to test your newly written plugin in an as small
setting as possible. Usually, gst-launch 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 --gst-plugin-path=$HOME/gst-template/gst-plugin/src/.libs TESTPIPELINE
However, you will often need more
testing features than gst-launch 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.
At the start, you need to initialize the &GStreamer; core library by
calling gst_init (). You can alternatively call
gst_init_with_popt_tables (), which will return
a pointer to popt tables. You can then use libpopt to handle the
given argument table, 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 the efence element
(indeed, an eletric fence memory debugger wrapper element) to check
for memory errors.
During linking, your test application can use fixation or 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.
Running the pipeline happens through the gst_bin_iterate ()
function. Note that during running, you should connect to at least the
error
and eos
signals on the pipeline
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;
/* 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));
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 ("osssink", "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-0.10/ or "
"~/.gstreamer-0.10/plugins/ and that gst-inspect-0.10 lists it. "
"If it doesn't, check with 'GST_DEBUG=*:2 gst-inspect-0.10' 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);
return 0;
}