docs/manual/: Rewrites. Remove cothreads, go a bit into opt specifically, document threads and their gotchas, and do ...

Original commit message from CVS:
* docs/manual/advanced-autoplugging.xml:
* docs/manual/advanced-schedulers.xml:
* docs/manual/advanced-threads.xml:
Rewrites. Remove cothreads, go a bit into opt specifically,
document threads and their gotchas, and do some technical stuff
on autoplugging plus add some working examples. Fixes #157395.
* examples/manual/Makefile.am:
Add typefind/autoplugger example (one that actually works).
Remove queue example since it's a duplicate of the thread one.
This commit is contained in:
Ronald S. Bultje 2004-12-19 22:54:12 +00:00
parent 756fba11ff
commit 56f05d60c3
6 changed files with 811 additions and 896 deletions

View file

@ -1,3 +1,15 @@
2004-12-19 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* docs/manual/advanced-autoplugging.xml:
* docs/manual/advanced-schedulers.xml:
* docs/manual/advanced-threads.xml:
Rewrites. Remove cothreads, go a bit into opt specifically,
document threads and their gotchas, and do some technical stuff
on autoplugging plus add some working examples. Fixes #157395.
* examples/manual/Makefile.am:
Add typefind/autoplugger example (one that actually works).
Remove queue example since it's a duplicate of the thread one.
2004-12-17 Benjamin Otte <in7y118@public.uni-hamburg.de> 2004-12-17 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gstvalue.c: (gst_value_deserialize_string): * gst/gstvalue.c: (gst_value_deserialize_string):

File diff suppressed because it is too large Load diff

View file

