1 | <chapter id="chapter-threads"> |
---|
2 | <title>Threads</title> |
---|
3 | <para> |
---|
4 | GStreamer has support for multithreading through the use of |
---|
5 | the <ulink type="http" |
---|
6 | url="&URLAPI;GstThread.html"><classname>GstThread</classname></ulink> |
---|
7 | object. This object is in fact a special <ulink type="http" |
---|
8 | url="&URLAPI;GstBin.html"><classname>GstBin</classname></ulink> |
---|
9 | that will start a new thread (using Glib's |
---|
10 | <classname>GThread</classname> system) when started. |
---|
11 | </para> |
---|
12 | <para> |
---|
13 | To create a new thread, you can simply use <function>gst_thread_new |
---|
14 | ()</function>. From then on, you can use it similar to how you would |
---|
15 | use a <classname>GstBin</classname>. You can add elements to it, |
---|
16 | change state and so on. The largest difference between a thread and |
---|
17 | other bins is that the thread does not require iteration. Once set to |
---|
18 | the <classname>GST_STATE_PLAYING</classname> state, it will iterate |
---|
19 | its contained children elements automatically. |
---|
20 | </para> |
---|
21 | <para> |
---|
22 | <xref linkend="section-threads-img"/> shows how a thread can be |
---|
23 | visualised. |
---|
24 | </para> |
---|
25 | <figure float="1" id="section-threads-img"> |
---|
26 | <title>A thread</title> |
---|
27 | <mediaobject> |
---|
28 | <imageobject> |
---|
29 | <imagedata fileref="images/thread.ℑ" format="&IMAGE;"/> |
---|
30 | </imageobject> |
---|
31 | </mediaobject> |
---|
32 | </figure> |
---|
33 | |
---|
34 | <sect1 id="section-threads-uses"> |
---|
35 | <title>When would you want to use a thread?</title> |
---|
36 | <para> |
---|
37 | There are several reasons to use threads. However, there's also some |
---|
38 | reasons to limit the use of threads as much as possible. We will go |
---|
39 | into the drawbacks of threading in &GStreamer; in the next section. |
---|
40 | Let's first list some situations where threads can be useful: |
---|
41 | </para> |
---|
42 | <itemizedlist> |
---|
43 | <listitem> |
---|
44 | <para> |
---|
45 | Data buffering, for example when dealing with network streams or |
---|
46 | when recording data from a live stream such as a video or audio |
---|
47 | card. Short hickups elsewhere in the pipeline will not cause data |
---|
48 | loss. See <xref linkend="section-queues-img"/> for a visualization |
---|
49 | of this idea. |
---|
50 | </para> |
---|
51 | </listitem> |
---|
52 | <listitem> |
---|
53 | <para> |
---|
54 | Synchronizing output devices, e.g. when playing a stream containing |
---|
55 | both video and audio data. By using threads for both outputs, they |
---|
56 | will run independently and their synchronization will be better. |
---|
57 | </para> |
---|
58 | </listitem> |
---|
59 | <listitem> |
---|
60 | <para> |
---|
61 | Data pre-rolls. You can use threads and queues (thread boundaries) |
---|
62 | to cache a few seconds of data before playing. By using this |
---|
63 | approach, the whole pipeline will already be setup and data will |
---|
64 | already be decoded. When activating the rest of the pipeline, the |
---|
65 | switch from PAUSED to PLAYING will be instant. |
---|
66 | </para> |
---|
67 | </listitem> |
---|
68 | </itemizedlist> |
---|
69 | <figure float="1" id="section-queues-img"> |
---|
70 | <title>a two-threaded decoder with a queue</title> |
---|
71 | <mediaobject> |
---|
72 | <imageobject> |
---|
73 | <imagedata fileref="images/queue.ℑ" format="&IMAGE;"/> |
---|
74 | </imageobject> |
---|
75 | </mediaobject> |
---|
76 | </figure> |
---|
77 | <para> |
---|
78 | Above, we've mentioned the <quote>queue</quote> element several times |
---|
79 | now. A queue is a thread boundary element. It does so by using a |
---|
80 | classic provider/receiver model as learned in threading classes at |
---|
81 | universities all around the world. By doing this, it acts both as a |
---|
82 | means to make data throughput between threads threadsafe, and it can |
---|
83 | also act as a buffer. Queues have several <classname>GObject</classname> |
---|
84 | properties to be configured for specific uses. For example, you can set |
---|
85 | lower and upper tresholds for the element. If there's less data than |
---|
86 | the lower treshold (default: disabled), it will block output. If |
---|
87 | there's more data than the upper treshold, it will block input or |
---|
88 | (if configured to do so) drop data. |
---|
89 | </para> |
---|
90 | </sect1> |
---|
91 | |
---|
92 | <sect1 id="section-threads-constraints"> |
---|
93 | <title>Constraints placed on the pipeline by the GstThread</title> |
---|
94 | <para> |
---|
95 | Within the pipeline, everything is the same as in any other bin. The |
---|
96 | difference lies at the thread boundary, at the link between the |
---|
97 | thread and the outside world (containing bin). Since &GStreamer; is |
---|
98 | fundamentally buffer-oriented rather than byte-oriented, the natural |
---|
99 | solution to this problem is an element that can "buffer" the buffers |
---|
100 | between the threads, in a thread-safe fashion. This element is the |
---|
101 | <quote>queue</quote> element. A queue should be placed in between any |
---|
102 | two elements whose pads are linked together while the elements live in |
---|
103 | different threads. It doesn't matter if the queue is placed in the |
---|
104 | containing bin or in the thread itself, but it needs to be present |
---|
105 | on one side or the other to enable inter-thread communication. |
---|
106 | </para> |
---|
107 | <para> |
---|
108 | If you are writing a GUI application, making the top-level bin a |
---|
109 | thread will make your GUI more responsive. If it were a pipeline |
---|
110 | instead, it would have to be iterated by your application's event |
---|
111 | loop, which increases the latency between events (say, keyboard |
---|
112 | presses) and responses from the GUI. In addition, any slight hang |
---|
113 | in the GUI would delay iteration of the pipeline, which (for example) |
---|
114 | could cause pops in the output of the sound card, if it is an audio |
---|
115 | pipeline. |
---|
116 | </para> |
---|
117 | <para> |
---|
118 | A problem with using threads is, however, thread contexts. If you |
---|
119 | connect to a signal that is emitted inside a thread, then the signal |
---|
120 | handler for this thread <emphasis>will be executed in that same |
---|
121 | thread</emphasis>! This is very important to remember, because many |
---|
122 | graphical toolkits can not run multi-threaded. Gtk+, for example, |
---|
123 | only allows threaded access to UI objects if you explicitely use |
---|
124 | mutexes. Not doing so will result in random crashes and X errors. |
---|
125 | A solution many people use is to place an idle handler in the signal |
---|
126 | handler, and have the actual signal emission code be executed in the |
---|
127 | idle handler, which will be executed from the mainloop. |
---|
128 | </para> |
---|
129 | <para> |
---|
130 | Generally, if you use threads, you will encounter some problems. Don't |
---|
131 | hesistate to ask us for help in case of problems. |
---|
132 | </para> |
---|
133 | </sect1> |
---|
134 | |
---|
135 | <sect1 id="section-threads-example"> |
---|
136 | <title>A threaded example application</title> |
---|
137 | <para> |
---|
138 | As an example we show the helloworld program that we coded in |
---|
139 | <xref linkend="chapter-helloworld"/> using a thread. Note that |
---|
140 | the whole application lives in a thread (as opposed to half |
---|
141 | of the application living in a thread and the other half being |
---|
142 | another thread or a pipeline). Therefore, it does not need a |
---|
143 | queue element in this specific case. |
---|
144 | </para> |
---|
145 | |
---|
146 | <programlisting><!-- example-begin threads.c --> |
---|
147 | #include <gst/gst.h> |
---|
148 | |
---|
149 | GstElement *thread, *source, *decodebin, *audiosink; |
---|
150 | |
---|
151 | static gboolean |
---|
152 | idle_eos (gpointer data) |
---|
153 | { |
---|
154 | g_print ("Have idle-func in thread %p\n", g_thread_self ()); |
---|
155 | gst_main_quit (); |
---|
156 | |
---|
157 | /* do this function only once */ |
---|
158 | return FALSE; |
---|
159 | } |
---|
160 | |
---|
161 | /* |
---|
162 | * EOS will be called when the src element has an end of stream. |
---|
163 | * Note that this function will be called in the thread context. |
---|
164 | * We will place an idle handler to the function that really |
---|
165 | * quits the application. |
---|
166 | */ |
---|
167 | static void |
---|
168 | cb_eos (GstElement *thread, |
---|
169 | gpointer data) |
---|
170 | { |
---|
171 | g_print ("Have eos in thread %p\n", g_thread_self ()); |
---|
172 | g_idle_add ((GSourceFunc) idle_eos, NULL); |
---|
173 | } |
---|
174 | |
---|
175 | /* |
---|
176 | * On error, too, you'll want to forward signals to the main |
---|
177 | * thread, especially when using GUI applications. |
---|
178 | */ |
---|
179 | |
---|
180 | static void |
---|
181 | cb_error (GstElement *thread, |
---|
182 | GstElement *source, |
---|
183 | GError *error, |
---|
184 | gchar *debug, |
---|
185 | gpointer data) |
---|
186 | { |
---|
187 | g_print ("Error in thread %p: %s\n", g_thread_self (), error->message); |
---|
188 | g_idle_add ((GSourceFunc) idle_eos, NULL); |
---|
189 | } |
---|
190 | |
---|
191 | /* |
---|
192 | * Link new pad from decodebin to audiosink. |
---|
193 | * Contains no further error checking. |
---|
194 | */ |
---|
195 | |
---|
196 | static void |
---|
197 | cb_newpad (GstElement *decodebin, |
---|
198 | GstPad *pad, |
---|
199 | gboolean last, |
---|
200 | gpointer data) |
---|
201 | { |
---|
202 | gst_pad_link (pad, gst_element_get_pad (audiosink, "sink")); |
---|
203 | gst_bin_add (GST_BIN (thread), audiosink); |
---|
204 | gst_bin_sync_children_state (GST_BIN (thread)); |
---|
205 | } |
---|
206 | |
---|
207 | gint |
---|
208 | main (gint argc, |
---|
209 | gchar *argv[]) |
---|
210 | { |
---|
211 | /* init GStreamer */ |
---|
212 | gst_init (&argc, &argv); |
---|
213 | |
---|
214 | /* make sure we have a filename argument */ |
---|
215 | if (argc != 2) { |
---|
216 | g_print ("usage: %s <Ogg/Vorbis filename>\n", argv[0]); |
---|
217 | return -1; |
---|
218 | } |
---|
219 | |
---|
220 | /* create a new thread to hold the elements */ |
---|
221 | thread = gst_thread_new ("thread"); |
---|
222 | g_signal_connect (thread, "eos", G_CALLBACK (cb_eos), NULL); |
---|
223 | g_signal_connect (thread, "error", G_CALLBACK (cb_error), NULL); |
---|
224 | |
---|
225 | /* create elements */ |
---|
226 | source = gst_element_factory_make ("filesrc", "source"); |
---|
227 | g_object_set (G_OBJECT (source), "location", argv[1], NULL); |
---|
228 | decodebin = gst_element_factory_make ("decodebin", "decoder"); |
---|
229 | g_signal_connect (decodebin, "new-decoded-pad", |
---|
230 | G_CALLBACK (cb_newpad), NULL); |
---|
231 | audiosink = gst_element_factory_make ("alsasink", "audiosink"); |
---|
232 | |
---|
233 | /* setup */ |
---|
234 | gst_bin_add_many (GST_BIN (thread), source, decodebin, NULL); |
---|
235 | gst_element_link (source, decodebin); |
---|
236 | gst_element_set_state (audiosink, GST_STATE_PAUSED); |
---|
237 | gst_element_set_state (thread, GST_STATE_PLAYING); |
---|
238 | |
---|
239 | /* no need to iterate. We can now use a mainloop */ |
---|
240 | gst_main (); |
---|
241 | |
---|
242 | /* unset */ |
---|
243 | gst_element_set_state (thread, GST_STATE_NULL); |
---|
244 | gst_object_unref (GST_OBJECT (thread)); |
---|
245 | |
---|
246 | return 0; |
---|
247 | } |
---|
248 | <!-- example-end threads.c --></programlisting> |
---|
249 | </sect1> |
---|
250 | </chapter> |
---|