gstreamer/docs/manual/advanced-threads.xml

163 lines
5 KiB
XML
Raw Normal View History

<chapter id="cha-threads">
<title>Threads</title>
<para>
GStreamer has support for multithreading through the use of
the <classname>GstThread</classname> object. This object is in fact
a special <classname>GstBin</classname> that will become a thread when started.
</para>
<para>
To construct a new thread you will perform something like:
</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));
/* connect the elements here... */
...
/* start playing */
gst_element_set_state (GST_ELEMENT (my_thread), GST_STATE_PLAYING);
</programlisting>
<para>
The above program will create a thread with two elements in it. As soon as it is set to the
PLAYING state, the thread will start to iterate itself. You never need to manually iterate a
thread.
</para>
<sect2>
<title>Constraints placed on the pipeline by the GstThread</title>
<para>
Within the pipeline, everything is the same as in any other bin. The difference lies at the
thread boundary, at the connection 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="cha-queues"/>. 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 of the other to enable inter-thread
communication.
</para>
</sect2>
<sect2>
<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>
</sect2>
<para>
A thread can be visualised as below
</para>
<figure float="1" id="sec-threads-img">
<title>A thread</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/thread.&magic;" format="&magic;" />
</imageobject>
</mediaobject>
</figure>
<para>
As an example we show the helloworld program using a thread.
</para>
<programlisting>
#include &lt;gst/gst.h&gt;
/* eos will be called when the src element has an end of stream */
void
eos (GstSrc *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);
gst_main_quit ();
}
int
main (int argc, char *argv[])
{
GstElement *filesrc, *audiosink;
GstElement *pipeline;
GstElement *thread;
if (argc != 2) {
g_print ("usage: &percnt;s &lt;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 new bin to hold the elements */
pipeline = gst_pipeline_new ("pipeline");
g_assert (pipeline != 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);
/* and an audio sink */
audiosink = gst_element_factory_make ("audiosink", "play_audio");
g_assert (audiosink != NULL);
/* add objects to the main pipeline */
gst_bin_add (GST_BIN (pipeline), filesrc);
gst_bin_add (GST_BIN (pipeline), audiosink);
/* automatically setup the pipeline */
if (!gst_pipeline_autoplug (GST_PIPELINE (pipeline))) {
g_print ("unable to handle stream\n");
exit (-1);
}
/* remove the source element from the pipeline */
gst_bin_remove (GST_BIN (pipeline), filesrc);
/* insert the source element in the thread, remember a thread needs at
least one source or connection element */
gst_bin_add (GST_BIN (thread), filesrc);
/* add the pipeline to the thread too */
gst_bin_add (GST_BIN (thread), GST_ELEMENT (pipeline));
/* start playing */
gst_element_set_state (GST_ELEMENT (thread), GST_STATE_PLAYING);
/* do whatever you want here, the thread will be playing */
...
gst_main ();
gst_pipeline_destroy (thread);
exit (0);
}
</programlisting>
</chapter>