@ -1,140 +1,152 @@
<chapter id="chapter-scheduler"> <chapter id="chapter-scheduler">
<title>Scheduling</title> <title>Scheduling</title>
<para>
By now, you've seen several example applications. All of them would set
up a pipeline and call <function>gst_bin_iterate ()</function> to start
media processing. You might have started wondering what happens during
pipeline iteration. This whole process of media processing is called
scheduling. Scheduling is considered one of the most complex parts of
&GStreamer;. Here, we will do no more than give a global overview of
scheduling, most of which will be purely informative. It might help in
understanding the underlying parts of &GStreamer;.
</para>
<para> <para>
The scheduler is responsible for managing the plugins at runtime. Its The scheduler is responsible for managing the plugins at runtime. Its
main responsibilities are: main responsibilities are:
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>
Preparing the plugins so they can be scheduled. Managing data throughput between pads and elements in a pipeline.
This might sometimes imply temporary data storage between elements.
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
Monitoring state changes and enabling/disabling the element in the Calling functions in elements that do the actual data processing.
</para>
</listitem>
<listitem>
<para>
Monitoring state changes and enabling/disabling elements in the
chain. chain.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
Choosing an element as the entry point for the pipeline.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
Selecting and distributing the global clock. Selecting and distributing the global clock.
<!-- FIXME: is this still true? -->
</para> </para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
</para> </para>
<para> <para>
The scheduler is a pluggable component; this means that alternative The scheduler is a pluggable component; this means that alternative
schedulers can be written and plugged into GStreamer. The default scheduler schedulers can be written and plugged into GStreamer. There is usually
uses cothreads to schedule the plugins in a pipeline. Cothreads are fast no need for interaction in the process of choosing the scheduler, though.
and lightweight user-space threads. The default scheduler in &GStreamer; is called <quote>opt</quote>. Some
</para> of the concepts discussed here are specific to opt.
<para>
There is usually no need to interact with the scheduler directly, however
in some cases it is feasible to set a specific clock or force a specific
plugin as the entry point in the pipeline.
</para> </para>
<sect1 id="section-chain-based"> <sect1 id="section-scheduler-manage">
<title>Chain-based elements</title> <title>Managing elements and data throughput</title>
<para> <para>
Chain based elements receive a buffer of data and are supposed To understand some specifics of scheduling, it is important to know
to handle the data and perform a gst_pad_push. how elements work internally. Largely, there are four types of elements:
<function>_chain ()</function>-based elements, <function>_loop
()</function>-based elements, <function>_get ()</function>-based
elements and decoupled elements. Each of those have a set of features
and limitations that are important for how they are scheduled.
</para>
<itemizedlist>
<listitem>
<para>
<function>_chain ()</function>-based elements are elements that
have a <function>_chain ()</function>-function defined for each of
their sinkpads. Those functions will receive data whenever input
data is available. In those functions, the element can
<emphasis>push</emphasis> data over its source pad(s) to peer
elements. <function>_chain ()</function>-based elements cannot
<emphasis>pull</emphasis> additional data from their sinkpad(s).
Most elements in &GStreamer; are <function>_chain
()</function>-based.
</para>
</listitem>
<listitem>
<para>
<function>_loop ()</function>-based elements are elements that have
a <function>_loop ()</function>-function defined for the whole
element. Inside this function, the element can pull buffers from
its sink pad(s) and push data over its source pad(s) as it sees fit.
Such elements usually require specific control over their input.
Muxers and demuxers are usually <function>_loop ()</function>-based.
</para>
</listitem>
<listitem>
<para>
<function>_get ()</function>-based elements are elements with only
source pads. For each source pad, a <function>_get
()</function>-function is defined, which is called whenever the peer
element needs additional input data. Most source elements are, in
fact, <function>_get ()</function>-based. Such an element cannot
actively push data.
</para>
</listitem>
<listitem>
<para>
Decoupled elements are elements whose source pads are
<function>_get ()</function>-based and whose sink pads are
<function>_chain ()</function>-based. The <function>_chain
()</function>-function cannot push data over its source pad(s),
however. One such element is the <quote>queue</quote> element,
which is a thread boundary element. Since only one side of such
elements are interesting for one particular scheduler, we can
safely handle those elements as if they were either
<function>_get ()</function>- or <function>_chain
()</function>-based. Therefore, we will further omit this type
of elements in the discussion.
</para>
</listitem>
</itemizedlist>
<para>
Obviously, the type of elements that are linked together have
implications for how the elements will be scheduled. If a get-based
element is linked to a loop-based element and the loop-based element
requests data from its sinkpad, we can just call the get-function and
be done with it. However, if two loop-based elements are linked to
each other, it's a lot more complicated. Similarly, a loop-based
element linked to a chain-based element is a lot easier than two
loop-based elements linked to each other.
</para> </para>
<para> <para>
The basic main function of a chain-based element is like: The default &GStreamer; scheduler, <quote>opt</quote>, uses a concept
</para> of chains and groups. A group is a series of elements that can that
<programlisting> do not require any context switches or intermediate data stores to
static void be executed. In practice, this implies zero or one loop-based elements,
chain_function (GstPad *pad, GstBuffer *buffer) one get-based element (at the beginning) and an infinite amount of
{ chain-based elements. If there is a loop-based element, then the
GstBuffer *outbuffer; scheduler will simply call this elements loop-function to iterate.
If there is no loop-based element, then data will be pulled from the
.... get-based element and will be pushed over the chain-based elements.
// process the buffer, create a new outbuffer
...
gst_pad_push (srcpad, outbuffer);
}
</programlisting>
<para>
Chain based function are mainly used for elements that have a one to one
relation between their input and output behaviour. An example of such an
element can be a simple video blur filter. The filter takes a buffer in, performs
the blur operation on it and sends out the resulting buffer.
</para> </para>
<para> <para>
Another element, for example, is a volume filter. The filter takes audio samples as A chain is a series of groups that depend on each other for data.
input, performs the volume effect and sends out the resulting buffer. For example, two linked loop-based elements would end up in different
</para> groups, but in the same chain. Whenever the first loop-based element
pushes data over its source pad, the data will be temporarily stored
</sect1> inside the scheduler until the loop-function returns. When it's done,
the loop-function of the second element will be called to process this
<sect1 id="section-loop-based"> data. If it pulls data from its sinkpad while no data is available,
<title>Loop-based elements</title> the scheduler will <quote>emulate</quote> a get-function and, in this
<para> function, iterate the first group until data is available.
As opposed to chain-based elements, loop-based elements enter an
infinite loop that looks like this:
<programlisting>
GstBuffer *buffer, *outbuffer;
while (1) {
buffer = gst_pad_pull (sinkpad);
...
// process buffer, create outbuffer
while (!done) {
....
// optionally request another buffer
buffer = gst_pad_pull (sinkpad);
....
}
...
gst_pad_push (srcpad, outbuffer);
}
</programlisting>
The loop-based elements request a buffer whenever they need one.
</para>
<para>
When the request for a buffer cannot be immediately satisfied, the control
will be given to the source element of the loop-based element until it
performs a push on its source pad. At that time the control is handed
back to the loop-based element, etc... The execution trace can get
fairly complex using cothreads when there are multiple input/output
pads for the loop-based element. Cothread switches are performed within
the call to gst_pad_pull and gst_pad_push; from the perspective of
the loop-based element, it just "appears" that gst_pad_push (or _pull)
might take a long time to return.
</para> </para>
<para> <para>
Loop based elements are mainly used for the more complex elements The above is roughly how scheduling works in &GStreamer;. This has
that need a specific amount of data before they can start to produce some implications for ideal pipeline design. An pipeline would
output. An example of such an element is the MPEG video decoder. The ideally contain at most one loop-based element, so that all data
element will pull a buffer, perform some decoding on it and optionally processing is immediate and no data is stored inside the scheduler
request more buffers to decode, and when a complete video frame has during group switches. You would think that this decreases overhead
been decoded, a buffer is sent out. For example, any plugin using the significantly. In practice, this is not so bad, however. It's something
bytestream library will need to be loop-based. to keep in the back of your mind, nothing more.
</para>
<para>
There is no problem in putting cothreaded elements into a <ulink
type="http" url="../../gstreamer/html/GstThread.html"><classname>GstThread
</classname></ulink> to
create even more complex pipelines with both user and kernel space threads.
</para>
</sect1>
<sect1 id="section-opt">
<title>The optimal scheduler</title>
<para>
Explain opt a bit, chains, groups, and how it affects execution.
</para> </para>
</sect1> </sect1>
</chapter> </chapter>

