diff options
Diffstat (limited to 'docs/manual/advanced-dataaccess.xml')
-rw-r--r-- | docs/manual/advanced-dataaccess.xml | 357 |
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> |