1 <chapter id="chapter-loopbased-sched">
2 <title>How scheduling works</title>
4 Scheduling is, in short, a method for making sure that every element gets
5 called once in a while to process data and prepare data for the next
6 element. Likewise, a kernel has a scheduler to for processes, and your
7 brain is a very complex scheduler too in a way.
8 Randomly calling elements' chain functions won't bring us far, however, so
9 you'll understand that the schedulers in &GStreamer; are a bit more complex
10 than this. However, as a start, it's a nice picture.
11 &GStreamer; currently provides two schedulers: a <emphasis>basic</emphasis>
12 scheduler and an <emphasis>optimal</emphasis> scheduler. As the name says,
13 the basic scheduler (<quote>basic</quote>) is an unoptimized, but very
14 complete and simple scheduler. The optimal scheduler (<quote>opt</quote>),
15 on the other hand, is optimized for media processing, but therefore also
19 Note that schedulers only operate on one thread. If your pipeline contains
20 multiple threads, each thread will run with a separate scheduler. That is
21 the reason why two elements running in different threads need a queue-like
22 element (a <classname>DECOUPLED</classname> element) in between them.
25 <sect1 id="section-sched-basic" xreflabel="The Basic Scheduler">
26 <title>The Basic Scheduler</title>
28 The <emphasis>basic</emphasis> scheduler assumes that each element is its
29 own process. We don't use UNIX processes or POSIX threads for this,
30 however; instead, we use so-called <emphasis>co-threads</emphasis>.
31 Co-threads are threads that run besides each other, but only one is active
32 at a time. The advantage of co-threads over normal threads is that they're
33 lightweight. The disadvantage is that UNIX or POSIX do not provide such a
34 thing, so we need to include our own co-threads stack for this to run.
37 The task of the scheduler here is to control which co-thread runs at what
38 time. A well-written scheduler based on co-threads will let an element run
39 until it outputs one piece of data. Upon pushing one piece of data to the
40 next element, it will let the next element run, and so on. Whenever a
41 running element requires data from the previous element, the scheduler will
42 switch to that previous element and run that element until it has provided
43 data for use in the next element.
46 This method of running elements as needed has the disadvantage that a lot
47 of data will often be queued in between two elements, as the one element
48 has provided data but the other element hasn't actually used it yet. These
49 storages of in-between-data are called <emphasis>bufpens</emphasis>, and
50 they can be visualized as a light <quote>queue</quote>.
53 Note that since every element runs in its own (co-)thread, this scheduler
54 is rather heavy on your system for larger pipelines.
58 <sect1 id="section-sched-opt" xreflabel="The Optimal Scheduler">
59 <title>The Optimal Scheduler</title>
61 The <emphasis>optimal</emphasis> scheduler takes advantage of the fact that
62 several elements can be linked together in one thread, with one element
63 controlling the other. This works as follows: in a series of chain-based
64 elements, each element has a function that accepts one piece of data, and
65 it calls a function that provides one piece of data to the next element.
66 The optimal scheduler will make sure that the <function>gst_pad_push ()</function>
67 function of the first element <emphasis>directly</emphasis> calls the
68 chain-function of the second element. This significantly decreases the
69 latency in a pipeline. It takes similar advantage of other possibilities
70 of short-cutting the data path from one element to the next.
73 The disadvantage of the optimal scheduler is that it is not fully
74 implemented. Also it is badly documented; for most developers, the opt
75 scheduler is one big black box. Features that are not implemented
76 include pad-unlinking within a group while running, pad-selecting
77 (i.e. waiting for data to arrive on a list of pads), and it can't really
78 cope with multi-input/-output elements (with the elements linked to each
79 of these in-/outputs running in the same thread) right now.
82 Some of our developers are intending to write a new scheduler, similar to
83 the optimal scheduler (but better documented and more completely
89 <chapter id="chapter-loopbased-loopfn">
90 <title>How a loopfunc works</title>
92 A <function>_loop ()</function> function is a function that is called by
93 the scheduler, but without providing data to the element. Instead, the
94 element will become responsible for acquiring its own data, and it will
95 still be responsible of sending data over to its source pads. This method
96 noticeably complicates scheduling; you should only write loop-based
97 elements when you need to. Normally, chain-based elements are preferred.
98 Examples of elements that <emphasis>have</emphasis> to be loop-based are
99 elements with multiple sink pads. Since the scheduler will push data into
100 the pads as it comes (and this might not be synchronous), you will easily
101 get ascynronous data on both pads, which means that the data that arrives
102 on the first pad has a different display timestamp then the data arriving
103 on the second pad at the same time. To get over these issues, you should
104 write such elements in a loop-based form. Other elements that are
105 <emphasis>easier</emphasis> to write in a loop-based form than in a
106 chain-based form are demuxers and parsers. It is not required to write such
107 elements in a loop-based form, though.
110 Below is an example of the easiest loop-function that one can write:
113 static void gst_my_filter_loopfunc (GstElement *element);
116 gst_my_filter_init (GstMyFilter *filter)
119 gst_element_set_loopfunc (GST_ELEMENT (filter), gst_my_filter_loopfunc);
124 gst_my_filter_loopfunc (GstElement *element)
126 GstMyFilter *filter = GST_MY_FILTER (element);
130 data = gst_pad_pull (filter->sinkpad);
133 gst_pad_push (filter->srcpad, data);
137 Obviously, this specific example has no single advantage over a chain-based
138 element, so you should never write such elements. However, it's a good
139 introduction to the concept.
142 <sect1 id="section-loopfn-multiinput" xreflabel="Multi-Input Elements">
143 <title>Multi-Input Elements</title>
145 Elements with multiple sink pads need to take manual control over their
146 input to assure that the input is synchronized. The following example
147 code could (should) be used in an aggregator, i.e. an element that takes
148 input from multiple streams and sends it out intermangled. Not really
149 useful in practice, but a good example, again.
154 typedef struct _GstMyFilterInputContext {
157 } GstMyFilterInputContext;
162 gst_my_filter_init (GstMyFilter *filter)
164 GstElementClass *klass = GST_ELEMENT_GET_CLASS (filter);
165 GstMyFilterInputContext *context;
167 filter->sinkpad1 = gst_pad_new_from_template (
168 gst_element_class_get_pad_template (klass, "sink"), "sink_1");
169 context = g_new0 (GstMyFilterInputContext, 1);
170 gst_pad_set_private_data (filter->sinkpad1, context);
172 filter->sinkpad2 = gst_pad_new_from_template (
173 gst_element_class_get_pad_template (klass, "sink"), "sink_2");
174 context = g_new0 (GstMyFilterInputContext, 1);
175 gst_pad_set_private_data (filter->sinkpad2, context);
177 gst_element_set_loopfunc (GST_ELEMENT (filter),
178 gst_my_filter_loopfunc);
184 gst_my_filter_loopfunc (GstElement *element)
186 GstMyFilter *filter = GST_MY_FILTER (element);
188 GstMyFilterInputContext *first_context = NULL;
190 /* Go over each sink pad, update the cache if needed, handle EOS
191 * or non-responding streams and see which data we should handle
193 for (padlist = gst_element_get_padlist (element);
194 padlist != NULL; padlist = g_list_next (padlist)) {
195 GstPad *pad = GST_PAD (padlist->data);
196 GstMyFilterInputContext *context = gst_pad_get_private_data (pad);
198 if (GST_PAD_IS_SRC (pad))
201 while (GST_PAD_IS_USABLE (pad) &&
202 !context->eos && !context->lastbuf) {
203 GstData *data = gst_pad_pull (pad);
205 if (GST_IS_EVENT (data)) {
206 /* We handle events immediately */
207 GstEvent *event = GST_EVENT (data);
209 switch (GST_EVENT_TYPE (event)) {
212 gst_event_unref (event);
214 case GST_EVENT_DISCONTINUOUS:
215 g_warning ("HELP! How do I handle this?");
218 gst_pad_event_default (pad, event);
222 /* We store the buffer to handle synchronization below */
223 context->lastbuf = GST_BUFFER (data);
227 /* synchronize streams by always using the earliest buffer */
228 if (context->lastbuf) {
229 if (!first_context) {
230 first_context = context;
232 if (GST_BUFFER_TIMESTAMP (context->lastbuf) <
233 GST_BUFFER_TIMESTAMP (first_context->lastbuf))
234 first_context = context;
239 /* If we handle no data at all, we're at the end-of-stream, so
240 * we should signal EOS. */
241 if (!first_context) {
242 gst_pad_push (filter->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
243 gst_element_set_eos (element);
247 /* So we do have data! Let's forward that to our source pad. */
248 gst_pad_push (filter->srcpad, GST_DATA (first_context->lastbuf));
249 first_context->lastbuf = NULL;
254 Note that a loop-function is allowed to return. Better yet, a loop
255 function <emphasis>has to</emphasis> return so the scheduler can
256 let other elements run (this is particularly true for the optimal
257 scheduler). Whenever the scheduler feels right, it will call the
258 loop-function of the element again.
262 <sect1 id="section-loopfn-bytestream" xreflabel="The Bytestream Object">
263 <title>The Bytestream Object</title>
265 A second type of elements that wants to be loop-based, are the so-called
266 bytestream-elements. Until now, we've only dealt with elements that
267 receive of pull full buffers of a random size from other elements. Often,
268 however, it is wanted to have control over the stream at a byte-level,
269 such as in stream parsers or demuxers. It is possible to manually pull
270 buffers and merge them until a certain size; it is easier, however, to
271 use bytestream, which wraps this behaviour.
274 Bytestream-using elements are ususally stream parsers or demuxers. For
275 now, we will take a parser as an example. Demuxers require some more
276 magic that will be dealt with later in this guide:
277 <xref linkend="chapter-advanced-request"/>. The goal of this parser will be
278 to parse a text-file and to push each line of text as a separate buffer
284 gst_my_filter_loopfunc (GstElement *element)
286 GstMyFilter *filter = GST_MY_FILTER (element);
291 num = gst_bytestream_peek_bytes (filter->bs, &data, n + 1);
293 GstEvent *event = NULL;
296 gst_bytestream_get_status (filter->bs, &remaining, &event);
298 if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)) {
300 gst_pad_push (filter->srcpad, GST_DATA (event));
301 gst_element_set_eos (element);
305 gst_event_unref (event);
308 /* failed to read - throw error and bail out */
309 gst_element_error (element, STREAM, READ, (NULL), (NULL));
314 /* check if the last character is a newline */
315 if (data[n] == '\n') {
316 GstBuffer *buf = gst_buffer_new_and_alloc (n + 1);
318 /* read the line of text without newline - then flush the newline */
319 gst_bytestream_peek_data (filter->bs, &data, n);
320 memcpy (GST_BUFFER_DATA (buf), data, n);
321 GST_BUFFER_DATA (buf)[n] = '\0';
322 gst_bytestream_flush_fast (filter->bs, n + 1);
323 g_print ("Pushing '%s'\n", GST_BUFFER_DATA (buf));
324 gst_pad_push (filter->srcpad, GST_DATA (buf));
332 gst_my_filter_change_state (GstElement *element)
334 GstMyFilter *filter = GST_MY_FILTER (element);
336 switch (GST_STATE_TRANSITION (element)) {
337 case GST_STATE_READY_TO_PAUSED:
338 filter->bs = gst_bytestream_new (filter->sinkpad);
340 case GST_STATE_PAUSED_TO_READY:
341 gst_bytestream_destroy (filter->bs);
347 if (GST_ELEMENT_CLASS (parent_class)->change_state)
348 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
350 return GST_STATE_SUCCESS;
355 In the above example, you'll notice how bytestream handles buffering of
356 data for you. The result is that you can handle the same data multiple
357 times. Event handling in bytestream is currently sort of
358 <emphasis>wacky</emphasis>, but it works quite well. The one big
359 disadvantage of bytestream is that it <emphasis>requires</emphasis>
360 the element to be loop-based. Long-term, we hope to have a chain-based
361 usable version of bytestream, too.
365 <sect1 id="section-loopbased-secnd">
366 <title>Adding a second output</title>
368 Identity is now a tee
372 <sect1 id="section-loopbased-modappl">
373 <title>Modifying the test application</title>