View file

@ -2,70 +2,25 @@
<title>Threads</title> <title>Threads</title>
<para> <para>
GStreamer has support for multithreading through the use of GStreamer has support for multithreading through the use of
the <ulink type="http" url="../../gstreamer/html/GstThread.html"><classname> the <ulink type="http"
GstThread</classname></ulink> object. This object is in fact url="&URLAPI;GstThread.html"><classname>GstThread</classname></ulink>
a special <ulink type="http" url="../../gstreamer/html/GstBin.html"><classname> object. This object is in fact a special <ulink type="http"
GstBin</classname></ulink> that will become a thread when started. url="&URLAPI;GstBin.html"><classname>GstBin</classname></ulink>
</para> that will start a new thread (using Glib's
<classname>GThread</classname> system) when started.
<para>
To construct a new thread you will perform something like:
</para>
<para>
<programlisting>
GstElement *my_thread;
/* create the thread object */
my_thread = gst_thread_new ("my_thread");
/* you could have used gst_element_factory_make ("thread", "my_thread"); */
g_return_if_fail (my_thread != NULL);
/* add some plugins */
gst_bin_add (GST_BIN (my_thread), GST_ELEMENT (funky_src));
gst_bin_add (GST_BIN (my_thread), GST_ELEMENT (cool_effect));
/* link the elements here... */
...
/* start playing */
gst_element_set_state (GST_ELEMENT (my_thread), GST_STATE_PLAYING);
</programlisting>
</para> </para>
<para> <para>
The above program will create a thread with two elements in it. As soon To create a new thread, you can simply use <function>gst_thread_new
as it is set to the PLAYING state, the thread will start to iterate ()</function>. From then on, you can use it similar to how you would
itself. You never need to explicitly iterate a thread. use a <classname>GstBin</classname>. You can add elements to it,
</para> change state and so on. The largest difference between a thread and
other bins is that the thread does not require iteration. Once set to
<sect1 id="section-threads-constraints"> the <classname>GST_STATE_PLAYING</classname> state, it will iterate
<title>Constraints placed on the pipeline by the GstThread</title> its contained children elements automatically.
<para>
Within the pipeline, everything is the same as in any other bin. The
difference lies at the thread boundary, at the link between the
thread and the outside world (containing bin). Since GStreamer is
fundamentally buffer-oriented rather than byte-oriented, the natural
solution to this problem is an element that can "buffer" the buffers
between the threads, in a thread-safe fashion. This element is the
queue, described more fully in <xref linkend="section-queue"/>. It doesn't
matter if the queue is placed in the containing bin or in the thread
itself, but it needs to be present on one side or the other to enable
inter-thread communication.
</para>
</sect1>
<sect1 id="section-threads-when">
<title>When would you want to use a thread?</title>
<para>
If you are writing a GUI application, making the top-level bin a thread will make your GUI
more responsive. If it were a pipeline instead, it would have to be iterated by your
application's event loop, which increases the latency between events (say, keyboard presses)
and responses from the GUI. In addition, any slight hang in the GUI would delay iteration of
the pipeline, which (for example) could cause pops in the output of the sound card, if it is
an audio pipeline.
</para> </para>
<para> <para>
<xref linkend="section-threads-img"/> shows how a thread can be visualised. <xref linkend="section-threads-img"/> shows how a thread can be
visualised.
</para> </para>
<figure float="1" id="section-threads-img"> <figure float="1" id="section-threads-img">
<title>A thread</title> <title>A thread</title>
@ -76,120 +31,41 @@
</mediaobject> </mediaobject>
</figure> </figure>
<sect1 id="section-threads-uses">
<title>When would you want to use a thread?</title>
<para> <para>
As an example we show the helloworld program using a thread. There are several reasons to use threads. However, there's also some
reasons to limit the use of threads as much as possible. We will go
into the drawbacks of threading in &GStreamer; in the next section.
Let's first list some situations where threads can be useful:
</para> </para>
<itemizedlist>
<listitem>
<para> <para>
<programlisting> Data buffering, for example when dealing with network streams or
<!-- example-begin threads.c --> when recording data from a live stream such as a video or audio
#include &lt;gst/gst.h&gt; card. Short hickups elsewhere in the pipeline will not cause data
loss. See <xref linkend="section-queues-img"/> for a visualization
/* we set this to TRUE right before gst_main (), but there could still of this idea.
be a race condition between setting it and entering the function */
gboolean can_quit = FALSE;
/* eos will be called when the src element has an end of stream */
void
eos (GstElement *src, gpointer data)
{
GstThread *thread = GST_THREAD (data);
g_print ("have eos, quitting\n");
/* stop the bin */
gst_element_set_state (GST_ELEMENT (thread), GST_STATE_NULL);
while (!can_quit) /* waste cycles */ ;
gst_main_quit ();
}
int
main (int argc, char *argv[])
{
GstElement *filesrc, *demuxer, *decoder, *converter, *audiosink;
GstElement *thread;
if (argc &lt; 2) {
g_print ("usage: %s &lt;Ogg/Vorbis filename&gt;\n", argv[0]);
exit (-1);
}
gst_init (&amp;argc, &amp;argv);
/* create a new thread to hold the elements */
thread = gst_thread_new ("thread");
g_assert (thread != NULL);
/* create a disk reader */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_assert (filesrc != NULL);
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
g_signal_connect (G_OBJECT (filesrc), "eos",
G_CALLBACK (eos), thread);
/* create an ogg demuxer */
demuxer = gst_element_factory_make ("oggdemux", "demuxer");
g_assert (demuxer != NULL);
/* create a vorbis decoder */
decoder = gst_element_factory_make ("vorbisdec", "decoder");
g_assert (decoder != NULL);
/* create an audio converter */
converter = gst_element_factory_make ("audioconvert", "converter");
g_assert (decoder != NULL);
/* and an audio sink */
audiosink = gst_element_factory_make ("osssink", "play_audio");
g_assert (audiosink != NULL);
/* add objects to the thread */
gst_bin_add_many (GST_BIN (thread), filesrc, demuxer, decoder, converter, audiosink, NULL);
/* link them in the logical order */
gst_element_link_many (filesrc, demuxer, decoder, converter, audiosink, NULL);
/* start playing */
gst_element_set_state (thread, GST_STATE_PLAYING);
/* do whatever you want here, the thread will be playing */
g_print ("thread is playing\n");
can_quit = TRUE;
gst_main ();
gst_object_unref (GST_OBJECT (thread));
exit (0);
}
<!-- example-end threads.c -->
</programlisting>
</para> </para>
</sect1> </listitem>
<listitem>
<sect1 id="section-queue">
<title>Queue</title>
<para> <para>
A queue is a filter element. Synchronizing output devices, e.g. when playing a stream containing
Queues can be used to link two elements in such way that the data can both video and audio data. By using threads for both outputs, they
be buffered. will run independently and their synchronization will be better.
</para> </para>
</listitem>
<listitem>
<para> <para>
A buffer that is sinked to a Queue will not automatically be pushed to the Data pre-rolls. You can use threads and queues (thread boundaries)
next linked element but will be buffered. It will be pushed to the next to cache a few seconds of data before playing. By using this
element as soon as a gst_pad_pull () is called on the queue's source pad. approach, the whole pipeline will already be setup and data will
</para> already be decoded. When activating the rest of the pipeline, the
<para> switch from PAUSED to PLAYING will be instant.
Queues are mostly used in conjunction with a thread bin to
provide an external link for the thread's elements. You could have one
thread feeding buffers into a queue and another
thread repeatedly pulling on the queue to feed its
internal elements.
</para>
<para>
Below is a figure of a two-threaded decoder. We have one thread (the main execution
thread) reading the data from a file, and another thread decoding the data.
</para> </para>
</listitem>
</itemizedlist>
<figure float="1" id="section-queues-img"> <figure float="1" id="section-queues-img">
<title>a two-threaded decoder with a queue</title> <title>a two-threaded decoder with a queue</title>
<mediaobject> <mediaobject>
@ -198,99 +74,177 @@ main (int argc, char *argv[])
</imageobject> </imageobject>
</mediaobject> </mediaobject>
</figure> </figure>
<para> <para>
The standard <application>GStreamer</application> queue implementation has some Above, we've mentioned the <quote>queue</quote> element several times
properties that can be changed using the g_objet_set () method. To set the now. A queue is a thread boundary element. It does so by using a
maximum number of buffers that can be queued to 30, do: classic provider/receiver model as learned in threading classes at
universities all around the world. By doing this, it acts both as a
means to make data throughput between threads threadsafe, and it can
also act as a buffer. Queues have several <classname>GObject</classname>
properties to be configured for specific uses. For example, you can set
lower and upper tresholds for the element. If there's less data than
the lower treshold (default: disabled), it will block output. If
there's more data than the upper treshold, it will block input or
(if configured to do so) drop data.
</para> </para>
<programlisting> </sect1>
g_object_set (G_OBJECT (queue), "max_level", 30, NULL);
</programlisting>
<sect1 id="section-threads-constraints">
<title>Constraints placed on the pipeline by the GstThread</title>
<para> <para>
The following MP3 player shows you how to create the above pipeline Within the pipeline, everything is the same as in any other bin. The
using a thread and a queue. difference lies at the thread boundary, at the link between the
thread and the outside world (containing bin). Since &GStreamer; is
fundamentally buffer-oriented rather than byte-oriented, the natural
solution to this problem is an element that can "buffer" the buffers
between the threads, in a thread-safe fashion. This element is the
<quote>queue</quote> element. A queue should be placed in between any
two elements whose pads are linked together while the elements live in
different threads. It doesn't matter if the queue is placed in the
containing bin or in the thread itself, but it needs to be present
on one side or the other to enable inter-thread communication.
</para>
<para>
If you are writing a GUI application, making the top-level bin a
thread will make your GUI more responsive. If it were a pipeline
instead, it would have to be iterated by your application's event
loop, which increases the latency between events (say, keyboard
presses) and responses from the GUI. In addition, any slight hang
in the GUI would delay iteration of the pipeline, which (for example)
could cause pops in the output of the sound card, if it is an audio
pipeline.
</para>
<para>
A problem with using threads is, however, thread contexts. If you
connect to a signal that is emitted inside a thread, then the signal
handler for this thread <emphasis>will be executed in that same
thread</emphasis>! This is very important to remember, because many
graphical toolkits can not run multi-threaded. Gtk+, for example,
only allows threaded access to UI objects if you explicitely use
mutexes. Not doing so will result in random crashes and X errors.
A solution many people use is to place an idle handler in the signal
handler, and have the actual signal emission code be executed in the
idle handler, which will be executed from the mainloop.
</para>
<para>
Generally, if you use threads, you will encounter some problems. Don't
hesistate to ask us for help in case of problems.
</para>
</sect1>
<sect1 id="section-threads-example">
<title>A threaded example application</title>
<para>
As an example we show the helloworld program that we coded in
<xref linkend="chapter-helloworld"/> using a thread. Note that
the whole application lives in a thread (as opposed to half
of the application living in a thread and the other half being
another thread or a pipeline). Therefore, it does not need a
queue element in this specific case.
</para> </para>
<programlisting> <programlisting><!-- example-begin threads.c -->
<!-- example-begin queue.c -->
#include &lt;stdlib.h&gt;
#include &lt;gst/gst.h&gt; #include &lt;gst/gst.h&gt;
gboolean playing; GstElement *thread, *source, *decodebin, *audiosink;
/* eos will be called when the src element has an end of stream */ static gboolean
void idle_eos (gpointer data)
eos (GstElement *element, gpointer data)
{ {
g_print ("have eos, quitting\n"); g_print ("Have idle-func in thread %p\n", g_thread_self ());
gst_main_quit ();
playing = FALSE; /* do this function only once */
return FALSE;
} }
int /*
main (int argc, char *argv[]) * EOS will be called when the src element has an end of stream.
* Note that this function will be called in the thread context.
* We will place an idle handler to the function that really
* quits the application.
*/
static void
cb_eos (GstElement *thread,
gpointer data)
{ {
GstElement *filesrc, *audiosink, *queue, *decode; g_print ("Have eos in thread %p\n", g_thread_self ());
GstElement *bin; g_idle_add ((GSourceFunc) idle_eos, NULL);
GstElement *thread; }
/*
* On error, too, you'll want to forward signals to the main
* thread, especially when using GUI applications.
*/
static void
cb_error (GstElement *thread,
GstElement *source,
GError *error,
gchar *debug,
gpointer data)
{
g_print ("Error in thread %p: %s\n", g_thread_self (), error->message);
g_idle_add ((GSourceFunc) idle_eos, NULL);
}
/*
* Link new pad from decodebin to audiosink.
* Contains no further error checking.
*/
static void
cb_newpad (GstElement *decodebin,
GstPad *pad,
gboolean last,
gpointer data)
{
gst_pad_link (pad, gst_element_get_pad (audiosink, "sink"));
gst_bin_add (GST_BIN (thread), audiosink);
gst_bin_sync_children_state (GST_BIN (thread));
}
gint
main (gint argc,
gchar *argv[])
{
/* init GStreamer */
gst_init (&amp;argc, &amp;argv); gst_init (&amp;argc, &amp;argv);
/* make sure we have a filename argument */
if (argc != 2) { if (argc != 2) {
g_print ("usage: %s &lt;mp3 filename&gt;\n", argv[0]); g_print ("usage: %s &lt;Ogg/Vorbis filename&gt;\n", argv[0]);
exit (-1); return -1;
} }
/* create a new thread to hold the elements */ /* create a new thread to hold the elements */
thread = gst_thread_new ("thread"); thread = gst_thread_new ("thread");
g_assert (thread != NULL); g_signal_connect (thread, "eos", G_CALLBACK (cb_eos), NULL);
g_signal_connect (thread, "error", G_CALLBACK (cb_error), NULL);
/* create a new bin to hold the elements */ /* create elements */
bin = gst_bin_new ("bin"); source = gst_element_factory_make ("filesrc", "source");
g_assert (bin != NULL); g_object_set (G_OBJECT (source), "location", argv[1], NULL);
decodebin = gst_element_factory_make ("decodebin", "decoder");
g_signal_connect (decodebin, "new-decoded-pad",
G_CALLBACK (cb_newpad), NULL);
audiosink = gst_element_factory_make ("alsasink", "audiosink");
/* create a disk reader */ /* setup */
filesrc = gst_element_factory_make ("filesrc", "disk_source"); gst_bin_add_many (GST_BIN (thread), source, decodebin, NULL);
g_assert (filesrc != NULL); gst_element_link (source, decodebin);
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); gst_element_set_state (audiosink, GST_STATE_PAUSED);
g_signal_connect (G_OBJECT (filesrc), "eos", gst_element_set_state (thread, GST_STATE_PLAYING);
G_CALLBACK (eos), thread);
queue = gst_element_factory_make ("queue", "queue"); /* no need to iterate. We can now use a mainloop */
g_assert (queue != NULL); gst_main ();
/* and an audio sink */ /* unset */
audiosink = gst_element_factory_make ("osssink", "play_audio"); gst_element_set_state (thread, GST_STATE_NULL);
g_assert (audiosink != NULL); gst_object_unref (GST_OBJECT (thread));
decode = gst_element_factory_make ("mad", "decode");
/* add objects to the main bin */
gst_bin_add_many (GST_BIN (thread), decode, audiosink, NULL);
gst_bin_add_many (GST_BIN (bin), filesrc, queue, thread, NULL);
gst_element_link (filesrc, queue);
gst_element_link_many (queue, decode, audiosink, NULL);
/* start playing */
gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING);
playing = TRUE;
while (playing) {
gst_bin_iterate (GST_BIN (bin));
}
gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL);
return 0; return 0;
} }
<!-- example-end queue.c --> <!-- example-end threads.c --></programlisting>
</programlisting>
</sect1> </sect1>
</chapter> </chapter>

