Your first application
This chapter will summarize everything you've learned in the previous
chapters. It describes all aspects of a simple &GStreamer; application,
including initializing libraries, creating elements, packing elements
together in a pipeline and playing this pipeline. By doing all this,
you will be able to build a simple Ogg/Vorbis audio player.
Hello world
We're going to create a simple first application, a simple Ogg/Vorbis
command-line audio player. For this, we will use only standard
&GStreamer; components. The player will read a file specified on
the command-line. Let's get started!
We've learned, in , that the first thing
to do in your application is to initialize &GStreamer; by calling
gst_init (). Also, make sure that the application
includes gst/gst.h so all function names and
objects are properly defined. Use #include
<gst/gst.h> to do that.
Next, you'll want to create the different elements using
gst_element_factory_make (). For an Ogg/Vorbis
audio player, we'll need a source element that reads files from a
disk. &GStreamer; includes this element under the name
filesrc. Next, we'll need something to parse the
file and decoder it into raw audio. &GStreamer; has two elements
for this: the first parses Ogg streams into elementary streams (video,
audio) and is called oggdemux. The second is a Vorbis
audio decoder, it's conveniently called vorbisdec.
Since oggdemux creates dynamic pads for each elementary
stream, you'll need to set a new-pad event handler
on the oggdemux element, like you've learned in
, to link the Ogg parser and
the Vorbis decoder elements together. At last, we'll also need an
audio output element, we will use alsasink, which
outputs sound to an ALSA audio device.
The last thing left to do is to add all elements into a container
element, a GstPipeline, and iterate this
pipeline until we've played the whole song. We've previously
learned how to add elements to a container bin in , and we've learned about element states
in . We will also attach
a message handler to the pipeline bus so we can retrieve errors
and detect the end-of-stream.
Let's now add all the code together to get our very first audio
player:
#include <gst/gst.h>
/*
* Global objects are usually a bad thing. For the purpose of this
* example, we will use them, however.
*/
GstElement *pipeline, *source, *parser, *decoder, *conv, *sink;
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;
}
static void
new_pad (GstElement *element,
GstPad *pad,
gpointer data)
{
/* We can now link this pad with the audio decoder */
g_print ("Dynamic pad created, linking parser/decoder\n");
gst_pad_link (pad, gst_element_get_pad (decoder, "sink"));
}
int
main (int argc,
char *argv[])
{
GMainLoop *loop;
/* initialize GStreamer */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* check input arguments */
if (argc != 2) {
g_print ("Usage: %s <Ogg/Vorbis filename>\n", argv[0]);
return -1;
}
/* create elements */
pipeline = gst_pipeline_new ("audio-player");
source = gst_element_factory_make ("filesrc", "file-source");
parser = gst_element_factory_make ("oggdemux", "ogg-parser");
decoder = gst_element_factory_make ("vorbisdec", "vorbis-decoder");
conv = gst_element_factory_make ("audioconvert", "converter");
sink = gst_element_factory_make ("alsasink", "alsa-output");
if (!pipeline || !source || !parser || !decoder || !conv || !sink) {
g_print ("One element could not be created\n");
return -1;
}
/* set filename property on the file source. Also add a message
* handler. */
g_object_set (G_OBJECT (source), "location", argv[1], NULL);
gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (pipeline)),
bus_call, loop);
/* link together - note that we cannot link the parser and
* decoder yet, becuse the parser uses dynamic pads. For that,
* we set a new-pad signal handler. */
gst_element_link (source, parser);
gst_element_link_many (decoder, conv, sink, NULL);
g_signal_connect (parser, "new-pad", G_CALLBACK (new_pad), NULL);
/* put all elements in a bin */
gst_bin_add_many (GST_BIN (pipeline),
source, parser, decoder, conv, sink, NULL);
/* Now set to playing and iterate. */
g_print ("Setting to PLAYING\n");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print ("Running\n");
g_main_loop_run (loop);
/* clean up nicely */
g_print ("Returned, stopping playback\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
g_print ("Deleting pipeline\n");
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}
We now have created a complete pipeline. We can visualise the
pipeline as follows:
Compiling and Running helloworld.c
To compile the helloworld example, use: gcc -Wall
$(pkg-config --cflags --libs gstreamer-&GST_MAJORMINOR;)
helloworld.c -o helloworld. &GStreamer; makes use of
pkg-config to get compiler and linker flags
needed to compile this application. If you're running a
non-standard installation, make sure the
PKG_CONFIG_PATH environment variable is
set to the correct location ($libdir/pkgconfig).
application against the uninstalled location.
You can run this example application with ./helloworld
file.ogg. Substitute file.ogg
with your favourite Ogg/Vorbis file.
Conclusion
This concludes our first example. As you see, setting up a pipeline
is very low-level but powerful. You will see later in this manual how
you can create a more powerful media player with even less effort
using higher-level interfaces. We will discuss all that in . We will first, however, go more in-depth
into more advanced &GStreamer; internals.
It should be clear from the example that we can very easily replace
the filesrc element with some other element that
reads data from a network, or some other data source element that
is better integrated with your desktop environment. Also, you can
use other decoders and parsers to support other media types. You
can use another audio sink if you're not running Linux, but Mac OS X,
Windows or FreeBSD, or you can instead use a filesink to write audio
files to disk instead of playing them back. By using an audio card
source, you can even do audio capture instead of playback. All this
shows the reusability of &GStreamer; elements, which is its greatest
advantage.