2000-08-18 20:38:54 +00:00
|
|
|
<chapter id="cha-hello">
|
|
|
|
<title>Your first application</title>
|
|
|
|
<para>
|
|
|
|
This chapter describes the most rudimentary aspects of a GStreamer application,
|
|
|
|
including initializing the libraries, creating elements, packing them into
|
|
|
|
a pipeline and playing, pause and stop the pipeline.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<sect1>
|
|
|
|
<title>Hello world</title>
|
|
|
|
<para>
|
|
|
|
We will create a simple first application. In fact it will be a complete
|
|
|
|
MP3 player, using standard GStreamer components. The player will read from
|
|
|
|
a file that is given as the first argument of the program.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
|
|
|
|
#include <gst/gst.h>
|
|
|
|
|
|
|
|
gboolean playing;
|
|
|
|
|
|
|
|
/* eos will be called when the src element has an end of stream */
|
|
|
|
void eos(GstSrc *src)
|
|
|
|
{
|
|
|
|
g_print("have eos, quitting\n");
|
|
|
|
|
|
|
|
playing = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc,char *argv[])
|
|
|
|
{
|
|
|
|
GstElement *bin, *disksrc, *parse, *decoder, *audiosink;
|
|
|
|
|
|
|
|
if (argc != 2) {
|
|
|
|
g_print("usage: %s <filename>n", argv[0]);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_init(&argc,&argv);
|
|
|
|
gst_plugin_load_all();
|
|
|
|
g_print("\n");
|
|
|
|
|
|
|
|
/* create a new bin to hold the elements */
|
|
|
|
bin = gst_bin_new("bin");
|
|
|
|
|
|
|
|
/* create a disk reader */
|
|
|
|
disksrc = gst_elementfactory_make("disksrc", "disk_source");
|
|
|
|
gtk_object_set(GTK_OBJECT(disksrc),"location", argv[1],NULL);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(disksrc),"eos",
|
|
|
|
GTK_SIGNAL_FUNC(eos),NULL);
|
|
|
|
|
|
|
|
/* now it's time to get the parser */
|
|
|
|
parse = gst_elementfactory_make("mp3parse","parse");
|
|
|
|
decoder = gst_elementfactory_make("mpg123","decoder");
|
|
|
|
/* and an audio sink */
|
|
|
|
audiosink = gst_elementfactory_make("audiosink", "play_audio");
|
|
|
|
|
|
|
|
/* add objects to the main pipeline */
|
|
|
|
gst_bin_add(GST_BIN(bin), disksrc);
|
|
|
|
gst_bin_add(GST_BIN(bin), parse);
|
|
|
|
gst_bin_add(GST_BIN(bin), decoder);
|
|
|
|
gst_bin_add(GST_BIN(bin), audiosink);
|
|
|
|
|
|
|
|
/* connect src to sink */
|
|
|
|
gst_pad_connect(gst_element_get_pad(disksrc,"src"),
|
|
|
|
gst_element_get_pad(parse,"sink"));
|
|
|
|
gst_pad_connect(gst_element_get_pad(parse,"src"),
|
|
|
|
gst_element_get_pad(decoder,"sink"));
|
|
|
|
gst_pad_connect(gst_element_get_pad(decoder,"src"),
|
|
|
|
gst_element_get_pad(audiosink,"sink"));
|
|
|
|
|
|
|
|
/* find out how to handle this bin */
|
|
|
|
gst_bin_create_plan(GST_BIN(bin));
|
|
|
|
|
|
|
|
/* make it ready */
|
|
|
|
gst_element_set_state(bin, GST_STATE_READY);
|
|
|
|
/* start playing */
|
|
|
|
gst_element_set_state(bin, GST_STATE_PLAYING);
|
|
|
|
|
|
|
|
playing = TRUE;
|
|
|
|
|
|
|
|
while (playing) {
|
|
|
|
gst_bin_iterate(GST_BIN(bin));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* stop the bin */
|
|
|
|
gst_element_set_state(bin, GST_STATE_NULL);
|
|
|
|
|
|
|
|
gst_object_destroy(GST_OBJECT(audiosink));
|
|
|
|
gst_object_destroy(GST_OBJECT(decoder));
|
|
|
|
gst_object_destroy(GST_OBJECT(disksrc));
|
|
|
|
gst_object_destroy(GST_OBJECT(bin));
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Let's go through this example step by step.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The first thing you have to do is to include the standard GStreamer headers and
|
|
|
|
initialize the framework.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
|
|
|
|
#include <gst/gst.h>
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
int main(int argc,char *argv[])
|
|
|
|
{
|
|
|
|
...
|
|
|
|
gst_init(&argc,&argv);
|
|
|
|
...
|
|
|
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
For simplicity, we are going to load all known plugins. This has the effect
|
|
|
|
that all the codecs known to GStreamer are registered to the system.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
...
|
|
|
|
gst_plugin_load_all();
|
|
|
|
...
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We are going to create 4 elements and one bin. Since all objects are
|
|
|
|
in fact elements, we can define them as:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
...
|
|
|
|
GstElement *bin, *disksrc, *parse, *decoder, *audiosink;
|
|
|
|
...
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Next, we are going to create an empty bin. As you have seen in the basic
|
|
|
|
introduction, this bin will hold and manage all the elements we are
|
|
|
|
going to stuff into it.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* create a new bin to hold the elements */
|
|
|
|
bin = gst_bin_new("bin");
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We then create a disk source element. The disk source element is able to
|
|
|
|
read from a file. We use the standard GTK+ argument mechanism to set
|
|
|
|
a property of the element: the file to read from.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* create a disk reader */
|
|
|
|
disksrc = gst_elementfactory_make("disksrc", "disk_source");
|
|
|
|
gtk_object_set(GTK_OBJECT(disksrc),"location", argv[1],NULL);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(disksrc),"eos",
|
|
|
|
GTK_SIGNAL_FUNC(eos),NULL);
|
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
We also connected the eos signal to our function. When the
|
|
|
|
disk source has reached an end-of-stream, this function will be called.
|
|
|
|
We will use it to set a gboolean value to FALSE;
|
|
|
|
</para>
|
|
|
|
<note>
|
|
|
|
<para>
|
|
|
|
You can check if the disksrc != NULL to verify the creation of the
|
|
|
|
disk source element.
|
|
|
|
</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
gboolean playing;
|
|
|
|
...
|
|
|
|
|
|
|
|
/* eos will be called when the src element has an end of stream */
|
|
|
|
void eos(GstSrc *src)
|
|
|
|
{
|
|
|
|
g_print("have eos, quitting\n");
|
|
|
|
|
|
|
|
playing = FALSE;
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We now create the MP3 decoder element. GStreamer requires you
|
|
|
|
to put a parser in front of the decoder. This parser will
|
|
|
|
cut the raw data from the disk source into MP3 frames
|
|
|
|
suitable for the decoder. In the advanced concepts chapter we will
|
|
|
|
see how this can be avoided.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* now it's time to get the parser */
|
|
|
|
parse = gst_elementfactory_make("mp3parse","parse");
|
|
|
|
decoder = gst_elementfactory_make("mpg123","decoder");
|
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
gst_elementfactory_make() takes two arguments: a string that will
|
|
|
|
identify the element you need and a second argument: how you want
|
|
|
|
to name the element. The name of the element is something you can
|
|
|
|
choose yourself and might be used to retrieve the element from a
|
|
|
|
bin.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Finally we create our audio sink element. This element will be able
|
|
|
|
to playback the audio using OSS.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* and an audio sink */
|
|
|
|
audiosink = gst_elementfactory_make("audiosink", "play_audio");
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We then add the elements to the bin.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* add objects to the main pipeline */
|
|
|
|
gst_bin_add(GST_BIN(bin), disksrc);
|
|
|
|
gst_bin_add(GST_BIN(bin), parse);
|
|
|
|
gst_bin_add(GST_BIN(bin), decoder);
|
|
|
|
gst_bin_add(GST_BIN(bin), audiosink);
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We connect the different pads of the elements together like this:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* connect src to sink */
|
|
|
|
gst_pad_connect(gst_element_get_pad(disksrc,"src"),
|
|
|
|
gst_element_get_pad(parse,"sink"));
|
|
|
|
gst_pad_connect(gst_element_get_pad(parse,"src"),
|
|
|
|
gst_element_get_pad(decoder,"sink"));
|
|
|
|
gst_pad_connect(gst_element_get_pad(decoder,"src"),
|
|
|
|
gst_element_get_pad(audiosink,"sink"));
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We now have a created a complete pipeline. We can visualise the
|
|
|
|
pipeline as follows:
|
|
|
|
</para>
|
|
|
|
<figure float="1" id="sec-hello-img">
|
|
|
|
<title>The Hello world pipeline</title>
|
|
|
|
<graphic fileref="images/hello-world" format="png"></graphic>
|
|
|
|
</figure>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Before we can run it, we have to instruct the bin to analyse its contents
|
|
|
|
and come up with a plan to handle the media streams.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* find out how to handle this bin */
|
|
|
|
gst_bin_create_plan(GST_BIN(bin));
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Everything is now set up to start the streaming. We use the following
|
|
|
|
statements to change the state of the bin:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* make it ready */
|
|
|
|
gst_element_set_state(bin, GST_STATE_READY);
|
|
|
|
/* start playing */
|
|
|
|
gst_element_set_state(bin, GST_STATE_PLAYING);
|
|
|
|
|
|
|
|
playing = TRUE;
|
|
|
|
</programlisting>
|
|
|
|
<note>
|
|
|
|
<para>
|
|
|
|
Before you set the bin to the PLAYING state, you must go through the
|
|
|
|
READY state. The READY state will, in this example, open the file, open
|
|
|
|
the audio device and initialise the MP3 decoder.
|
|
|
|
</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Since we do not use threads, nothing will happen yet. We manually have to
|
|
|
|
call gst_bin_iterate() to execute one iteration of the bin.
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
while (playing) {
|
|
|
|
gst_bin_iterate(GST_BIN(bin));
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
Remember that the variable playing will become false if the disk source
|
|
|
|
has reached an end-of-file. When that happens, the follwing code takes
|
|
|
|
care of the cleanup:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
/* stop the bin */
|
|
|
|
gst_element_set_state(bin, GST_STATE_NULL);
|
|
|
|
|
|
|
|
gst_object_destroy(GST_OBJECT(audiosink));
|
|
|
|
gst_object_destroy(GST_OBJECT(decoder));
|
|
|
|
gst_object_destroy(GST_OBJECT(disksrc));
|
|
|
|
gst_object_destroy(GST_OBJECT(bin));
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
</programlisting>
|
|
|
|
<note>
|
|
|
|
<para>
|
|
|
|
don't forget to set the state of the bin to NULL. This will free
|
|
|
|
all of the resources held by the elements.
|
|
|
|
</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<sect1>
|
|
|
|
<title>compiling helloworld.c</title>
|
|
|
|
<para>
|
|
|
|
To compile the helloworld example, use:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
gcc -Wall `gstreamer-config --cflags` `gtk-config --cflags` helloworld.c \
|
|
|
|
-o helloworld `gstreamer-config --libs` `gtk-config --libs`
|
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
This uses the program gstreamer-config, which comes with GStreamer. This program "knows"
|
|
|
|
what compiler switches are needed to compile programs that use GStreamer.
|
|
|
|
gstreamer-config --cflags will output a list of include
|
|
|
|
directories for the compiler to look in, and gstreamer-config --libs will output the
|
|
|
|
list of libraries for the compiler to link with and the directories to find them
|
|
|
|
in.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
You can run the example with (substitute helloworld.mp3 with you favorite MP3 file):
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
./helloworld helloworld.mp3
|
|
|
|
</programlisting>
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<sect1>
|
|
|
|
<title>conclusion</title>
|
|
|
|
<para>
|
|
|
|
This concludes our first example. As you see, setting up a pipeline
|
|
|
|
is very lowlevel but powerfull. You will later in this manual how
|
|
|
|
you can create a custom MP3 element with a more high level API.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
It should be clear from the example that we can vary easily replace the
|
|
|
|
disksrc element with a httpsrc, giving you instant network streaming.
|
|
|
|
An element could be build to handle icecast connections, for example.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
We can also choose to use another type of sink instead of the audiosink.
|
|
|
|
We could use a disksink to write the raw samples to a file, for example.
|
|
|
|
It should also be clear that inserting filters, like a stereo effect,
|
|
|
|
into the pipeline is not that hard to do. The most important thing is
|
2000-08-19 16:36:24 +00:00
|
|
|
that you can reuse allready existing elements.
|
2000-08-18 20:38:54 +00:00
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|