View file

@ -34,16 +34,12 @@ EXAMPLES = \
init \ init \
popt \ popt \
query \ query \
queue \
threads \ threads \
typefind \
playbin \ playbin \
decodebin \ decodebin \
$(GST_LOADSAVE_SRC) $(GST_LOADSAVE_SRC)
dynamic.c: $(top_srcdir)/docs/manual/advanced-autoplugging.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-autoplugging.xml
elementmake.c elementcreate.c elementget.c elementlink.c elementfactory.c: $(top_srcdir)/docs/manual/basics-elements.xml elementmake.c elementcreate.c elementget.c elementlink.c elementfactory.c: $(top_srcdir)/docs/manual/basics-elements.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/basics-elements.xml $(top_srcdir)/docs/manual/basics-elements.xml
@ -72,10 +68,14 @@ query.c: $(top_srcdir)/docs/manual/advanced-position.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-position.xml $(top_srcdir)/docs/manual/advanced-position.xml
queue.c threads.c: $(top_srcdir)/docs/manual/advanced-threads.xml threads.c: $(top_srcdir)/docs/manual/advanced-threads.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-threads.xml $(top_srcdir)/docs/manual/advanced-threads.xml
typefind.c dynamic.c: $(top_srcdir)/docs/manual/advanced-autoplugging.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-autoplugging.xml
playbin.c decodebin.c: $(top_srcdir)/docs/manual/highlevel-components.xml playbin.c decodebin.c: $(top_srcdir)/docs/manual/highlevel-components.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/highlevel-components.xml $(top_srcdir)/docs/manual/highlevel-components.xml

