Your second application
In a previous chapter we created a first version of the helloworld
application. We then explained a better way of creating the elements
using factories identified by MIME types and the autoplugger.
Autoplugging helloworld
We will create a second version of the helloworld application using
autoplugging. Its source code is a bit more complicated but
it can handle many more data types.
We start by creating the main() of our program:
#include <gst/gst.h>
static void gst_play_have_type (GstElement *typefind, GstCaps *caps, GstElement *pipeline);
static void gst_play_cache_empty (GstElement *element, GstElement *pipeline);
int
main (int argc, char *argv[])
{
GstElement *disksrc;
GstElement *pipeline;
GstElement *autobin;
GstElement *typefind;
GstElement *cache;
gst_init (&argc, &argv);
if (argc != 2) {
g_print ("usage: %s <filename>\n", argv[0]);
exit (-1);
}
/* create a new pipeline to hold the elements */
pipeline = gst_pipeline_new ("pipeline");
g_assert (pipeline != NULL);
/* create a disk reader */
disksrc = gst_elementfactory_make ("disksrc", "disk_source");
g_assert (disksrc != NULL);
g_object_set (G_OBJECT (disksrc), "location", argv[1], NULL);
gst_bin_add (GST_BIN (pipeline), disksrc);
autobin = gst_bin_new ("autobin");
cache = gst_elementfactory_make ("autoplugcache", "cache");
g_signal_connect (G_OBJECT (cache), "cache_empty",
G_CALLBACK (gst_play_cache_empty), pipeline);
typefind = gst_elementfactory_make ("typefind", "typefind");
g_signal_connect (G_OBJECT (typefind), "have_type",
G_CALLBACK (gst_play_have_type), pipeline);
gst_bin_add (GST_BIN (autobin), cache);
gst_bin_add (GST_BIN (autobin), typefind);
gst_element_connect (cache, "src", typefind, "sink");
gst_element_add_ghost_pad (autobin, gst_element_get_pad (cache, "sink"), "sink");
gst_bin_add (GST_BIN( pipeline), autobin);
gst_element_connect (disksrc, "src", autobin, "sink");
/* start playing */
gst_element_set_state( GST_ELEMENT (pipeline), GST_STATE_PLAYING);
while (gst_bin_iterate (GST_BIN (pipeline)));
/* stop the pipeline */
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
exit(0);
}
We start by constructing a 'disksrc' element and an 'autobin' element that
holds the autoplugcache and the typefind element.
We attach the "cache_empty" signal to gst_play_cache_empty and the
"have_type" to our gst_play_have_type function (defined below).
static void
gst_play_have_type (GstElement *typefind, GstCaps *caps, GstElement *pipeline)
{
GstElement *osssink;
GstElement *new_element;
GstAutoplug *autoplug;
GstElement *autobin;
GstElement *disksrc;
GstElement *cache;
GST_DEBUG (0,"GstPipeline: play have type\n");
gst_element_set_state (pipeline, GST_STATE_PAUSED);
disksrc = gst_bin_get_by_name (GST_BIN (pipeline), "disk_source");
autobin = gst_bin_get_by_name (GST_BIN (pipeline), "autobin");
cache = gst_bin_get_by_name (GST_BIN (autobin), "cache");
// disconnect the typefind from the pipeline and remove it
gst_element_disconnect (cache, "src", typefind, "sink");
gst_bin_remove (GST_BIN (autobin), typefind);
/* and an audio sink */
osssink = gst_elementfactory_make("osssink", "play_audio");
g_assert(osssink != NULL);
autoplug = gst_autoplugfactory_make ("staticrender");
g_assert (autoplug != NULL);
new_element = gst_autoplug_to_renderers (autoplug,
caps,
osssink,
NULL);
if (!new_element) {
g_print ("could not autoplug, no suitable codecs found...\n");
exit (-1);
}
gst_element_set_name (new_element, "new_element");
gst_bin_add (GST_BIN (autobin), new_element);
g_object_set (G_OBJECT (cache), "reset", TRUE, NULL);
gst_element_connect (cache, "src", new_element, "sink");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
}
The _have_type function first sets the pipeline to the PAUSED state so that
it can safely modify the pipeline. It then finds the elements it is going to
manipulate in the pipeline with:
disksrc = gst_bin_get_by_name (GST_BIN (pipeline), "disk_source");
autobin = gst_bin_get_by_name (GST_BIN (pipeline), "autobin");
cache = gst_bin_get_by_name (GST_BIN (autobin), "cache");
Now we have a handle to the elements we are going to manipulate in
the next step.
We don't need the typefind element anymore so we remove it from the pipeline:
// disconnect the typefind from the pipeline and remove it
gst_element_disconnect (cache, "src", typefind, "sink");
gst_bin_remove (GST_BIN (autobin), typefind);
Our next step is to construct an element that can play the type we just
detected. We are going to use the autoplugger to create an element that
connects the type to an osssink. We add the new element to our
autobin.
/* and an audio sink */
osssink = gst_elementfactory_make("osssink", "play_audio");
g_assert(osssink != NULL);
autoplug = gst_autoplugfactory_make ("staticrender");
g_assert (autoplug != NULL);
new_element = gst_autoplug_to_renderers (autoplug,
caps,
osssink,
NULL);
if (!new_element) {
g_print ("could not autoplug, no suitable codecs found...\n");
exit (-1);
}
gst_element_set_name (new_element, "new_element");
gst_bin_add (GST_BIN (autobin), new_element);
Our next step is to reset the cache so that the buffers used by the
typefind element are fed into the new element we just created. We reset
the cache by setting the "reset" property of the cache element to TRUE.
g_object_set (G_OBJECT (cache), "reset", TRUE, NULL);
gst_element_connect (cache, "src", new_element, "sink");
Finally we set the pipeline back to the playing state. At this point the
cache will replay the buffers. We will be notified when the cache is empty
with the gst_play_cache_empty callback function:
static void
gst_play_cache_empty (GstElement *element, GstElement *pipeline)
{
GstElement *autobin;
GstElement *disksrc;
GstElement *cache;
GstElement *new_element;
fprintf (stderr, "have cache empty\n");
gst_element_set_state (pipeline, GST_STATE_PAUSED);
disksrc = gst_bin_get_by_name (GST_BIN (pipeline), "disk_source");
autobin = gst_bin_get_by_name (GST_BIN (pipeline), "autobin");
cache = gst_bin_get_by_name (GST_BIN (autobin), "cache");
new_element = gst_bin_get_by_name (GST_BIN (autobin), "new_element");
gst_element_disconnect (disksrc, "src", cache, "sink");
gst_element_disconnect (cache, "src", new_element, "sink");
gst_bin_remove (GST_BIN (autobin), cache);
gst_element_connect (disksrc, "src", new_element, "sink");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
fprintf (stderr, "done with cache_empty\n");
}
The cache empty function simply removes the autoplugcache element from
the pipeline and reconnects the disksrc to the autoplugged element.
To compile the helloworld2 example, use:
gcc -Wall `gstreamer-config --cflags --libs` helloworld2.c \
-o helloworld2
You can run the example with (substitute helloworld.mp3 with you favorite MP3 file):
./helloworld2 helloworld.mp3
You can also try to use an AVI or MPEG file as its input. Using autoplugging,
GStreamer will automatically figure out how to
handle the stream. Remember that only the audio part will be played because
we have only added an osssink to the pipeline.
./helloworld2 mymovie.mpeg