aboutsummaryrefslogtreecommitdiff
path: root/docs/manual/advanced-autoplugging.xml
diff options
context:
space:
mode:
Diffstat (limited to 'docs/manual/advanced-autoplugging.xml')
-rw-r--r--docs/manual/advanced-autoplugging.xml387
1 files changed, 3 insertions, 384 deletions
diff --git a/docs/manual/advanced-autoplugging.xml b/docs/manual/advanced-autoplugging.xml
index 984617b..5f7a0e0 100644
--- a/docs/manual/advanced-autoplugging.xml
+++ b/docs/manual/advanced-autoplugging.xml
@@ -10,7 +10,7 @@
by looking at all available elements in a system. This process is called
autoplugging, and &GStreamer; contains high-quality autopluggers. If
you're looking for an autoplugger, don't read any further and go to
- <xref linkend="chapter-components"/>. This chapter will explain the
+ <xref linkend="chapter-playback-components"/>. This chapter will explain the
<emphasis>concept</emphasis> of autoplugging and typefinding. It will
explain what systems &GStreamer; includes to dynamically detect the
type of a media stream, and how to generate a pipeline of decoder
@@ -234,390 +234,9 @@ main (gint argc,
<sect1 id="section-dynamic">
<title>Dynamically autoplugging a pipeline</title>
- <warning><para>
- The code in this section is broken, outdated and overly complicated.
- Also, you should use decodebin, playbin or uridecodebin to get
- decoders plugged automatically.
- </para></warning>
- <para>
- In this chapter we will see how you can create a dynamic pipeline. A
- dynamic pipeline is a pipeline that is updated or created while data
- is flowing through it. We will create a partial pipeline first and add
- more elements while the pipeline is playing. The basis of this player
- will be the application that we wrote in the previous section (<xref
- linkend="section-typefinding"/>) to identify unknown media streams.
- </para>
- <!-- example-begin dynamic.c a --><!--
-#include &lt;gst/gst.h&gt;
-
-GstElement *pipeline;
- --><!-- example-end dynamic.c a -->
- <para>
- Once the type of the media has been found, we will find elements in
- the registry that can decode this streamtype. For this, we will get
- all element factories (which we've seen before in <xref
- linkend="section-elements-create"/>) and find the ones with the
- given media type and capabilities on their sinkpad. Note that we will
- only use parsers, demuxers and decoders. We will not use factories for
- any other element types, or we might get into a loop of encoders and
- decoders. For this, we will want to build a list of <quote>allowed</quote>
- factories right after initializing &GStreamer;.
- </para>
- <programlisting><!-- example-begin dynamic.c b -->
-static GList *factories;
-
-/*
- * This function is called by the registry loader. Its return value
- * (TRUE or FALSE) decides whether the given feature will be included
- * in the list that we're generating further down.
- */
-
-static gboolean
-cb_feature_filter (GstPluginFeature *feature,
- gpointer data)
-{
- const gchar *klass;
- guint rank;
-
- /* we only care about element factories */
- if (!GST_IS_ELEMENT_FACTORY (feature))
- return FALSE;
-
- /* only parsers, demuxers and decoders */
- klass = gst_element_factory_get_metadata (GST_ELEMENT_FACTORY (feature), GST_ELEMENT_METADATA_KLASS);
- if (g_strrstr (klass, "Demux") == NULL &amp;&amp;
- g_strrstr (klass, "Decoder") == NULL &amp;&amp;
- g_strrstr (klass, "Parse") == NULL)
- return FALSE;
-
- /* only select elements with autoplugging rank */
- rank = gst_plugin_feature_get_rank (feature);
- if (rank &lt; GST_RANK_MARGINAL)
- return FALSE;
-
- return TRUE;
-}
-
-/*
- * This function is called to sort features by rank.
- */
-
-static gint
-cb_compare_ranks (GstPluginFeature *f1,
- GstPluginFeature *f2)
-{
- return gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
-}
-
-static void
-init_factories (void)
-{
- /* first filter out the interesting element factories */
- factories = gst_registry_feature_filter (
- gst_registry_get (),
- (GstPluginFeatureFilter) cb_feature_filter, FALSE, NULL);
-
- /* sort them according to their ranks */
- factories = g_list_sort (factories, (GCompareFunc) cb_compare_ranks);
-}
- <!-- example-end dynamic.c b --></programlisting>
- <para>
- From this list of element factories, we will select the one that most
- likely will help us decoding a media stream to a given output type.
- For each newly created element, we will again try to autoplug new
- elements to its source pad(s). Also, if the element has dynamic pads
- (which we've seen before in <xref linkend="section-pads-dynamic"/>),
- we will listen for newly created source pads and handle those, too.
- The following code replaces the <function>cb_type_found</function>
- from the previous section with a function to initiate autoplugging,
- which will continue with the above approach.
- </para>
- <programlisting><!-- example-begin dynamic.c c -->
-static void try_to_plug (GstPad *pad, GstCaps *caps);
-
-static GstElement *audiosink;
-
-static void
-cb_newpad (GstElement *element,
- GstPad *pad,
- gpointer data)
-{
- GstCaps *caps;
-
- caps = gst_pad_query_caps (pad, NULL);
- try_to_plug (pad, caps);
- gst_caps_unref (caps);
-}
-
-static void
-close_link (GstPad *srcpad,
- GstElement *sinkelement,
- const gchar *padname,
- const GList *templlist)
-{
- GstPad *pad;
- gboolean has_dynamic_pads = FALSE;
-
- g_print ("Plugging pad %s:%s to newly created %s:%s\n",
- gst_object_get_name (GST_OBJECT (gst_pad_get_parent (srcpad))),
- gst_pad_get_name (srcpad),
- gst_object_get_name (GST_OBJECT (sinkelement)), padname);
-
- /* add the element to the pipeline and set correct state */
- if (sinkelement != audiosink) {
- gst_bin_add (GST_BIN (pipeline), sinkelement);
- gst_element_set_state (sinkelement, GST_STATE_READY);
- }
- pad = gst_element_get_static_pad (sinkelement, padname);
- gst_pad_link (srcpad, pad);
- if (sinkelement != audiosink) {
- gst_element_set_state (sinkelement, GST_STATE_PAUSED);
- }
- gst_object_unref (GST_OBJECT (pad));
-
- /* if we have static source pads, link those. If we have dynamic
- * source pads, listen for pad-added signals on the element */
- for ( ; templlist != NULL; templlist = templlist->next) {
- GstStaticPadTemplate *templ = templlist->data;
-
- /* only sourcepads, no request pads */
- if (templ->direction != GST_PAD_SRC ||
- templ->presence == GST_PAD_REQUEST) {
- continue;
- }
-
- switch (templ->presence) {
- case GST_PAD_ALWAYS: {
- GstPad *pad = gst_element_get_static_pad (sinkelement, templ->name_template);
- GstCaps *caps = gst_pad_query_caps (pad, NULL);
-
- /* link */
- try_to_plug (pad, caps);
- gst_object_unref (GST_OBJECT (pad));
- gst_caps_unref (caps);
- break;
- }
- case GST_PAD_SOMETIMES:
- has_dynamic_pads = TRUE;
- break;
- default:
- break;
- }
- }
-
- /* listen for newly created pads if this element supports that */
- if (has_dynamic_pads) {
- g_signal_connect (sinkelement, "pad-added", G_CALLBACK (cb_newpad), NULL);
- }
-}
-
-static void
-try_to_plug (GstPad *pad,
- GstCaps *caps)
-{
- GstObject *parent = GST_OBJECT (GST_OBJECT_PARENT (pad));
- const gchar *media;
- const GList *item;
- GstCaps *res, *audiocaps;
-
- /* don't plug if we're already plugged - FIXME: memleak for pad */
- if (GST_PAD_IS_LINKED (gst_element_get_static_pad (audiosink, "sink"))) {
- g_print ("Omitting link for pad %s:%s because we're already linked\n",
- GST_OBJECT_NAME (parent), GST_OBJECT_NAME (pad));
- return;
- }
-
- /* as said above, we only try to plug audio... Omit video */
- media = gst_structure_get_name (gst_caps_get_structure (caps, 0));
- if (g_strrstr (media, "video")) {
- g_print ("Omitting link for pad %s:%s because media type %s is non-audio\n",
- GST_OBJECT_NAME (parent), GST_OBJECT_NAME (pad), media);
- return;
- }
-
- /* can it link to the audiopad? */
- audiocaps = gst_pad_query_caps (gst_element_get_static_pad (audiosink, "sink"),
- NULL);
- res = gst_caps_intersect (caps, audiocaps);
- if (res &amp;&amp; !gst_caps_is_empty (res)) {
- g_print ("Found pad to link to audiosink - plugging is now done\n");
- close_link (pad, audiosink, "sink", NULL);
- gst_caps_unref (audiocaps);
- gst_caps_unref (res);
- return;
- }
- gst_caps_unref (audiocaps);
- gst_caps_unref (res);
-
- /* try to plug from our list */
- for (item = factories; item != NULL; item = item->next) {
- GstElementFactory *factory = GST_ELEMENT_FACTORY (item->data);
- const GList *pads;
-
- for (pads = gst_element_factory_get_static_pad_templates (factory);
- pads != NULL; pads = pads->next) {
- GstStaticPadTemplate *templ = pads->data;
-
- /* find the sink template - need an always pad*/
- if (templ->direction != GST_PAD_SINK ||
- templ->presence != GST_PAD_ALWAYS) {
- continue;
- }
-
- /* can it link? */
- res = gst_caps_intersect (caps,
- gst_static_caps_get (&amp;templ->static_caps));
- if (res &amp;&amp; !gst_caps_is_empty (res)) {
- GstElement *element;
- gchar *name_template = g_strdup (templ->name_template);
-
- /* close link and return */
- gst_caps_unref (res);
- element = gst_element_factory_create (factory, NULL);
- close_link (pad, element, name_template,
- gst_element_factory_get_static_pad_templates (factory));
- g_free (name_template);
- return;
- }
- gst_caps_unref (res);
-
- /* we only check one sink template per factory, so move on to the
- * next factory now */
- break;
- }
- }
-
- /* if we get here, no item was found */
- g_print ("No compatible pad found to decode %s on %s:%s\n",
- media, GST_OBJECT_NAME (parent), GST_OBJECT_NAME (pad));
-}
-
-static void
-cb_typefound (GstElement *typefind,
- guint probability,
- GstCaps *caps,
- gpointer data)
-{
- gchar *s;
- GstPad *pad;
-
- s = gst_caps_to_string (caps);
- g_print ("Detected media type %s\n", s);
- g_free (s);
-
- /* actually plug now */
- pad = gst_element_get_static_pad (typefind, "src");
- try_to_plug (pad, caps);
- gst_object_unref (GST_OBJECT (pad));
-}
- <!-- example-end dynamic.c c --></programlisting>
- <para>
- By doing all this, we will be able to make a simple autoplugger that
- can automatically setup a pipeline for any media type. In the example
- above, we did this for audio only. However, we can also do this
- for video to create a player that plays both audio and video.
- </para>
- <!-- example-begin dynamic.c d --><!--
-static gboolean
-my_bus_callback (GstBus *bus,
- GstMessage *message,
- gpointer data)
-{
- GMainLoop *loop = data;
-
- switch (GST_MESSAGE_TYPE (message)) {
- case GST_MESSAGE_ERROR: {
- GError *err;
- gchar *debug;
-
- gst_message_parse_error (message, &amp;err, &amp;debug);
- g_print ("Error: %s\n", err-&gt;message);
- g_error_free (err);
- g_free (debug);
-
- g_main_loop_quit (loop);
- break;
- }
- case GST_MESSAGE_EOS:
- /* end-of-stream */
- g_main_loop_quit (loop);
- break;
- default:
- break;
- }
-
- /* remove from queue */
- return TRUE;
-}
-
-gint
-main (gint argc,
- gchar *argv[])
-{
- GMainLoop *loop;
- GstElement *typefind, *realsink;
- GstBus *bus;
- GError *err = NULL;
- gchar *p;
-
- /* init GStreamer and ourselves */
- gst_init (&amp;argc, &amp;argv);
- loop = g_main_loop_new (NULL, FALSE);
- init_factories ();
-
- /* args */
- if (argc != 2) {
- g_print ("Usage: %s &lt;filename&gt;\n", argv[0]);
- return -1;
- }
-
- /* pipeline */
- p = g_strdup_printf ("filesrc location=\"%s\" ! typefind name=tf", argv[1]);
- pipeline = gst_parse_launch (p, &amp;err);
- g_free (p);
-
- if (err) {
- g_error ("Could not construct pipeline: %s", err-&gt;message);
- g_error_free (err);
- return -1;
- }
-
- bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
- gst_bus_add_watch (bus, my_bus_callback, NULL);
- gst_object_unref (bus);
-
- typefind = gst_bin_get_by_name (GST_BIN (pipeline), "tf");
- g_signal_connect (typefind, "have-type", G_CALLBACK (cb_typefound), NULL);
- gst_object_unref (GST_OBJECT (typefind));
- audiosink = gst_element_factory_make ("audioconvert", "aconv");
- realsink = gst_element_factory_make ("alsasink", "audiosink");
- gst_bin_add_many (GST_BIN (pipeline), audiosink, realsink, NULL);
- gst_element_link (audiosink, realsink);
- gst_element_set_state (pipeline, GST_STATE_PLAYING);
-
- /* run */
- g_main_loop_run (loop);
-
- /* exit */
- gst_element_set_state (pipeline, GST_STATE_NULL);
- gst_object_unref (GST_OBJECT (pipeline));
-
- return 0;
-}
- --><!-- example-end dynamic.c d -->
<para>
- The example above is a good first try for an autoplugger. Next steps
- would be to listen for <quote>pad-removed</quote> signals, so we
- can dynamically change the plugged pipeline if the stream changes
- (this happens for DVB or Ogg radio). Also, you might want special-case
- code for input with known content (such as a DVD or an audio-CD),
- and much, much more. Moreover, you'll want many checks to prevent
- infinite loops during autoplugging, maybe you'll want to implement
- shortest-path-finding to make sure the most optimal pipeline is chosen,
- and so on. Basically, the features that you implement in an autoplugger
- depend on what you want to use it for. For full-blown implementations,
- see the <quote>playbin</quote> and <quote>decodebin</quote> elements in
- <xref linkend="chapter-components"/>.
+ See <xref linkend="chapter-playback-components"/> for using the high
+ level object that you can use to dynamically construct pipelines.
</para>
</sect1>
</chapter>