mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 02:30:35 +00:00
216 lines
7.9 KiB
XML
216 lines
7.9 KiB
XML
<!-- ############ chapter ############# -->
|
|
|
|
<chapter id="chapter-building-testapp">
|
|
<title>Building a Test Application</title>
|
|
<para>
|
|
Often, you will want to test your newly written plugin in an as small
|
|
setting as possible. Usually, <filename>gst-launch-1.0</filename> 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
|
|
<command>
|
|
gst-launch-1.0 --gst-plugin-path=$HOME/gst-template/gst-plugin/src/.libs TESTPIPELINE
|
|
</command>
|
|
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
|
|
<ulink type="http" url="../../manual/html/index.html">Application Development
|
|
Manual</ulink>.
|
|
</para>
|
|
|
|
<para>
|
|
At the start, you need to initialize the &GStreamer; core library by
|
|
calling <function>gst_init ()</function>. You can alternatively call
|
|
<function>gst_init_get_option_group ()</function>, which will return
|
|
a pointer to GOptionGroup. You can then use GOption to handle the
|
|
initialization, and this will finish the &GStreamer; initialization.
|
|
</para>
|
|
|
|
<para>
|
|
You can create elements using <function>gst_element_factory_make ()</function>,
|
|
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
|
|
<classname>identity</classname> 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 <classname>fakesink</classname>
|
|
element at the end of the pipeline to dump your data to the stdout
|
|
(in order to do this, set the <function>dump</function> property to
|
|
TRUE). Lastly, you can use valgrind to check for memory errors.
|
|
</para>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
Note that during running, you should listen for at least the
|
|
<quote>error</quote> and <quote>eos</quote> 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.).
|
|
</para>
|
|
|
|
<para>
|
|
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 <function>unref ()</function>
|
|
the pipeline and make sure it doesn't crash.
|
|
</para>
|
|
|
|
<programlisting><!-- example-begin test.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;
|
|
}
|
|
<!-- example-end test.c --></programlisting>
|
|
</chapter>
|