View file

@ -34,16 +34,12 @@ EXAMPLES = \
init \ init \
popt \ popt \
query \ query \
queue \
threads \ threads \
typefind \
playbin \ playbin \
decodebin \ decodebin \
$(GST_LOADSAVE_SRC) $(GST_LOADSAVE_SRC)
dynamic.c: $(top_srcdir)/docs/manual/advanced-autoplugging.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-autoplugging.xml
elementmake.c elementcreate.c elementget.c elementlink.c elementfactory.c: $(top_srcdir)/docs/manual/basics-elements.xml elementmake.c elementcreate.c elementget.c elementlink.c elementfactory.c: $(top_srcdir)/docs/manual/basics-elements.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/basics-elements.xml $(top_srcdir)/docs/manual/basics-elements.xml
@ -72,10 +68,14 @@ query.c: $(top_srcdir)/docs/manual/advanced-position.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-position.xml $(top_srcdir)/docs/manual/advanced-position.xml
queue.c threads.c: $(top_srcdir)/docs/manual/advanced-threads.xml threads.c: $(top_srcdir)/docs/manual/advanced-threads.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-threads.xml $(top_srcdir)/docs/manual/advanced-threads.xml
typefind.c dynamic.c: $(top_srcdir)/docs/manual/advanced-autoplugging.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/advanced-autoplugging.xml
playbin.c decodebin.c: $(top_srcdir)/docs/manual/highlevel-components.xml playbin.c decodebin.c: $(top_srcdir)/docs/manual/highlevel-components.xml
$(PERL_PATH) $(srcdir)/extract.pl $@ \ $(PERL_PATH) $(srcdir)/extract.pl $@ \
$(top_srcdir)/docs/manual/highlevel-components.xml $(top_srcdir)/docs/manual/highlevel-components.xml