| <!-- ############ 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</filename> is a |
| good first step at testing a plugin. 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 |
| <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_with_popt_tables ()</function>, 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; intialization. |
| </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 the <classname>efence</classname> element |
| (indeed, an eletric fence memory debugger wrapper element) to check |
| for memory errors. |
| </para> |
| |
| <para> |
| 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. |
| </para> |
| |
| <para> |
| Running the pipeline happens through the <function>gst_bin_iterate ()</function> |
| function. Note that during running, you should connect to at least the |
| <quote>error</quote> and <quote>eos</quote> 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.). |
| </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; |
| GError *err; |
| |
| gst_message_parse_error (msg, &err, &debug); |
| g_free (debug); |
| |
| g_print ("Error: %s\n", err->message); |
| g_error_free (err); |
| |
| g_main_loop_quit (loop); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| gint |
| main (gint argc, |
| gchar *argv[]) |
| { |
| GstElement *pipeline, *filesrc, *decoder, *filter, *sink; |
| GMainLoop *loop; |
| |
| /* 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"); |
| gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (pipeline)), |
| bus_call, loop); |
| |
| filesrc = gst_element_factory_make ("filesrc", "my_filesource"); |
| decoder = gst_element_factory_make ("mad", "my_decoder"); |
| filter = gst_element_factory_make ("my_filter", "my_filter"); |
| 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 (!filter) { |
| g_print ("Your self-written filter could not be found. Make sure it " |
| "is installed correctly in $(libdir)/gstreamer-0.9/ and that " |
| "you've ran gst-register-0.9 to register it. Check availability " |
| "of the plugin afterwards using \"gst-inspect-0.9 my_filter\""); |
| return -1; |
| } |
| |
| g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); |
| |
| /* link everything together */ |
| gst_element_link_many (filesrc, decoder, filter, sink, NULL); |
| gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, filter, sink, NULL); |
| |
| /* run */ |
| gst_element_set_state (pipeline, GST_STATE_PLAYING); |
| g_main_loop_run (loop); |
| |
| /* clean up */ |
| gst_element_set_state (pipeline, GST_STATE_NULL); |
| gst_object_unref (GST_OBJECT (pipeline)); |
| |
| return 0; |
| } |
| <!-- example-end test.c --></programlisting> |
| </chapter> |