aboutsummaryrefslogtreecommitdiff
path: root/docs/manual/advanced-dataaccess.xml
diff options
context:
space:
mode:
Diffstat (limited to 'docs/manual/advanced-dataaccess.xml')
-rw-r--r--docs/manual/advanced-dataaccess.xml357
1 files changed, 297 insertions, 60 deletions
diff --git a/docs/manual/advanced-dataaccess.xml b/docs/manual/advanced-dataaccess.xml
index 28fc56c..983d757 100644
--- a/docs/manual/advanced-dataaccess.xml
+++ b/docs/manual/advanced-dataaccess.xml
@@ -1248,6 +1248,7 @@ main (int argc, char **argv)
in the pipeline, it is possible that the pipeline needs to
negotiate a new format and this can fail. Usually you can fix this
by inserting the right converter elements where needed.
+ See also <xref linkend="section-dynamic-changing"/>.
</para>
</listitem>
</itemizedlist>
@@ -1262,77 +1263,313 @@ main (int argc, char **argv)
<sect2 id="section-dynamic-changing">
<title>Changing elements in a pipeline</title>
<para>
- WRITEME
+ In the next example we look at the following chain of elements:
</para>
- </sect2>
- </sect1>
+ <programlisting>
+ - ----. .----------. .---- -
+ element1 | | element2 | | element3
+ src -> sink src -> sink
+ - ----' '----------' '---- -
+ </programlisting>
+ <para>
+ We want to change element2 by element4 while the pipeline is in
+ the PLAYING state. Let's say that element2 is a visualization and
+ that you want to switch the visualization in the pipeline.
+ </para>
+ <para>
+ We can't just unlink element2's sinkpad from element1's source
+ pad because that would leave element1's source pad
+ unlinked and would cause a streaming error in the pipeline when
+ data is pushed on the source pad.
+ The technique is to block the dataflow from element1's source pad
+ before we change element2 by element4 and then resume dataflow
+ as shown in the following steps:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Block element1's source pad with a blocking pad probe. When the
+ pad is blocked, the probe callback will be called.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Inside the block callback nothing is flowing between element1
+ and element2 and nothing will flow until unblocked.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Unlink element1 and element2.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Make sure data is flushed out of element2. Some elements might
+ internally keep some data, you need to make sure not to lose data
+ by forcing it out of element2. You can do this by pushing EOS into
+ element2, like this:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Put an event probe on element2's source pad.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Send EOS to element2's sinkpad. This makes sure the all the
+ data inside element2 is forced out.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Wait for the EOS event to appear on element2's source pad.
+ When the EOS is received, drop it and remove the event
+ probe.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ <listitem>
+ <para>
+ Unlink element2 and element3. You can now also remove element2
+ from the pipeline and set the state to NULL.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Add element4 to the pipeline, if not already added. Link element4
+ and element3. Link element1 and element4.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Make sure element4 is in the same state as the rest of the elements
+ in the pipeline. It should be at least in the PAUSED state before
+ it can receive buffers and events.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Unblock element1's source pad probe. This will let new data into
+ element4 and continue streaming.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ The above algorithm works when the source pad is blocked, i.e. when
+ there is dataflow in the pipeline. If there is no dataflow, there is
+ also no point in changing the element (just yet) so this algorithm can
+ be used in the PAUSED state as well.
+ </para>
+ <para>
+ Let show you how this works with an example. This example changes the
+ video effect on a simple pipeline every second.
+ </para>
+ <programlisting>
+<!-- example-begin effectswitch.c -->
+<![CDATA[
+#include <gst/gst.h>
- <sect1 id="section-data-manager">
- <title>Embedding static elements in your application</title>
- <para>
- The <ulink type="http"
- url="http://gstreamer.freedesktop.org/data/doc/gstreamer/head/pwg/html/index.html">Plugin
- Writer's Guide</ulink> describes in great detail how to write elements
- for the &GStreamer; framework. In this section, we will solely discuss
- how to embed such elements statically in your application. This can be
- useful for application-specific elements that have no use elsewhere in
- &GStreamer;.
- </para>
- <para>
- Dynamically loaded plugins contain a structure that's defined using
- <function>GST_PLUGIN_DEFINE ()</function>. This structure is loaded
- when the plugin is loaded by the &GStreamer; core. The structure
- contains an initialization function (usually called
- <function>plugin_init</function>) that will be called right after that.
- It's purpose is to register the elements provided by the plugin with
- the &GStreamer; framework.
- If you want to embed elements directly in
- your application, the only thing you need to do is to replace
- <function>GST_PLUGIN_DEFINE ()</function> with a call to
- <function>gst_plugin_register_static ()</function>. As soon as you
- call <function>gst_plugin_register_static ()</function>, the elements
- will from then on be available like any other element, without them
- having to be dynamically loadable libraries. In the example below, you
- would be able to call <function>gst_element_factory_make
- ("my-element-name", "some-name")</function> to create an instance of the
- element.
- </para>
+static gchar *opt_effects = NULL;
- <programlisting>
-<![CDATA[
-/*
- * Here, you would write the actual plugin code.
- */
+#define DEFAULT_EFFECTS "identity,exclusion,navigationtest," \
+ "agingtv,videoflip,vertigotv,gaussianblur,shagadelictv,edgetv"
+
+static GstPad *blockpad;
+static GstElement *conv_before;
+static GstElement *conv_after;
+static GstElement *cur_effect;
+static GstElement *pipeline;
+
+static GQueue effects = G_QUEUE_INIT;
+
+static GstPadProbeReturn
+event_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+ GMainLoop *loop = user_data;
+ GstElement *next;
+
+ if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_DATA (info)) != GST_EVENT_EOS)
+ return GST_PAD_PROBE_OK;
+
+ gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
+
+ /* push current event back into the queue */
+ g_queue_push_tail (&effects, gst_object_ref (cur_effect));
+ /* take next effect from the queue */
+ next = g_queue_pop_head (&effects);
+ if (next == NULL) {
+ GST_DEBUG_OBJECT (pad, "no more effects");
+ g_main_loop_quit (loop);
+ return GST_PAD_PROBE_DROP;
+ }
+
+ g_print ("Switching from '%s' to '%s'..\n", GST_OBJECT_NAME (cur_effect),
+ GST_OBJECT_NAME (next));
+
+ gst_element_set_state (cur_effect, GST_STATE_NULL);
+
+ /* remove unlinks automatically */
+ GST_DEBUG_OBJECT (pipeline, "removing %" GST_PTR_FORMAT, cur_effect);
+ gst_bin_remove (GST_BIN (pipeline), cur_effect);
-[..]
+ GST_DEBUG_OBJECT (pipeline, "adding %" GST_PTR_FORMAT, next);
+ gst_bin_add (GST_BIN (pipeline), next);
+
+ GST_DEBUG_OBJECT (pipeline, "linking..");
+ gst_element_link_many (conv_before, next, conv_after, NULL);
+
+ gst_element_set_state (next, GST_STATE_PLAYING);
+
+ cur_effect = next;
+ GST_DEBUG_OBJECT (pipeline, "done");
+
+ return GST_PAD_PROBE_DROP;
+}
+
+static GstPadProbeReturn
+pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+ GstPad *srcpad, *sinkpad;
+
+ GST_DEBUG_OBJECT (pad, "pad is blocked now");
+
+ /* remove the probe first */
+ gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
+
+ /* install new probe for EOS */
+ srcpad = gst_element_get_static_pad (cur_effect, "src");
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BLOCK |
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, event_probe_cb, user_data, NULL);
+ gst_object_unref (srcpad);
+
+ /* push EOS into the element, the probe will be fired when the
+ * EOS leaves the effect and it has thus drained all of its data */
+ sinkpad = gst_element_get_static_pad (cur_effect, "sink");
+ gst_pad_send_event (sinkpad, gst_event_new_eos ());
+ gst_object_unref (sinkpad);
+
+ return GST_PAD_PROBE_OK;
+}
static gboolean
-register_elements (GstPlugin *plugin)
+timeout_cb (gpointer user_data)
{
- return gst_element_register (plugin, "my-element-name",
- GST_RANK_NONE, MY_PLUGIN_TYPE);
+ gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ pad_probe_cb, user_data, NULL);
+
+ return TRUE;
}
-static
-my_code_init (void)
+static gboolean
+bus_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
{
- ...
-
- gst_plugin_register_static (
- GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "my-private-plugins",
- "Private elements of my application",
- register_elements,
- VERSION,
- "LGPL",
- "my-application-source",
- "my-application",
- "http://www.my-application.net/")
-
- ...
+ GMainLoop *loop = user_data;
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_ERROR:{
+ GError *err = NULL;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ gst_object_default_error (msg->src, err, dbg);
+ g_error_free (err);
+ g_free (dbg);
+ g_main_loop_quit (loop);
+ break;
+ }
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ GOptionEntry options[] = {
+ {"effects", 'e', 0, G_OPTION_ARG_STRING, &opt_effects,
+ "Effects to use (comma-separated list of element names)", NULL},
+ {NULL}
+ };
+ GOptionContext *ctx;
+ GError *err = NULL;
+ GMainLoop *loop;
+ GstElement *src, *sink;
+ gchar **effect_names, **e;
+
+ ctx = g_option_context_new ("");
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing: %s\n", err->message);
+ return 1;
+ }
+ g_option_context_free (ctx);
+
+ if (opt_effects != NULL)
+ effect_names = g_strsplit (opt_effects, ",", -1);
+ else
+ effect_names = g_strsplit (DEFAULT_EFFECTS, ",", -1);
+
+ for (e = effect_names; e != NULL && *e != NULL; ++e) {
+ GstElement *el;
+
+ el = gst_element_factory_make (*e, NULL);
+ if (el) {
+ g_print ("Adding effect '%s'\n", *e);
+ g_queue_push_tail (&effects, el);
+ }
+ }
+
+ pipeline = gst_pipeline_new ("pipeline");
+
+ src = gst_element_factory_make ("videotestsrc", NULL);
+ g_object_set (src, "is-live", TRUE, NULL);
+
+ blockpad = gst_element_get_static_pad (src, "src");
+
+ conv_before = gst_element_factory_make ("videoconvert", NULL);
+
+ cur_effect = g_queue_pop_head (&effects);
+
+ conv_after = gst_element_factory_make ("videoconvert", NULL);
+
+ sink = gst_element_factory_make ("ximagesink", NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, conv_before, cur_effect,
+ conv_after, sink, NULL);
+
+ gst_element_link_many (src, conv_before, cur_effect, conv_after,
+ sink, NULL);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_cb, loop);
+
+ g_timeout_add_seconds (1, timeout_cb, loop);
+
+ g_main_loop_run (loop);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ return 0;
}
]]>
- </programlisting>
+<!-- example-end effectswitch.c -->
+ </programlisting>
+ <para>
+ Note how we added videoconvert elements before and after the effect.
+ This is needed because some elements might operate in different
+ colorspaces than other elements. By inserting the conversion elements
+ you ensure that the right format can be negotiated at any time.
+ </para>
+ </sect2>
</sect1>
+
</chapter>