2004-12-15 17:32:49 +00:00
|
|
|
<chapter id="chapter-helloworld">
|
2004-12-15 07:30:55 +00:00
|
|
|
<title>Your first application</title>
|
2004-12-15 17:32:49 +00:00
|
|
|
<para>
|
|
|
|
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.
|
2004-12-15 07:30:55 +00:00
|
|
|
</para>
|
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
<sect1 id="section-helloworld">
|
2004-12-15 07:30:55 +00:00
|
|
|
<title>Hello world</title>
|
|
|
|
<para>
|
2004-12-15 17:32:49 +00:00
|
|
|
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!
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
We've learned, in <xref linkend="chapter-init"/>, that the first thing
|
|
|
|
to do in your application is to initialize &GStreamer; by calling
|
|
|
|
<function>gst_init ()</function>. Also, make sure that the application
|
|
|
|
includes <filename>gst/gst.h</filename> so all function names and
|
|
|
|
objects are properly defined. Use <function>#include
|
|
|
|
<gst/gst.h></function> to do that.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Next, you'll want to create the different elements using
|
|
|
|
<function>gst_element_factory_make ()</function>. 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
|
|
|
|
<quote>filesrc</quote>. 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 <quote>oggdemux</quote>. The second is a Vorbis
|
|
|
|
audio decoder, it's conveniently called <quote>vorbisdec</quote>.
|
|
|
|
Since <quote>oggdemux</quote> creates dynamic pads for each elementary
|
GstElement::new-pad -> pad-added, GstElement::state-change -> state-changed, GstValueFixedList -> GstValueArray, add ...
Original commit message from CVS:
* docs/manual/advanced-autoplugging.xml:
* docs/manual/basics-helloworld.xml:
* docs/manual/basics-pads.xml:
* docs/random/ds/0.9-suggested-changes:
* gst/gstelement.c: (gst_element_class_init), (gst_element_seek):
* gst/gstelement.h:
* gst/gstevent.h:
* gst/gstformat.h:
* gst/gstquery.h:
* gst/gststructure.c: (gst_structure_value_get_generic_type),
(gst_structure_parse_array), (gst_structure_parse_value):
* gst/gstvalue.c: (gst_type_is_fixed),
(gst_value_list_prepend_value), (gst_value_list_append_value),
(gst_value_list_get_size), (gst_value_list_get_value),
(gst_value_transform_array_string), (gst_value_serialize_array),
(gst_value_deserialize_array), (gst_value_intersect_array),
(gst_value_is_fixed), (_gst_value_initialize):
* gst/gstvalue.h:
GstElement::new-pad -> pad-added, GstElement::state-change ->
state-changed, GstValueFixedList -> GstValueArray, add format and
flags as their own arguments in gst_element_seek() (should improve
"bindeability"), remove function generators since they don't work
under a whole bunch of compilers (they were deprecated already
anyway).
2005-07-20 17:16:44 +00:00
|
|
|
stream, you'll need to set a <quote>pad-added</quote> event handler
|
2004-12-15 17:32:49 +00:00
|
|
|
on the <quote>oggdemux</quote> element, like you've learned in
|
|
|
|
<xref linkend="section-pads-dynamic"/>, to link the Ogg parser and
|
|
|
|
the Vorbis decoder elements together. At last, we'll also need an
|
|
|
|
audio output element, we will use <quote>alsasink</quote>, which
|
|
|
|
outputs sound to an ALSA audio device.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
The last thing left to do is to add all elements into a container
|
|
|
|
element, a <classname>GstPipeline</classname>, and iterate this
|
|
|
|
pipeline until we've played the whole song. We've previously
|
|
|
|
learned how to add elements to a container bin in <xref
|
|
|
|
linkend="chapter-bins"/>, and we've learned about element states
|
2005-06-29 09:25:51 +00:00
|
|
|
in <xref linkend="section-elements-states"/>. We will also attach
|
|
|
|
a message handler to the pipeline bus so we can retrieve errors
|
|
|
|
and detect the end-of-stream.
|
2004-12-15 17:32:49 +00:00
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Let's now add all the code together to get our very first audio
|
|
|
|
player:
|
2004-12-15 07:30:55 +00:00
|
|
|
</para>
|
|
|
|
<programlisting>
|
2004-12-15 17:32:49 +00:00
|
|
|
<!-- example-begin helloworld.c -->
|
|
|
|
#include <gst/gst.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Global objects are usually a bad thing. For the purpose of this
|
|
|
|
* example, we will use them, however.
|
|
|
|
*/
|
|
|
|
|
2005-06-29 09:25:51 +00:00
|
|
|
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;
|
|
|
|
}
|
2004-12-15 07:30:55 +00:00
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
static void
|
|
|
|
new_pad (GstElement *element,
|
|
|
|
GstPad *pad,
|
|
|
|
gpointer data)
|
2004-12-15 07:30:55 +00:00
|
|
|
{
|
2005-11-24 14:39:59 +00:00
|
|
|
GstPad *sinkpad;
|
2005-06-29 09:25:51 +00:00
|
|
|
/* We can now link this pad with the audio decoder */
|
|
|
|
g_print ("Dynamic pad created, linking parser/decoder\n");
|
2005-11-24 14:39:59 +00:00
|
|
|
|
|
|
|
sinkpad = gst_element_get_pad (decoder, "sink"));
|
|
|
|
gst_pad_link (pad, sinkpad);
|
|
|
|
|
|
|
|
gst_object_unref (sinkpad);
|
2004-12-15 17:32:49 +00:00
|
|
|
}
|
2004-12-15 07:30:55 +00:00
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
int
|
|
|
|
main (int argc,
|
|
|
|
char *argv[])
|
|
|
|
{
|
2005-06-29 09:25:51 +00:00
|
|
|
GMainLoop *loop;
|
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
/* initialize GStreamer */
|
|
|
|
gst_init (&argc, &argv);
|
2005-06-29 09:25:51 +00:00
|
|
|
loop = g_main_loop_new (NULL, FALSE);
|
2004-12-15 07:30:55 +00:00
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
/* check input arguments */
|
2004-12-15 07:30:55 +00:00
|
|
|
if (argc != 2) {
|
2004-12-15 17:32:49 +00:00
|
|
|
g_print ("Usage: %s <Ogg/Vorbis filename>\n", argv[0]);
|
|
|
|
return -1;
|
2004-12-15 07:30:55 +00:00
|
|
|
}
|
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
/* 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");
|
2005-06-29 09:25:51 +00:00
|
|
|
conv = gst_element_factory_make ("audioconvert", "converter");
|
2004-12-15 17:32:49 +00:00
|
|
|
sink = gst_element_factory_make ("alsasink", "alsa-output");
|
2005-06-29 09:25:51 +00:00
|
|
|
if (!pipeline || !source || !parser || !decoder || !conv || !sink) {
|
|
|
|
g_print ("One element could not be created\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2004-12-15 17:32:49 +00:00
|
|
|
|
2005-06-29 09:25:51 +00:00
|
|
|
/* set filename property on the file source. Also add a message
|
|
|
|
* handler. */
|
2004-12-15 17:32:49 +00:00
|
|
|
g_object_set (G_OBJECT (source), "location", argv[1], NULL);
|
2005-06-29 09:25:51 +00:00
|
|
|
gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (pipeline)),
|
|
|
|
bus_call, loop);
|
2004-12-15 17:32:49 +00:00
|
|
|
|
2005-08-16 12:15:46 +00:00
|
|
|
/* put all elements in a bin */
|
|
|
|
gst_bin_add_many (GST_BIN (pipeline),
|
|
|
|
source, parser, decoder, conv, sink, NULL);
|
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
/* link together - note that we cannot link the parser and
|
|
|
|
* decoder yet, becuse the parser uses dynamic pads. For that,
|
GstElement::new-pad -> pad-added, GstElement::state-change -> state-changed, GstValueFixedList -> GstValueArray, add ...
Original commit message from CVS:
* docs/manual/advanced-autoplugging.xml:
* docs/manual/basics-helloworld.xml:
* docs/manual/basics-pads.xml:
* docs/random/ds/0.9-suggested-changes:
* gst/gstelement.c: (gst_element_class_init), (gst_element_seek):
* gst/gstelement.h:
* gst/gstevent.h:
* gst/gstformat.h:
* gst/gstquery.h:
* gst/gststructure.c: (gst_structure_value_get_generic_type),
(gst_structure_parse_array), (gst_structure_parse_value):
* gst/gstvalue.c: (gst_type_is_fixed),
(gst_value_list_prepend_value), (gst_value_list_append_value),
(gst_value_list_get_size), (gst_value_list_get_value),
(gst_value_transform_array_string), (gst_value_serialize_array),
(gst_value_deserialize_array), (gst_value_intersect_array),
(gst_value_is_fixed), (_gst_value_initialize):
* gst/gstvalue.h:
GstElement::new-pad -> pad-added, GstElement::state-change ->
state-changed, GstValueFixedList -> GstValueArray, add format and
flags as their own arguments in gst_element_seek() (should improve
"bindeability"), remove function generators since they don't work
under a whole bunch of compilers (they were deprecated already
anyway).
2005-07-20 17:16:44 +00:00
|
|
|
* we set a pad-added signal handler. */
|
2004-12-15 17:32:49 +00:00
|
|
|
gst_element_link (source, parser);
|
2005-06-29 09:25:51 +00:00
|
|
|
gst_element_link_many (decoder, conv, sink, NULL);
|
GstElement::new-pad -> pad-added, GstElement::state-change -> state-changed, GstValueFixedList -> GstValueArray, add ...
Original commit message from CVS:
* docs/manual/advanced-autoplugging.xml:
* docs/manual/basics-helloworld.xml:
* docs/manual/basics-pads.xml:
* docs/random/ds/0.9-suggested-changes:
* gst/gstelement.c: (gst_element_class_init), (gst_element_seek):
* gst/gstelement.h:
* gst/gstevent.h:
* gst/gstformat.h:
* gst/gstquery.h:
* gst/gststructure.c: (gst_structure_value_get_generic_type),
(gst_structure_parse_array), (gst_structure_parse_value):
* gst/gstvalue.c: (gst_type_is_fixed),
(gst_value_list_prepend_value), (gst_value_list_append_value),
(gst_value_list_get_size), (gst_value_list_get_value),
(gst_value_transform_array_string), (gst_value_serialize_array),
(gst_value_deserialize_array), (gst_value_intersect_array),
(gst_value_is_fixed), (_gst_value_initialize):
* gst/gstvalue.h:
GstElement::new-pad -> pad-added, GstElement::state-change ->
state-changed, GstValueFixedList -> GstValueArray, add format and
flags as their own arguments in gst_element_seek() (should improve
"bindeability"), remove function generators since they don't work
under a whole bunch of compilers (they were deprecated already
anyway).
2005-07-20 17:16:44 +00:00
|
|
|
g_signal_connect (parser, "pad-added", G_CALLBACK (new_pad), NULL);
|
2004-12-15 17:32:49 +00:00
|
|
|
|
2005-06-29 09:25:51 +00:00
|
|
|
/* Now set to playing and iterate. */
|
|
|
|
g_print ("Setting to PLAYING\n");
|
2004-12-15 07:30:55 +00:00
|
|
|
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
2005-06-29 09:25:51 +00:00
|
|
|
g_print ("Running\n");
|
|
|
|
g_main_loop_run (loop);
|
2004-12-15 07:30:55 +00:00
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
/* clean up nicely */
|
2005-06-29 09:25:51 +00:00
|
|
|
g_print ("Returned, stopping playback\n");
|
2004-12-15 07:30:55 +00:00
|
|
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
2005-06-29 09:25:51 +00:00
|
|
|
g_print ("Deleting pipeline\n");
|
2004-12-15 07:30:55 +00:00
|
|
|
gst_object_unref (GST_OBJECT (pipeline));
|
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
return 0;
|
2004-12-15 07:30:55 +00:00
|
|
|
}
|
2004-12-15 17:32:49 +00:00
|
|
|
<!-- example-end helloworld.c -->
|
2004-12-15 07:45:16 +00:00
|
|
|
</programlisting>
|
2004-12-15 17:32:49 +00:00
|
|
|
<!-- FIXME: this image needs updating -->
|
2004-12-15 07:30:55 +00:00
|
|
|
<para>
|
|
|
|
We now have created a complete pipeline. We can visualise the
|
|
|
|
pipeline as follows:
|
|
|
|
</para>
|
|
|
|
<figure float="1" id="section-hello-img">
|
|
|
|
<title>The "hello world" pipeline</title>
|
|
|
|
<mediaobject>
|
|
|
|
<imageobject>
|
|
|
|
<imagedata fileref="images/hello-world.ℑ" format="&IMAGE;" />
|
|
|
|
</imageobject>
|
|
|
|
</mediaobject>
|
|
|
|
</figure>
|
|
|
|
</sect1>
|
|
|
|
|
2004-12-15 17:32:49 +00:00
|
|
|
<sect1 id="section-helloworld-compilerun">
|
|
|
|
<title>Compiling and Running helloworld.c</title>
|
|
|
|
<para>
|
|
|
|
To compile the helloworld example, use: <command>gcc -Wall
|
|
|
|
$(pkg-config --cflags --libs gstreamer-&GST_MAJORMINOR;)
|
|
|
|
helloworld.c -o helloworld</command>. &GStreamer; makes use of
|
|
|
|
<command>pkg-config</command> to get compiler and linker flags
|
|
|
|
needed to compile this application. If you're running a
|
|
|
|
non-standard installation, make sure the
|
|
|
|
<classname>PKG_CONFIG_PATH</classname> environment variable is
|
|
|
|
set to the correct location (<filename>$libdir/pkgconfig</filename>).
|
2004-12-15 07:30:55 +00:00
|
|
|
application against the uninstalled location.
|
|
|
|
</para>
|
|
|
|
<para>
|
2004-12-15 17:32:49 +00:00
|
|
|
You can run this example application with <command>./helloworld
|
|
|
|
file.ogg</command>. Substitute <filename>file.ogg</filename>
|
|
|
|
with your favourite Ogg/Vorbis file.
|
2004-12-15 07:30:55 +00:00
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<sect1 id="section-hello-world-conclusion">
|
|
|
|
<title>Conclusion</title>
|
|
|
|
<para>
|
|
|
|
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
|
2004-12-15 17:32:49 +00:00
|
|
|
you can create a more powerful media player with even less effort
|
|
|
|
using higher-level interfaces. We will discuss all that in <xref
|
|
|
|
linkend="part-highlevel"/>. We will first, however, go more in-depth
|
|
|
|
into more advanced &GStreamer; internals.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
It should be clear from the example that we can very easily replace
|
|
|
|
the <quote>filesrc</quote> 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.
|
2004-12-15 07:30:55 +00:00
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|