-<chapter id="chapter-loopbased-sched">
- <title>How scheduling works</title>
+<chapter id="chapter-scheduling" xreflabel="Different scheduling modes">
+ <title>Different scheduling modes</title>
<para>
Scheduling is, in short, a method for making sure that every element gets
called once in a while to process data and prepare data for the next
Randomly calling elements' chain functions won't bring us far, however, so
you'll understand that the schedulers in &GStreamer; are a bit more complex
than this. However, as a start, it's a nice picture.
- &GStreamer; currently provides two schedulers: a <emphasis>basic</emphasis>
- scheduler and an <emphasis>optimal</emphasis> scheduler. As the name says,
- the basic scheduler (<quote>basic</quote>) is an unoptimized, but very
- complete and simple scheduler. The optimal scheduler (<quote>opt</quote>),
- on the other hand, is optimized for media processing, but therefore also
- more complex.
</para>
<para>
- Note that schedulers only operate on one thread. If your pipeline contains
- multiple threads, each thread will run with a separate scheduler. That is
- the reason why two elements running in different threads need a queue-like
- element (a <classname>DECOUPLED</classname> element) in between them.
+ So far, we have only discussed <function>_chain ()</function>-operating
+ elements, i.e. elements that have a chain-function set on their sinkpad
+ and push buffers on their sinkpad. Pads (or elements) can also operate
+ in two other scheduling modes, however. In this chapter, we will discuss
+ what those scheduling modes are, how they can be enabled and in what
+ cases they are useful. The other two scheduling modes are random access
+ (<function>_getrange ()</function>-based) or task-runner (which means
+ that this element is the driving force in the pipeline) mode.
</para>
- <sect1 id="section-sched-basic" xreflabel="The Basic Scheduler">
- <title>The Basic Scheduler</title>
- <para>
- The <emphasis>basic</emphasis> scheduler assumes that each element is its
- own process. We don't use UNIX processes or POSIX threads for this,
- however; instead, we use so-called <emphasis>co-threads</emphasis>.
- Co-threads are threads that run besides each other, but only one is active
- at a time. The advantage of co-threads over normal threads is that they're
- lightweight. The disadvantage is that UNIX or POSIX do not provide such a
- thing, so we need to include our own co-threads stack for this to run.
- </para>
- <para>
- The task of the scheduler here is to control which co-thread runs at what
- time. A well-written scheduler based on co-threads will let an element run
- until it outputs one piece of data. Upon pushing one piece of data to the
- next element, it will let the next element run, and so on. Whenever a
- running element requires data from the previous element, the scheduler will
- switch to that previous element and run that element until it has provided
- data for use in the next element.
- </para>
- <para>
- This method of running elements as needed has the disadvantage that a lot
- of data will often be queued in between two elements, as the one element
- has provided data but the other element hasn't actually used it yet. These
- storages of in-between-data are called <emphasis>bufpens</emphasis>, and
- they can be visualized as a light <quote>queue</quote>.
- </para>
- <para>
- Note that since every element runs in its own (co-)thread, this scheduler
- is rather heavy on your system for larger pipelines.
- </para>
- </sect1>
-
- <sect1 id="section-sched-opt" xreflabel="The Optimal Scheduler">
- <title>The Optimal Scheduler</title>
- <para>
- The <emphasis>optimal</emphasis> scheduler takes advantage of the fact that
- several elements can be linked together in one thread, with one element
- controlling the other. This works as follows: in a series of chain-based
- elements, each element has a function that accepts one piece of data, and
- it calls a function that provides one piece of data to the next element.
- The optimal scheduler will make sure that the <function>gst_pad_push ()</function>
- function of the first element <emphasis>directly</emphasis> calls the
- chain-function of the second element. This significantly decreases the
- latency in a pipeline. It takes similar advantage of other possibilities
- of short-cutting the data path from one element to the next.
- </para>
- <para>
- The disadvantage of the optimal scheduler is that it is not fully
- implemented. Also it is badly documented; for most developers, the opt
- scheduler is one big black box. Features that are not implemented
- include pad-unlinking within a group while running, pad-selecting
- (i.e. waiting for data to arrive on a list of pads), and it can't really
- cope with multi-input/-output elements (with the elements linked to each
- of these in-/outputs running in the same thread) right now.
- </para>
- <para>
- Some of our developers are intending to write a new scheduler, similar to
- the optimal scheduler (but better documented and more completely
- implemented).
- </para>
+ <sect1 id="section-scheduling-activation"
+ xreflabel="The pad actication stage">
+ <title>The pad activation stage</title>
+ <para>
+ The stage in which &GStreamer; decides in what scheduling mode the
+ various elements will operate, is called the pad-activation stage. In
+ this stage, &GStreamer; will query the scheduling capabilities (i.e.
+ it will see in what modes each particular element/pad can operate) and
+ decide on the optimal scheduling composition for the pipeline. Next,
+ each pad will be notified of the scheduling mode that was assigned to
+ it, and after that the pipeline will start running.
+ </para>
+ <para>
+ Pads can be assigned one of three modes, each mode putting several
+ prerequisites on the pads. Pads should implement a notification
+ function (<function>gst_pad_set_activatepull_function ()</function> and
+ <function>gst_pad_set_activatepush_function ()</function>) to be
+ notified of the scheduling mode assignment. Also, sinkpads assigned
+ to do pull-based scheduling mode should start and stop their task
+ in this function.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ If all pads of an element are assigned to do
+ <quote>push</quote>-based scheduling, then this means that data
+ will be pushed by upstream elements to this element using the
+ sinkpads <function>_chain ()</function>-function. Pprerequisites
+ for this scheduling mode are that a chain-function was set for
+ each sinkpad using<function>gst_pad_set_chain_function ()</function>
+ and that all downstream elements operate in the same mode. Pads are
+ assigned to do push-based scheduling in sink-to-source element
+ order, and within an element first sourcepads and then sinkpads.
+ Sink elements can operate in this mode if their sinkpad is activated
+ for push-based scheduling. Source elements cannot be chain-based.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Alternatively, sinkpads can be the driving force behind a pipeline
+ by operating in <quote>pull</quote>-based mode, while the sourcepads
+ of the element still operate in push-based mode. In order to be the
+ driving force, those pads start a <classname>GstTask</classname>
+ when their pads are being activated. This task is a thread, which
+ will call a function specified by the element. When called, this
+ function will have random data access (through
+ <function>gst_pad_get_range ()</function>) over all sinkpads, and
+ can push data over the sourcepads, which effectively means that
+ this element controls dataflow in the pipeline. Prerequisites for
+ this mode are that all downstream elements can act in chain-based
+ mode, and that all upstream elements allow random access (see below).
+ Source elements can be told to act in this mode if their sourcepads
+ are activated in push-based fashion. Sink elements can be told to
+ act in this mode when their sinkpads are activated in pull-mode.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ lastly, all pads in an element can be assigned to act in pull-mode.
+ too. However, contrary to the above, this does not mean that they
+ start a task on their own. Rather, it means that they are pull
+ slave for the downstream element, and have to provide random data
+ access to it from their <function>_get_range ()</function>-function.
+ Requiremenents are that the a <function>_get_range
+ ()</function>-function was set on this pad using the function
+ <function>gst_pad_set_getrange_function ()</function>. Also, if
+ the element has any sinkpads, all those pads (and thereby their
+ peers) need to operate in random access mode, too. Note that the
+ element is supposed to activate those elements itself! &GStreamer;
+ will not do that for you.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ In the next two sections, we will go closer into pull-based scheduling
+ (elements/pads driving the pipeline, and elements/pads providing random
+ access), and some specific use cases will be given.
+ </para>
</sect1>
-</chapter>
-<chapter id="chapter-loopbased-loopfn">
- <title>How a loopfunc works</title>
- <para>
- A <function>_loop ()</function> function is a function that is called by
- the scheduler, but without providing data to the element. Instead, the
- element will become responsible for acquiring its own data, and it will
- still be responsible of sending data over to its source pads. This method
- noticeably complicates scheduling; you should only write loop-based
- elements when you need to. Normally, chain-based elements are preferred.
- Examples of elements that <emphasis>have</emphasis> to be loop-based are
- elements with multiple sink pads. Since the scheduler will push data into
- the pads as it comes (and this might not be synchronous), you will easily
- get asynchronous data on both pads, which means that the data that arrives
- on the first pad has a different display timestamp than the data arriving
- on the second pad at the same time. To get over these issues, you should
- write such elements in a loop-based form. Other elements that are
- <emphasis>easier</emphasis> to write in a loop-based form than in a
- chain-based form are demuxers and parsers. It is not required to write such
- elements in a loop-based form, though.
- </para>
- <para>
- Below is an example of the easiest loop-function that one can write:
- </para>
- <programlisting>
-static void gst_my_filter_loopfunc (GstElement *element);
+ <sect1 id="section-scheduling-loop" xreflabel="Pads driving the pipeline">
+ <title>Pads driving the pipeline</title>
+ <para>
+ Sinkpads assigned to operate in pull-based mode, while none of its
+ sourcepads operate in pull-based mode (or it has no sourcepads), can
+ start a task that will drive the pipeline dataflow. Within this
+ function, those elements have random access over all of their sinkpads,
+ and push data over their sourcepads. This can come in useful for
+ several different kinds of elements:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Demuxers, parsers and certain kinds of decoders where data comes
+ in unparsed (such as MPEG-audio or video streams), since those will
+ prefer byte-exact (random) access from their input. If possible,
+ however, such elements should be prepared to operate in chain-based
+ mode, too.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Certain kind of audio outputs, which require control over their
+ input dataflow, such as the Jack sound server.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ In order to start this task, you will need to create it in the
+ activation function.
+ </para>
+ <programlisting><!-- example-begin task.c a -->
+#include "filter.h"
+#include <string.h>
+
+static gboolean gst_my_filter_activate (GstPad * pad);
+static gboolean gst_my_filter_activate_pull (GstPad * pad,
+ gboolean active);
+static void gst_my_filter_loop (GstMyFilter * filter);
+
+GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);
+<!-- example-end task.c a -->
+<!-- example-begin task.c b --><!--
+static gboolean gst_my_filter_setcaps (GstPad *pad,
+ GstCaps *caps);
+static GstCaps *gst_my_filter_getcaps (GstPad *pad);
static void
-gst_my_filter_init (GstMyFilter *filter)
+gst_my_filter_base_init (gpointer klass)
{
-[..]
- gst_element_set_loopfunc (GST_ELEMENT (filter), gst_my_filter_loopfunc);
-[..]
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ static GstElementDetails my_filter_details = {
+ "An example plugin",
+ "Example/FirstExample",
+ "Shows the basic structure of a plugin",
+ "your name <your.name@your.isp>"
+ };
+ static GstStaticPadTemplate sink_factory =
+ GST_STATIC_PAD_TEMPLATE (
+ "sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+ );
+ static GstStaticPadTemplate src_factory =
+ GST_STATIC_PAD_TEMPLATE (
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+ );
+
+ gst_element_class_set_details (element_class, &my_filter_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_factory));
}
static void
-gst_my_filter_loopfunc (GstElement *element)
+gst_my_filter_class_init (GstMyFilterClass * klass)
{
- GstMyFilter *filter = GST_MY_FILTER (element);
- GstData *data;
-
- /* acquire data */
- data = gst_pad_pull (filter->sinkpad);
-
- /* send data */
- gst_pad_push (filter->srcpad, data);
}
- </programlisting>
- <para>
- Obviously, this specific example has no single advantage over a chain-based
- element, so you should never write such elements. However, it's a good
- introduction to the concept.
- </para>
-
- <sect1 id="section-loopfn-multiinput" xreflabel="Multi-Input Elements">
- <title>Multi-Input Elements</title>
- <para>
- Elements with multiple sink pads need to take manual control over their
- input to assure that the input is synchronized. The following example
- code could (should) be used in an aggregator, i.e. an element that takes
- input from multiple streams and sends it out intermangled. Not really
- useful in practice, but a good example, again.
- </para>
- <programlisting>
-<![CDATA[
-
-typedef struct _GstMyFilterInputContext {
- gboolean eos;
- GstBuffer *lastbuf;
-} GstMyFilterInputContext;
-
-[..]
-
+--><!-- example-begin task.c b -->
+<!-- example-begin task.c c -->
static void
-gst_my_filter_init (GstMyFilter *filter)
+gst_my_filter_init (GstMyFilter * filter)
{
+<!-- example-end task.c c -->
+[..]<!-- example-begin task.c d --><!--
GstElementClass *klass = GST_ELEMENT_GET_CLASS (filter);
- GstMyFilterInputContext *context;
- filter->sinkpad1 = gst_pad_new_from_template (
- gst_element_class_get_pad_template (klass, "sink"), "sink_1");
- context = g_new0 (GstMyFilterInputContext, 1);
- gst_pad_set_private_data (filter->sinkpad1, context);
-[..]
- filter->sinkpad2 = gst_pad_new_from_template (
- gst_element_class_get_pad_template (klass, "sink"), "sink_2");
- context = g_new0 (GstMyFilterInputContext, 1);
- gst_pad_set_private_data (filter->sinkpad2, context);
-[..]
- gst_element_set_loopfunc (GST_ELEMENT (filter),
- gst_my_filter_loopfunc);
+ filter->sinkpad = gst_pad_new_from_template (
+ gst_element_class_get_pad_template (klass, "sink"), "sink");
+ gst_pad_set_setcaps_function (filter->sinkpad, gst_my_filter_setcaps);
+ gst_pad_set_getcaps_function (filter->sinkpad, gst_my_filter_getcaps);
+--><!-- example-end task.c d -->
+<!-- example-begin task.c e -->
+ gst_pad_set_activate_function (filter->sinkpad, gst_my_filter_activate);
+ gst_pad_set_activatepull_function (filter->sinkpad,
+ gst_my_filter_activate_pull);
+<!-- example-end task.c e -->
+<!-- example-begin task.c f --><!--
+ gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+
+ filter->srcpad = gst_pad_new_from_template (
+ gst_element_class_get_pad_template (klass, "src"), "src");
+ gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+--><!-- example-end task.c f -->
+[..]<!-- example-begin task.c g -->
}
-
-[..]
-
-static void
-gst_my_filter_loopfunc (GstElement *element)
+<!-- example-end task.c g -->
+[..]<!-- example-begin task.c h --><!--
+#include "caps.func"
+--><!-- example-end task.c h -->
+<!-- example-begin task.c i -->
+static gboolean
+gst_my_filter_activate (GstPad * pad)
{
- GstMyFilter *filter = GST_MY_FILTER (element);
- GList *padlist;
- GstMyFilterInputContext *first_context = NULL;
-
- /* Go over each sink pad, update the cache if needed, handle EOS
- * or non-responding streams and see which data we should handle
- * next. */
- for (padlist = gst_element_get_padlist (element);
- padlist != NULL; padlist = g_list_next (padlist)) {
- GstPad *pad = GST_PAD (padlist->data);
- GstMyFilterInputContext *context = gst_pad_get_private_data (pad);
-
- if (GST_PAD_IS_SRC (pad))
- continue;
-
- while (GST_PAD_IS_USABLE (pad) &&
- !context->eos && !context->lastbuf) {
- GstData *data = gst_pad_pull (pad);
-
- if (GST_IS_EVENT (data)) {
- /* We handle events immediately */
- GstEvent *event = GST_EVENT (data);
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- context->eos = TRUE;
- gst_event_unref (event);
- break;
- case GST_EVENT_DISCONTINUOUS:
- g_warning ("HELP! How do I handle this?");
- /* fall-through */
- default:
- gst_pad_event_default (pad, event);
- break;
- }
- } else {
- /* We store the buffer to handle synchronization below */
- context->lastbuf = GST_BUFFER (data);
- }
- }
+ if (gst_pad_check_pull_range (pad)) {
+ return gst_pad_activate_pull (pad, TRUE);
+ } else {
+ return FALSE;
+ }
+}
- /* synchronize streams by always using the earliest buffer */
- if (context->lastbuf) {
- if (!first_context) {
- first_context = context;
- } else {
- if (GST_BUFFER_TIMESTAMP (context->lastbuf) <
- GST_BUFFER_TIMESTAMP (first_context->lastbuf))
- first_context = context;
- }
- }
+static gboolean
+gst_my_filter_activate_pull (GstPad *pad,
+ gboolean active)
+{
+ GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
+
+ if (active) {
+ filter->offset = 0;
+ return gst_pad_start_task (pad,
+ (GstTaskFunction) gst_my_filter_loop, filter);
+ } else {
+ return gst_pad_stop_task (pad);
}
+}
+ <!-- example-end task.c i --></programlisting>
+ <para>
+ Once started, your task has full control over input and output. The
+ most simple case of a task function is one that reads input and pushes
+ that over its source pad. It's not all that useful, but provides some
+ more flexibility than the old chain-based case that we've been looking
+ at so far.
+ </para>
+ <programlisting><!-- example-begin task.c j -->
+#define BLOCKSIZE 2048
- /* If we handle no data at all, we're at the end-of-stream, so
- * we should signal EOS. */
- if (!first_context) {
- gst_pad_push (filter->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
- gst_element_set_eos (element);
+static void
+gst_my_filter_loop (GstMyFilter * filter)
+{
+ guint64 len;
+ GstFormat fmt = GST_FORMAT_BYTES;
+ GstBuffer *buf = NULL;
+
+ if (!gst_pad_query_position (filter->sinkpad, &fmt, NULL, &len)) {
+ goto stop;
+ } else if (filter->offset >= len) {
+ gst_pad_push_event (filter->sinkpad, gst_event_new (GST_EVENT_EOS));
+ } else if (gst_pad_pull_range (filter->sinkpad, filter->offset,
+ BLOCKSIZE, &buf) != GST_FLOW_OK ||
+ gst_pad_push (filter->sinkpad, buf) != GST_FLOW_OK) {
+ goto stop;
+ } else {
+ filter->offset += BLOCKSIZE;
return;
}
- /* So we do have data! Let's forward that to our source pad. */
- gst_pad_push (filter->srcpad, GST_DATA (first_context->lastbuf));
- first_context->lastbuf = NULL;
+stop:
+ gst_pad_pause_task (filter->sinkpad);
}
-]]>
- </programlisting>
- <para>
- Note that a loop-function is allowed to return. Better yet, a loop
- function <emphasis>has to</emphasis> return so the scheduler can
- let other elements run (this is particularly true for the optimal
- scheduler). Whenever the scheduler feels right, it will call the
- loop-function of the element again.
- </para>
+<!-- example-end task.c j -->
+<!-- example-begin task.c k --><!--
+#include "register.func"
+ --><!-- example-end task.c k --></programlisting>
</sect1>
- <sect1 id="section-loopfn-bytestream" xreflabel="The Bytestream Object">
- <title>The Bytestream Object</title>
+ <sect1 id="section-scheduling-randomxs" xreflabel="Providing random access">
+ <title>Providing random access</title>
<para>
- A second type of elements that wants to be loop-based, are the so-called
- bytestream-elements. Until now, we've only dealt with elements that
- receive or pull full buffers of a random size from other elements. Often,
- however, it is wanted to have control over the stream at a byte-level,
- such as in stream parsers or demuxers. It is possible to manually pull
- buffers and merge them until a certain size; it is easier, however, to
- use bytestream, which wraps this behaviour.
+ In the previous section, we have talked about how elements (or pads)
+ that are assigned to drive the pipeline using their own task, have
+ random access over their sinkpads. This means that all elements linked
+ to those pads (recursively) need to provide random access functions.
+ Requesting random access is done using the function
+ <function>gst_pad_pull_range ()</function>, which requests a buffer of
+ a specified size and offset. Source pads implementing and assigned to
+ do random access will have a <function>_get_range ()</function>-function
+ set using <function>gst_pad_set_getrange_function ()</function>, and
+ that function will be called when the peer pad requests some data. The
+ element is then responsible for seeking to the right offset and
+ providing the requested data. Several elements can implement random
+ access:
</para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Data sources, such as a file source, that can provide data from any
+ offset with reasonable low latency.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Filters that would like to provide a pull-based-like scheduling
+ mode over the whole pipeline. Note that elements assigned to do
+ random access-based scheduling are themselves responsible for
+ assigning this scheduling mode to their upstream peers! &GStreamer;
+ will not do that for you.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Parsers who can easily provide this by skipping a small part of
+ their input and are thus essentially "forwarding" random access
+ requests literally without any own processing involved. Examples
+ include tag readers (e.g. ID3) or single output parsers, such as
+ a WAVE parser.
+ </para>
+ </listitem>
+ </itemizedlist>
<para>
- To use bytestream, you need to load the bytestream when your plugin is
- loaded; you should do this before registering the element, which you
- learned previously in <xref linkend="section-boiler-plugininit"/>.
- After that, all functions of the bytestream plugin are available in
- your plugin as well.
+ The following example will show how a <function>_get_range
+ ()</function>-function can be implemented in a source element:
</para>
- <programlisting>
-#include <gst/bytestream/bytestream.h>
-
-static gboolean
-plugin_init (GstPlugin *plugin)
+ <programlisting><!-- example-begin range.c a -->
+#include "filter.h"
+static GstFlowReturn
+ gst_my_filter_get_range (GstPad * pad,
+ guint64 offset,
+ guint length,
+ GstBuffer ** buf);
+
+GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);
+<!-- example-end range.c a -->
+<!-- example-begin range.c b --><!--
+static void
+gst_my_filter_base_init (gpointer klass)
{
- if (!gst_library_load ("gstbytestream"))
- return FALSE;
-
- /* and now, actually register the element */
-[..]
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ static GstElementDetails my_filter_details = {
+ "An example plugin",
+ "Example/FirstExample",
+ "Shows the basic structure of a plugin",
+ "your name <your.name@your.isp>"
+ };
+ static GstStaticPadTemplate src_factory =
+ GST_STATIC_PAD_TEMPLATE (
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+ );
+
+ gst_element_class_set_details (element_class, &my_filter_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_factory));
}
- </programlisting>
- <para>
- Bytestream-using elements are usually stream parsers or demuxers. For
- now, we will take a parser as an example. Demuxers require some more
- magic that will be dealt with later in this guide:
- <xref linkend="chapter-advanced-request"/>. The goal of this parser will be
- to parse a text-file and to push each line of text as a separate buffer
- over its source pad.
- </para>
- <programlisting>
-<![CDATA[
+
static void
-gst_my_filter_loopfunc (GstElement *element)
+gst_my_filter_class_init (GstMyFilterClass * klass)
{
- GstMyFilter *filter = GST_MY_FILTER (element);
- gint n, num;
- guint8 *data;
-
- for (n = 0; ; n++) {
- num = gst_bytestream_peek_bytes (filter->bs, &data, n + 1);
- if (num != n + 1) {
- GstEvent *event = NULL;
- guint remaining;
-
- gst_bytestream_get_status (filter->bs, &remaining, &event);
- if (event) {
- if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)) {
- /* end-of-file */
- gst_pad_push (filter->srcpad, GST_DATA (event));
- gst_element_set_eos (element);
-
- return;
- }
- gst_event_unref (event);
- }
-
- /* failed to read - throw error and bail out */
- gst_element_error (element, STREAM, READ, (NULL), (NULL));
-
- return;
- }
-
- /* check if the last character is a newline */
- if (data[n] == '\n') {
- GstBuffer *buf = gst_buffer_new_and_alloc (n + 1);
-
- /* read the line of text without newline - then flush the newline */
- gst_bytestream_peek_data (filter->bs, &data, n);
- memcpy (GST_BUFFER_DATA (buf), data, n);
- GST_BUFFER_DATA (buf)[n] = '\0';
- gst_bytestream_flush_fast (filter->bs, n + 1);
- g_print ("Pushing '%s'\n", GST_BUFFER_DATA (buf));
- gst_pad_push (filter->srcpad, GST_DATA (buf));
-
- return;
- }
- }
}
-
+--><!-- example-begin range.c b -->
+<!-- example-begin range.c c -->
static void
-gst_my_filter_change_state (GstElement *element)
+gst_my_filter_init (GstMyFilter * filter)
{
- GstMyFilter *filter = GST_MY_FILTER (element);
-
- switch (GST_STATE_TRANSITION (element)) {
- case GST_STATE_READY_TO_PAUSED:
- filter->bs = gst_bytestream_new (filter->sinkpad);
- break;
- case GST_STATE_PAUSED_TO_READY:
- gst_bytestream_destroy (filter->bs);
- break;
- default:
- break;
- }
-
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+ GstElementClass *klass = GST_ELEMENT_GET_CLASS (filter);
- return GST_STATE_SUCCESS;
+ filter->srcpad = gst_pad_new_from_template (
+ gst_element_class_get_pad_template (klass, "src"), "src");
+ gst_pad_set_getrange_function (filter->srcpad,
+ gst_my_filter_get_range);
+ gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+<!-- example-end range.c c -->
+[..]<!-- example-begin range.c d -->
}
-]]>
- </programlisting>
- <para>
- In the above example, you'll notice how bytestream handles buffering of
- data for you. The result is that you can handle the same data multiple
- times. Event handling in bytestream is currently sort of
- <emphasis>wacky</emphasis>, but it works quite well. The one big
- disadvantage of bytestream is that it <emphasis>requires</emphasis>
- the element to be loop-based. Long-term, we hope to have a chain-based
- usable version of bytestream, too.
- </para>
- </sect1>
- <sect1 id="section-loopbased-secnd">
- <title>Adding a second output</title>
- <para>
- WRITEME
- </para>
- </sect1>
+static gboolean
+gst_my_filter_get_range (GstPad * pad,
+ guint64 offset,
+ guint length,
+ GstBuffer ** buf)
+{
+<!-- example-end range.c d -->
+ GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));
- <sect1 id="section-loopbased-modappl">
- <title>Modifying the test application</title>
+ [.. here, you would fill *buf ..]
+<!-- example-begin range.c e -->
+ return GST_FLOW_OK;
+}
+<!-- example-end range.c e -->
+<!-- example-begin range.c f --><!--
+#include "register.func"
+ --><!-- example-end range.c f --></programlisting>
<para>
- WRITEME
+ In practice, many elements that could theoretically do random access,
+ may in practice often be assigned to do push-based scheduling anyway,
+ since there is no downstream element able to start its own task.
+ Therefore, in practice, those elements should implement both a
+ <function>_get_range ()</function>-function and a <function>_chain
+ ()</function>-function (for filters and parsers) or a <function>_get_range
+ ()</function>-function and be prepared to start their own task by
+ providing <function>_activate_* ()</function>-functions (for
+ source elements), so that &GStreamer; can decide for the optimal
+ scheduling mode and have it just work fine in practice.
</para>
</sect1>
</chapter>
-