--- /home/ensonic/projects/gstreamer/gst-plugins-base/configure.ac	2008-12-10 09:29:30.000000000 +0200
+++ ./configure.ac	2008-10-23 16:12:05.000000000 +0300
@@ -297,6 +297,7 @@ AG_GST_CHECK_PLUGIN(gdp)
 AG_GST_CHECK_PLUGIN(playback)
 AG_GST_CHECK_PLUGIN(speexresample)
 AG_GST_CHECK_PLUGIN(subparse)
+AG_GST_CHECK_PLUGIN(tagread)
 AG_GST_CHECK_PLUGIN(tcp)
 AG_GST_CHECK_PLUGIN(typefind)
 AG_GST_CHECK_PLUGIN(videotestsrc)
@@ -656,6 +652,7 @@ gst/videotestsrc/Makefile
 gst/videorate/Makefile
 gst/videoscale/Makefile
 gst/volume/Makefile
+gst/tagread/Makefile
 sys/Makefile
 sys/ximage/Makefile
 sys/xvimage/Makefile
@@ -717,6 +714,7 @@ pkgconfig/gstreamer-video-uninstalled.pc
 pkgconfig/gstreamer-plugins-base.pc
 pkgconfig/gstreamer-plugins-base-uninstalled.pc
 tests/Makefile
+tests/benchmarks/Makefile
 tests/check/Makefile
 tests/examples/Makefile
 tests/examples/dynamic/Makefile
--- /dev/null	2008-11-17 13:44:35.000000000 +0200
+++ gst/tagread/Makefile.am	2008-10-23 16:12:02.000000000 +0300
@@ -0,0 +1,11 @@
+plugin_LTLIBRARIES = libgsttagreadbin.la
+
+libgsttagreadbin_la_SOURCES = tagreadbin.c
+libgsttagreadbin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
+libgsttagreadbin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgsttagreadbin_la_LIBADD = \
+	$(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \
+	$(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \
+	$(GST_BASE_LIBS) $(GST_LIBS)
+
+noinst_HEADERS = tagreadbin.h
--- /dev/null	2008-11-17 13:44:35.000000000 +0200
+++ gst/tagread/tagreadbin.c	2008-10-23 16:12:02.000000000 +0300
@@ -0,0 +1,1274 @@
+/* GStreamer tag reader bin
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-tagreadbin
+ * @short_description: Tag reader bin
+ *
+ * <refsect2>
+ * <para>
+ * #GstBin that auto-magically constructs a tag reading pipeline using
+ * available demuxers and parsers via auto-plugging. It filters only elements
+ * which implement #GstTagReader interface.
+ * </para>
+ * <para>
+ * TagReadBin is able to autoplug multiple tagreaders in a row. It stops
+ * autoplugging when there is no element that is capable of handling the
+ * caps from earlier element. The code is heavily based on DecodeBin2 element.
+ * </para>
+ * <para>
+ * This element is used by linking it into a source element and setting the
+ * pipeline into PLAYING mode. TagReadBin collects all the tags from tagreader
+ * elements and merges them. When all tagreaders have sent their tags,
+ * TagReadBin sends the tags forward to application and sends EOS message.
+ * This EOS indicates that tag reading is finished and pipeline can be
+ * shut down.
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <gst/gst.h>
+#include <gst/interfaces/tagreader.h>
+#include <gst/pbutils/pbutils.h>
+
+#include "tagreadbin.h"
+
+#define MERGE_TAGS_FIRST
+
+/* generic templates */
+static GstStaticPadTemplate decoder_bin_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (gst_tagreader_bin_debug);
+#define GST_CAT_DEFAULT gst_tagreader_bin_debug
+
+static GstBinClass *parent_class;
+
+static const GstElementDetails gst_tagreader_bin_details =
+GST_ELEMENT_DETAILS ("Tag Reader Bin",
+    "Generic/Bin/Parser",
+    "Read metadata information from media",
+    "Stefan Kost <stefan.kost@nokia.com>");
+
+
+static void do_async_start (GstTagReaderBin * trbin);
+static void do_async_done (GstTagReaderBin * trbin);
+
+static void type_found (GstElement * typefind, guint probability,
+    GstCaps * caps, GstTagReaderBin * tagreader_bin);
+
+static void caps_notify_cb (GstPad * pad, GParamSpec * unused,
+    GstTagReaderBin * trbin);
+
+static GstBusSyncReply gst_tagreader_bin_bus_sync_cb (GstBus *bus,
+    GstMessage *msg, gpointer data);
+
+static GstPad *find_sink_pad (GstElement * element);
+static GstStateChangeReturn gst_tagreader_bin_change_state (GstElement *element,
+    GstStateChange transition);
+
+static GValueArray *gst_tagreader_bin_get_elements ();
+static GValueArray *gst_tagreader_bin_filter (GValueArray * array,
+    const GstCaps * caps);
+
+/*******************************************************************************
+ * Standard GObject boilerplate
+ ******************************************************************************/
+
+static void gst_tagreader_bin_class_init (GstTagReaderBinClass * klass);
+static void gst_tagreader_bin_init (GstTagReaderBin * tagreader_bin);
+static void gst_tagreader_bin_dispose (GObject * object);
+static void gst_tagreader_bin_finalize (GObject * object);
+
+static GType
+gst_tagreader_bin_get_type (void)
+{
+  static GType gst_tagreader_bin_type = 0;
+
+  if (!gst_tagreader_bin_type) {
+    static const GTypeInfo gst_tagreader_bin_info = {
+      sizeof (GstTagReaderBinClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_tagreader_bin_class_init,
+      NULL,
+      NULL,
+      sizeof (GstTagReaderBin),
+      0,
+      (GInstanceInitFunc) gst_tagreader_bin_init,
+      NULL
+    };
+
+    gst_tagreader_bin_type =
+        g_type_register_static (GST_TYPE_BIN, "GstTagReaderBin",
+        &gst_tagreader_bin_info, 0);
+  }
+
+  return gst_tagreader_bin_type;
+}
+
+static void
+gst_tagreader_bin_class_init (GstTagReaderBinClass * klass)
+{
+  GObjectClass *gobject_klass;
+  GstElementClass *gstelement_klass;
+  GstBinClass *gstbin_klass;
+
+  gobject_klass = (GObjectClass *) klass;
+  gstelement_klass = (GstElementClass *) klass;
+  gstbin_klass = (GstBinClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_tagreader_bin_dispose);
+  gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_tagreader_bin_finalize);
+
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&decoder_bin_sink_template));
+
+  gst_element_class_set_details (gstelement_klass, &gst_tagreader_bin_details);
+
+  gstelement_klass->change_state =
+      GST_DEBUG_FUNCPTR (gst_tagreader_bin_change_state);
+}
+
+static void
+gst_tagreader_bin_init (GstTagReaderBin * tagreader_bin)
+{
+  /* first filter out the interesting element factories */
+  tagreader_bin->factories = gst_tagreader_bin_get_elements ();
+  tagreader_bin->finish_lock = g_mutex_new();
+  tagreader_bin->tagmutex = g_mutex_new();
+
+  /* we create the typefind element only once */
+  tagreader_bin->typefind = gst_element_factory_make ("typefind", "typefind");
+  if (!tagreader_bin->typefind) {
+    g_warning ("can't find typefind element, tagreaderbin will not work");
+  } else {
+    GstPad *pad;
+    GstPad *gpad;
+
+    /* add the typefind element */
+    if (!gst_bin_add (GST_BIN (tagreader_bin), tagreader_bin->typefind)) {
+      g_warning ("Could not add typefind element, tagreaderbin will not work");
+      gst_object_unref (tagreader_bin->typefind);
+      tagreader_bin->typefind = NULL;
+    }
+
+    /* get the sinkpad */
+    pad = gst_element_get_static_pad (tagreader_bin->typefind, "sink");
+
+    /* ghost the sink pad to ourself */
+    gpad = gst_ghost_pad_new ("sink", pad);
+    gst_pad_set_active (gpad, TRUE);
+    gst_element_add_pad (GST_ELEMENT (tagreader_bin), gpad);
+
+    gst_object_unref (pad);
+
+    /* connect a signal to find out when the typefind element found
+     * a type */
+    tagreader_bin->have_type_id =
+        g_signal_connect (G_OBJECT (tagreader_bin->typefind), "have-type",
+                          G_CALLBACK (type_found), tagreader_bin);
+  }
+
+  tagreader_bin->bus = gst_bus_new();
+
+  gst_bus_set_sync_handler (tagreader_bin->bus,
+      gst_tagreader_bin_bus_sync_cb, tagreader_bin);
+
+  gst_element_set_bus (tagreader_bin->typefind, tagreader_bin->bus);
+}
+
+static void
+gst_tagreader_bin_dispose (GObject * object)
+{
+  GstTagReaderBin *trbin;
+
+  trbin = GST_TAGREADER_BIN (object);
+
+  if (trbin->tags) {
+    gst_tag_list_free (trbin->tags);
+    trbin->tags = NULL;
+  }
+
+  if (trbin->bus) {
+    gst_object_unref (trbin->bus);
+    trbin->bus = NULL;
+  }
+
+  if (trbin->done_elements) {
+    g_list_foreach (trbin->done_elements, (GFunc) gst_object_unref, NULL);
+    g_list_free (trbin->done_elements);
+    trbin->done_elements = NULL;
+  }
+
+  if (trbin->factories) {
+    g_value_array_free (trbin->factories);
+    trbin->factories = NULL;
+  }
+
+  if (trbin->finish_lock) {
+    g_mutex_free (trbin->finish_lock);
+    trbin->finish_lock = NULL;
+  }
+  if (trbin->tagmutex) {
+    g_mutex_free (trbin->tagmutex);
+    trbin->tagmutex = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_tagreader_bin_finalize (GObject * object)
+{
+  GstTagReaderBin *tagreader_bin;
+
+  tagreader_bin = GST_TAGREADER_BIN (object);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static gboolean connect_pad (GstTagReaderBin * trbin, GstElement * src,
+    GstPad * pad, GstCaps * caps, GValueArray * factories);
+
+static gboolean connect_element (GstTagReaderBin * trbin, GstElement * element);
+
+static void pad_added_cb (GstElement * element, GstPad * pad,
+    GstTagReaderBin * trbin);
+
+/* gst_tagreader_finished:
+ *
+ * called when the tag reading process is done. This sends the gathered tags,
+ * finishes the ASYNC state change and finally sends EOS message to bus.
+ */
+static void
+gst_tagreader_finished (GstTagReaderBin * trbin)
+{
+  g_mutex_lock (trbin->tagmutex);
+  if (trbin->tags) {
+    gst_element_post_message (GST_ELEMENT (trbin),
+                              gst_message_new_tag (GST_OBJECT (trbin),
+                                  gst_tag_list_copy (trbin->tags)));
+
+    gst_tag_list_free (trbin->tags);
+    trbin->tags = NULL;
+  }
+  g_mutex_unlock (trbin->tagmutex);
+
+  do_async_done (trbin);
+
+  GstMessage *m = gst_message_new_eos (GST_OBJECT (trbin));
+  gst_element_post_message (GST_ELEMENT (trbin), m);
+}
+
+/* is_demuxer_element:
+ *
+ * Decide whether an element is a demuxer based on the klass and
+ * number/type of src pad templates it has.
+ */
+static gboolean
+is_demuxer_element (GstElement * srcelement)
+{
+  GstElementFactory *srcfactory;
+  GstElementClass *elemclass;
+  GList *templates, *walk;
+  const gchar *klass;
+  gint potential_src_pads = 0;
+
+  srcfactory = gst_element_get_factory (srcelement);
+  klass = gst_element_factory_get_klass (srcfactory);
+
+  /* Can't be a demuxer unless it has Demux in the klass name */
+  if (!strstr (klass, "Demux"))
+    return FALSE;
+
+  /* Walk the src pad templates and count how many the element
+  * might produce */
+  elemclass = GST_ELEMENT_GET_CLASS (srcelement);
+
+  walk = templates = gst_element_class_get_pad_template_list (elemclass);
+  while (walk != NULL) {
+    GstPadTemplate *templ;
+
+    templ = (GstPadTemplate *) walk->data;
+    if (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) {
+      switch (GST_PAD_TEMPLATE_PRESENCE (templ)) {
+        case GST_PAD_ALWAYS:
+        case GST_PAD_SOMETIMES:
+          if (strstr (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ), "%"))
+            potential_src_pads += 2;    /* Might make multiple pads */
+          else
+            potential_src_pads += 1;
+          break;
+        case GST_PAD_REQUEST:
+          potential_src_pads += 2;
+          break;
+      }
+    }
+    walk = g_list_next (walk);
+  }
+
+  if (potential_src_pads < 2)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* analyze_new_pad:
+ *
+ * called when a new pad is discovered. It will perform some basic actions
+ * before trying to link something to it.
+ *
+ *  - Check the caps, don't do anything when there are no caps or when they have
+ *    no good type.
+ *  - if the caps are non-fixed, setup a handler to continue autoplugging when
+ *    the caps become fixed (connect to notify::caps).
+ *  - get list of factories to autoplug.
+ *  - continue autoplugging to one of the factories.
+ */
+static void
+analyze_new_pad (GstTagReaderBin * trbin, GstElement * src, GstPad * pad,
+    GstCaps * caps)
+{
+  GValueArray *factories = NULL;
+  gboolean is_demux = FALSE;
+  GstElement *src_elem = NULL;
+  GstPad *srcpad = NULL;
+  GstElement *queue = NULL;
+
+  GST_DEBUG_OBJECT (trbin, "Pad %s:%s caps:%" GST_PTR_FORMAT,
+      GST_DEBUG_PAD_NAME (pad), caps);
+
+  if ((caps == NULL) || gst_caps_is_empty (caps))
+    goto unknown_type;
+
+  if (gst_caps_is_any (caps))
+    goto any_caps;
+
+  /* when the caps are not fixed yet, we can't be sure what element to
+   * connect. We delay autoplugging until the caps are fixed */
+  if (!gst_caps_is_fixed (caps))
+    goto non_fixed;
+
+  factories = gst_tagreader_bin_filter (trbin->factories, caps);
+  is_demux = is_demuxer_element (src);
+
+  /* continue autoplugging something from the list. */
+
+  /* Demuxer needs queue elements to be added after it */
+  if (is_demux) {
+    GstPad *qsinkpad;
+
+    queue = gst_element_factory_make ("queue", NULL);
+    if (!queue) {
+      goto no_queue;
+    }
+    gst_bin_add (GST_BIN_CAST (trbin), queue);
+    qsinkpad = gst_element_get_static_pad (queue, "sink");
+
+    if ((gst_pad_link (pad, qsinkpad)) != GST_PAD_LINK_OK) {
+      GST_WARNING_OBJECT (trbin, "Link failed on pad %s:%s",
+                          GST_DEBUG_PAD_NAME (qsinkpad));
+      gst_bin_remove (GST_BIN (trbin), queue);
+      gst_object_unref (qsinkpad);
+      goto no_queue;
+    }
+
+    gst_object_unref (qsinkpad);
+
+    if ((gst_element_set_state (queue, GST_STATE_PAUSED)) ==
+         GST_STATE_CHANGE_FAILURE)
+    {
+      GST_WARNING_OBJECT (trbin, "Couldn't set %s to PAUSED",
+                          GST_ELEMENT_NAME (queue));
+      gst_bin_remove (GST_BIN (trbin), queue);
+      goto no_queue;
+    }
+
+    srcpad = gst_element_get_static_pad (queue, "src");
+    src_elem = queue;
+
+    g_object_set (G_OBJECT (queue), "max-size-buffers", 1, NULL);
+    g_object_set (G_OBJECT (queue), "max-size-bytes", 0, NULL);
+    g_object_set (G_OBJECT (queue), "max-size-time", G_GINT64_CONSTANT (0),
+                  NULL);
+    g_object_set (G_OBJECT (queue), "leaky", 1, NULL);
+
+    GST_LOG_OBJECT (srcpad, "added a queue after demuxer");
+  } else {
+    src_elem = src;
+    srcpad = pad;
+  }
+
+no_queue:
+  GST_LOG_OBJECT (srcpad, "Let's continue discovery on this pad");
+  connect_pad (trbin, src_elem, srcpad, caps, factories);
+
+  g_value_array_free (factories);
+
+  if (queue) {
+    /* Pad was taken from queue with gst_element_get_static_pad()
+       that ref'ed it */
+    gst_object_unref (srcpad);
+  }
+
+  return;
+
+unknown_type:
+  {
+    GST_LOG_OBJECT (pad, "Unknown type, firing signal");
+    do_async_done (trbin);
+    return;
+  }
+non_fixed:
+  {
+    GST_DEBUG_OBJECT (pad, "pad has non-fixed caps delay autoplugging");
+    goto setup_caps_delay;
+  }
+any_caps:
+  {
+    GST_WARNING_OBJECT (pad,
+        "pad has ANY caps, not able to autoplug to anything");
+    goto setup_caps_delay;
+  }
+setup_caps_delay:
+  {
+    /* connect to caps notification */
+    g_signal_connect (G_OBJECT (pad), "notify::caps",
+                      G_CALLBACK (caps_notify_cb), trbin);
+    return;
+  }
+}
+
+/* add_fakesink_to_pad:
+ *
+ * called when autoplugging cannot find element capable of handling the
+ * found caps. In this case fakesink will be plugged to the srcpad of the
+ * last element added.
+ */
+static gboolean
+add_fakesink_to_pad (GstTagReaderBin * trbin, GstElement * src, GstPad * pad,
+                     GstCaps * caps)
+{
+  GstElement *fakesink;
+  GstPad *fsinkpad;
+  gboolean ret = TRUE;
+
+  GST_DEBUG_OBJECT (trbin, "adding fakesink to the end of pipeline");
+
+  fakesink = gst_element_factory_make ("fakesink", NULL);
+  g_return_val_if_fail (fakesink != NULL, FALSE);
+
+  gst_bin_add (GST_BIN_CAST (trbin), fakesink);
+  gst_element_set_bus (fakesink, trbin->bus);
+
+  /* Try to link the fakesink */
+  fsinkpad = gst_element_get_static_pad (fakesink, "sink");
+
+  if ((gst_pad_link (pad, fsinkpad)) != GST_PAD_LINK_OK) {
+    GST_WARNING_OBJECT (trbin, "Link failed on pad %s:%s",
+                        GST_DEBUG_PAD_NAME (fsinkpad));
+    gst_bin_remove (GST_BIN (trbin), fakesink);
+    ret = FALSE;
+  }
+
+  if (ret) {
+    if ((gst_element_set_state (fakesink, GST_STATE_PAUSED)) ==
+            GST_STATE_CHANGE_FAILURE)
+    {
+      GST_WARNING_OBJECT (trbin, "Couldn't set %s to PAUSED",
+                          GST_ELEMENT_NAME (fakesink));
+      gst_element_set_state (fakesink, GST_STATE_NULL);
+      gst_bin_remove (GST_BIN (trbin), fakesink);
+      ret = FALSE;
+    }
+  }
+  gst_object_unref (fsinkpad);
+  GST_DEBUG_OBJECT (trbin, "fakesink added");
+
+  return ret;
+}
+
+/* connect_pad:
+ *
+ * Try to connect the given pad to an element created from one of the factories,
+ * and recursively. If there are no factories, then a fakesink will be
+ * connected.
+ *
+ * Returns TRUE if an element was properly created and linked
+ */
+static gboolean
+connect_pad (GstTagReaderBin * trbin, GstElement * src, GstPad * pad,
+    GstCaps * caps, GValueArray * factories)
+{
+  gboolean res = FALSE;
+
+  g_return_val_if_fail (factories != NULL, FALSE);
+
+  GST_DEBUG_OBJECT (trbin, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+  /* Do we have element factories to choose? */
+  if (factories->n_values == 0) {
+    /* No. Add fakesink */
+    return add_fakesink_to_pad (trbin, src, pad, caps);
+  }
+
+  /* Try to create an element and link to it */
+  while (factories->n_values > 0) {
+    GstElementFactory *factory;
+    GstElement *element;
+    GstPad *sinkpad;
+
+    /* take first factory */
+    factory = g_value_get_object (g_value_array_get_nth (factories, 0));
+    /* Remove selected factory from the list. */
+    g_value_array_remove (factories, 0);
+
+    /* Try to create an element */
+    if ((element = gst_element_factory_create (factory, NULL)) == NULL) {
+      GST_WARNING_OBJECT (trbin, "Could not create an element from %s",
+          gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
+      continue;
+    }
+
+    /* ... activate it ... We do this before adding it to the bin so that we
+     * don't accidentally make it post error messages that will stop
+     * everything. */
+    if ((gst_element_set_state (element,
+                GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE) {
+      GST_WARNING_OBJECT (trbin, "Couldn't set %s to READY",
+          GST_ELEMENT_NAME (element));
+      gst_object_unref (element);
+      continue;
+    }
+
+    /* Find its sink pad, this should work after activating it. */
+    if (!(sinkpad = find_sink_pad (element))) {
+      GST_WARNING_OBJECT (trbin, "Element %s doesn't have a sink pad",
+          GST_ELEMENT_NAME (element));
+      gst_element_set_state (element, GST_STATE_NULL);
+      gst_object_unref (element);
+      continue;
+    }
+
+    /* add it to the bin */
+    if (!(gst_bin_add (GST_BIN_CAST (trbin), element))) {
+      GST_WARNING_OBJECT (trbin, "Couldn't add %s to the bin",
+          GST_ELEMENT_NAME (element));
+      gst_element_set_state (element, GST_STATE_NULL);
+      gst_object_unref (sinkpad);
+      gst_object_unref (element);
+      continue;
+    }
+
+    /* ...and try to link */
+    if ((gst_pad_link (pad, sinkpad)) != GST_PAD_LINK_OK) {
+      GST_WARNING_OBJECT (trbin, "Link failed on pad %s:%s",
+          GST_DEBUG_PAD_NAME (sinkpad));
+      gst_element_set_state (element, GST_STATE_NULL);
+      gst_object_unref (sinkpad);
+      gst_bin_remove (GST_BIN (trbin), element);
+      continue;
+    }
+    gst_object_unref (sinkpad);
+    GST_LOG_OBJECT (trbin, "linked on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+    /* Use our own message bus */
+    gst_element_set_bus (element, trbin->bus);
+
+    /* Activate the tag reading mode */
+    gst_tagreader_set_mode (GST_TAGREADER (element), GST_TAGREAD_MODE_TAGS);
+
+    /* We need to remember how many readers are added to bin */
+    trbin->n_parsers++;
+
+    /* link this element further */
+    connect_element (trbin, element);
+
+    /* Bring the element to the state of the parent */
+    if ((gst_element_set_state (element,
+                GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) {
+      GST_WARNING_OBJECT (trbin, "Couldn't set %s to PAUSED",
+          GST_ELEMENT_NAME (element));
+      gst_element_set_state (element, GST_STATE_NULL);
+      gst_bin_remove (GST_BIN (trbin), element);
+      trbin->n_parsers--;
+      continue;
+    }
+
+    res = TRUE;
+    break;
+  }
+
+  return res;
+}
+
+/* connect_element:
+ *
+ * try to connect the @element further by examining its source pads and
+ * finding a compatible element for it. If the pad is "sometimes pad", then
+ * a "pad-added" handler will be added and plugging will be delayed until
+ * the pad gets available.
+ */
+static gboolean
+connect_element (GstTagReaderBin * trbin, GstElement * element)
+{
+  GList *pads;
+  gboolean res = TRUE;
+  gboolean dynamic = FALSE;
+  GList *to_connect = NULL;
+
+  GST_DEBUG_OBJECT (trbin, "Attempting to connect element %s further",
+      GST_ELEMENT_NAME (element));
+
+  /* 1. Loop over pad templates, grabbing existing pads along the way */
+  for (pads = GST_ELEMENT_GET_CLASS (element)->padtemplates; pads;
+      pads = g_list_next (pads)) {
+    GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data);
+    const gchar *templ_name;
+
+    /* we are only interested in source pads */
+    if (GST_PAD_TEMPLATE_DIRECTION (templ) != GST_PAD_SRC)
+      continue;
+
+    templ_name = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
+    GST_DEBUG_OBJECT (trbin, "got a source pad template %s", templ_name);
+
+    /* figure out what kind of pad this is */
+    switch (GST_PAD_TEMPLATE_PRESENCE (templ)) {
+      case GST_PAD_ALWAYS:
+      {
+        /* get the pad that we need to autoplug */
+        GstPad *pad = gst_element_get_static_pad (element, templ_name);
+
+        if (pad) {
+          GST_DEBUG_OBJECT (trbin, "got the pad for always template %s",
+              templ_name);
+          /* here is the pad, we need to autoplug it */
+          to_connect = g_list_prepend (to_connect, pad);
+        } else {
+          /* strange, pad is marked as always but it's not
+           * there. Fix the element */
+          GST_WARNING_OBJECT (trbin,
+              "could not get the pad for always template %s", templ_name);
+        }
+        break;
+      }
+      case GST_PAD_SOMETIMES:
+      {
+        /* try to get the pad to see if it is already created or
+         * not */
+        GstPad *pad = gst_element_get_static_pad (element, templ_name);
+
+        if (pad) {
+          GST_DEBUG_OBJECT (trbin, "got the pad for sometimes template %s",
+              templ_name);
+          /* the pad is created, we need to autoplug it */
+          to_connect = g_list_prepend (to_connect, pad);
+        } else {
+          GST_DEBUG_OBJECT (trbin,
+              "did not get the sometimes pad of template %s", templ_name);
+          /* we have an element that will create dynamic pads */
+          dynamic = TRUE;
+        }
+        break;
+      }
+      case GST_PAD_REQUEST:
+        /* ignore request pads */
+        GST_DEBUG_OBJECT (trbin, "ignoring request padtemplate %s", templ_name);
+        break;
+    }
+  }
+
+  /* 2. if there are more potential pads, connect to relevent signals */
+  if (dynamic) {
+    /* This is a non-grouped element, the handlers are different */
+    g_signal_connect (G_OBJECT (element), "pad-added",
+        G_CALLBACK (pad_added_cb), trbin);
+  }
+
+  /* 3. for every available pad, connect it */
+  for (pads = to_connect; pads; pads = g_list_next (pads)) {
+    GstPad *pad = GST_PAD_CAST (pads->data);
+    GstCaps *caps;
+
+    caps = gst_pad_get_caps (pad);
+    analyze_new_pad (trbin, element, pad, caps);
+    if (caps)
+      gst_caps_unref (caps);
+
+    gst_object_unref (pad);
+  }
+  g_list_free (to_connect);
+
+  return res;
+}
+
+/* gst_tagreader_handle_done_msg:
+ *
+ * Handle the "tag-reading-done" message.
+ */
+static void
+gst_tagreader_handle_done_msg (GstTagReaderBin *trbin, GstMessage *msg)
+{
+  GstObject *sender = GST_MESSAGE_SRC (msg);
+  GList *iter;
+
+  g_return_if_fail (sender != NULL);
+
+  /* Check if the sender has already sent "tag-reading-done" message */
+  for (iter = trbin->done_elements; iter; iter = iter->next) {
+    if (iter->data == sender) return;
+  }
+
+  gst_object_ref (sender);
+  trbin->done_elements = g_list_prepend (trbin->done_elements, sender);
+  trbin->n_done++;
+
+  GST_LOG_OBJECT (trbin, "done: %d, needed: %d", trbin->n_done,
+                  trbin->n_parsers);
+
+  /* We can finish tag reading process when following conditions are met:
+      - tag reading is not finished yet
+      - all tag readers have already sent "done" message
+      - at least one fakesink has prerolled (sent "async-done" message) */
+  g_mutex_lock (trbin->finish_lock);
+  if (!trbin->finished &&
+       trbin->n_done == trbin->n_parsers &&
+       trbin->n_asyncs_done > 0)
+  {
+    gst_tagreader_finished (trbin);
+  }
+  g_mutex_unlock (trbin->finish_lock);
+}
+
+/*******************************************************************************
+ * Callback functions
+ ******************************************************************************/
+
+/* gst_tagreader_bin_bus_sync_cb:
+ *
+ * synchronous handler for bus messages for our own internal #GstBus.
+ * It handles the GST_MESSAGE_TAG, "tag-reading-done" and "async-done"
+ * messages and forwards the rest into global bus.
+ */
+static GstBusSyncReply
+gst_tagreader_bin_bus_sync_cb (GstBus *bus, GstMessage *msg, gpointer data)
+{
+  GstTagReaderBin *trbin = (GstTagReaderBin *) data;
+  guint msgtype = GST_MESSAGE_TYPE (msg);
+
+  GST_LOG_OBJECT (trbin, "got message from bus: %s, sender: %s",
+                  gst_message_type_get_name (msgtype),
+                  GST_ELEMENT_NAME (GST_MESSAGE_SRC (msg)));
+
+  if (gst_is_tagreading_done_message (msg)) {
+    GST_DEBUG_OBJECT (trbin, "tag reading done message from %s",
+                      GST_ELEMENT_NAME (GST_MESSAGE_SRC (msg)));
+    gst_tagreader_handle_done_msg (trbin, msg);
+  }
+  else if (msgtype == GST_MESSAGE_ASYNC_DONE)
+  {
+    if (GST_OBJECT_FLAG_IS_SET (GST_MESSAGE_SRC (msg), GST_ELEMENT_IS_SINK)) {
+      GST_LOG_OBJECT (trbin, "sink changed state to PAUSED");
+      trbin->n_asyncs_done++;
+
+      GST_LOG_OBJECT (trbin, "n_asyncs_done = %d", trbin->n_asyncs_done);
+
+      g_mutex_lock (trbin->finish_lock);
+      if (trbin->n_done == trbin->n_parsers && !trbin->finished) {
+        gst_tagreader_finished (trbin);
+      }
+      g_mutex_unlock (trbin->finish_lock);
+    }
+    goto forward;
+  }
+#ifdef MERGE_TAGS_FIRST
+  else if (msgtype == GST_MESSAGE_TAG) {
+    /* Collect & merge all tags first and send them at once when finished */
+    /* TODO: audio/video categorization? */
+
+    g_mutex_lock (trbin->tagmutex);
+    if (trbin->tags) {
+      GstTagList *new_tags;
+      GstTagList *merged;
+
+      gst_message_parse_tag (msg, &new_tags);
+      merged = gst_tag_list_merge (trbin->tags, new_tags,
+                                   GST_TAG_MERGE_REPLACE);
+      gst_tag_list_free (trbin->tags);
+      gst_tag_list_free (new_tags);
+      trbin->tags = merged;
+    }
+    else {
+      gst_message_parse_tag (msg, &trbin->tags);
+    }
+    g_mutex_unlock (trbin->tagmutex);
+  }
+#endif
+  else {
+    goto forward;
+  }
+
+  gst_message_unref (msg);
+  return GST_BUS_DROP;
+
+forward:
+
+  /* Forward the message to application. This needs to be done through
+     parent class' handler function so the parent bin object will be aware of
+     child elements' state changes etc. */
+  parent_class->handle_message (GST_BIN_CAST (trbin), msg);
+  return GST_BUS_DROP;
+}
+
+/* type_found:
+ *
+ * callback handler for typefind element. Starts the recursive autoplugging
+ * procedure.
+ */
+static void
+type_found (GstElement * typefind, guint probability,
+    GstCaps * caps, GstTagReaderBin * tagreader_bin)
+{
+  GstPad *pad;
+
+  GST_DEBUG_OBJECT (tagreader_bin,
+      "typefind found caps %" GST_PTR_FORMAT, caps);
+
+  /* we can only deal with one type, we don't yet support dynamically changing
+   * caps from the typefind element */
+  if (tagreader_bin->have_type)
+    goto exit;
+
+  tagreader_bin->have_type = TRUE;
+  pad = gst_element_get_static_pad (typefind, "src");
+  analyze_new_pad (tagreader_bin, typefind, pad, caps);
+  gst_object_unref (pad);
+
+exit:
+  return;
+}
+
+/* pad_added_cb:
+ *
+ * callback for "pad-added" signal. This is used when #connect_element found
+ * a sometimes source pad from element and added a signal handler for it. This
+ * gets called when the element finally creates the pad.
+ */
+static void
+pad_added_cb (GstElement * element, GstPad * pad, GstTagReaderBin * trbin)
+{
+  GstCaps *caps;
+
+  GST_LOG_OBJECT (pad, "Pad added");
+
+  caps = gst_pad_get_caps (pad);
+  analyze_new_pad (trbin, element, pad, caps);
+  if (caps)
+    gst_caps_unref (caps);
+}
+
+/* caps_notify_cb:
+ *
+ * callback for "notify::caps" that is set when autoplugging finds a srcpad
+ * which caps are not yet set. This causes the #pad_added_cb to be called.
+ */
+static void
+caps_notify_cb (GstPad * pad, GParamSpec * unused, GstTagReaderBin * trbin)
+{
+  GstElement *element;
+
+  GST_LOG_OBJECT (trbin, "Notified caps for pad %s",
+      GST_DEBUG_PAD_NAME (pad));
+
+  element = GST_ELEMENT_CAST (gst_pad_get_parent (pad));
+
+  pad_added_cb (element, pad, trbin);
+
+  gst_object_unref (element);
+}
+
+/*******************************************************************************
+ * Element async state changing
+ ******************************************************************************/
+
+/* do_async_start:
+ *
+ * post ASYNC_START message to bus.
+ */
+static void
+do_async_start (GstTagReaderBin *trbin)
+{
+  GstMessage *message;
+
+  trbin->async_pending = TRUE;
+
+  GST_LOG_OBJECT (trbin, "sending async-start message");
+
+  message = gst_message_new_async_start (GST_OBJECT_CAST (trbin), FALSE);
+  parent_class->handle_message (GST_BIN_CAST (trbin), message);
+}
+
+/* do_async_start:
+ *
+ * post ASYNC_DONE message to bus. It means that the async state change
+ * initiated with #do_async_start is now finished.
+ */
+static void
+do_async_done (GstTagReaderBin *trbin)
+{
+  GstMessage *message;
+
+  if (trbin->async_pending) {
+    GST_LOG_OBJECT (trbin, "sending async-done message");
+
+    message = gst_message_new_async_done (GST_OBJECT_CAST (trbin));
+    parent_class->handle_message (GST_BIN_CAST (trbin), message);
+    trbin->async_pending = FALSE;
+  }
+}
+
+/*******************************************************************************
+ * Convenience functions
+ ******************************************************************************/
+
+/* find_sink_pad:
+ *
+ * Returns the first sink pad of the given element, or NULL if it doesn't have
+ * any.
+ */
+static GstPad *
+find_sink_pad (GstElement * element)
+{
+  GstIterator *it;
+  GstPad *pad = NULL;
+  gpointer point;
+
+  it = gst_element_iterate_sink_pads (element);
+
+  if ((gst_iterator_next (it, &point)) == GST_ITERATOR_OK)
+    pad = (GstPad *) point;
+
+  gst_iterator_free (it);
+
+  return pad;
+}
+
+/* cleanup_tagreader_bin:
+ *
+ * cleans up the bin by removing all autoplugged elements and setting them
+ * to NULL state.
+ */
+static void
+cleanup_tagreader_bin (GstTagReaderBin *trbin)
+{
+  GstIterator *elem_it = NULL;
+  gboolean done = FALSE;
+
+  g_return_if_fail (GST_IS_TAGREADER_BIN (trbin));
+
+  GST_DEBUG_OBJECT (trbin, "cleaning up the bin");
+
+  if (trbin->done_elements) {
+    g_list_foreach (trbin->done_elements, (GFunc) gst_object_unref, NULL);
+    g_list_free (trbin->done_elements);
+    trbin->done_elements = NULL;
+  }
+
+  elem_it = gst_bin_iterate_elements (GST_BIN (trbin));
+  while (!done) {
+    GstElement *element = NULL;
+
+    switch (gst_iterator_next (elem_it, (gpointer) & element)) {
+      case GST_ITERATOR_OK:
+        if (element != trbin->typefind) {
+          GST_DEBUG_OBJECT (element, "removing autoplugged element");
+          gst_element_set_state (element, GST_STATE_NULL);
+          gst_bin_remove (GST_BIN (trbin), element);
+        }
+        gst_object_unref (element);
+        break;
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (elem_it);
+        break;
+      case GST_ITERATOR_ERROR:
+        done = TRUE;
+        break;
+      case GST_ITERATOR_DONE:
+        done = TRUE;
+        break;
+    }
+  }
+  gst_iterator_free (elem_it);
+  trbin->n_parsers = 0;
+  trbin->n_asyncs_done = 0;
+  trbin->n_done = 0;
+}
+
+/*******************************************************************************
+ * State changing
+ ******************************************************************************/
+
+static GstStateChangeReturn
+gst_tagreader_bin_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  GstTagReaderBin *trbin = GST_TAGREADER_BIN (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      if (trbin->typefind == NULL)
+        goto missing_typefind;
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      trbin->have_type = FALSE;
+      trbin->finished = FALSE;
+      trbin->n_parsers = 0;
+      trbin->n_asyncs_done = 0;
+      trbin->n_done = 0;
+      ret = GST_STATE_CHANGE_ASYNC;
+      do_async_start (trbin);
+      break;
+    default:
+      break;
+  }
+
+  {
+    GstStateChangeReturn bret;
+
+    bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+    if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
+      goto activate_failed;
+  }
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      GST_DEBUG_OBJECT (trbin, "PAUSED_TO_READY");
+      do_async_done (trbin);
+      cleanup_tagreader_bin (trbin);
+      break;
+
+    default:
+      break;
+  }
+
+  return ret;
+
+/* ERRORS */
+missing_typefind:
+  {
+    gst_element_post_message (element,
+        gst_missing_element_message_new (element, "typefind"));
+    GST_ELEMENT_ERROR (trbin, CORE, MISSING_PLUGIN, (NULL), ("no typefind!"));
+    return GST_STATE_CHANGE_FAILURE;
+  }
+activate_failed:
+  {
+    GST_DEBUG_OBJECT (element,
+                      "element failed to change states -- activation problem?");
+    return GST_STATE_CHANGE_FAILURE;
+  }
+}
+
+/*******************************************************************************
+ * Plugin filtering
+ ******************************************************************************/
+
+/* element_filter_function:
+ *
+ * filter only elements that implement #GstTagReader interface. This function
+ * will be used when filtering elements with
+ * #gst_default_registry_feature_filter function.
+ */
+static gboolean
+element_filter_function (GstPluginFeature * feature)
+{
+  /* we only care about element factories */
+  if (!GST_IS_ELEMENT_FACTORY (feature))
+    return FALSE;
+
+  return gst_element_factory_has_interface (GST_ELEMENT_FACTORY (feature),
+                                            "GstTagReader");
+}
+
+
+#if 0
+/* compare_ranks:
+ *
+ * function used to sort element features. We first sort on the rank, then
+ * on the element name (to get a consistent, predictable list).
+ */
+static gint
+compare_ranks (GValue * v1, GValue * v2)
+{
+  gint diff;
+  const gchar *rname1, *rname2;
+  GstPluginFeature *f1, *f2;
+
+  f1 = g_value_get_object (v1);
+  f2 = g_value_get_object (v2);
+
+  diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
+  if (diff != 0)
+    return diff;
+
+  rname1 = gst_plugin_feature_get_name (f1);
+  rname2 = gst_plugin_feature_get_name (f2);
+
+  diff = strcmp (rname2, rname1);
+
+  return diff;
+}
+#endif
+
+/* gst_tagreader_bin_get_elements:
+ *
+ * create a list of all elements in #GstRegistry that implement #GstTagReader
+ * interface.
+ */
+static GValueArray *
+gst_tagreader_bin_get_elements ()
+{
+  GValueArray *result;
+  GList *walk, *list;
+
+  result = g_value_array_new (0);
+
+  /* get the feature list using the filter */
+  list =
+      gst_default_registry_feature_filter ((GstPluginFeatureFilter)
+      element_filter_function, FALSE, NULL);
+
+  /* convert to an array */
+  for (walk = list; walk; walk = g_list_next (walk)) {
+    GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data);
+    GValue val = { 0, };
+
+    g_value_init (&val, G_TYPE_OBJECT);
+    g_value_set_object (&val, factory);
+    g_value_array_append (result, &val);
+    g_value_unset (&val);
+  }
+  gst_plugin_feature_list_free (list);
+
+  /* FIXME: Do we need this? */
+//   g_value_array_sort (result, (GCompareFunc) compare_ranks);
+
+  return result;
+}
+
+/* gst_tagreader_bin_filter:
+ *
+ * choose element(s) from the given array of #GstElementFactory objects that
+ * can handle given @caps.
+ */
+static GValueArray *
+gst_tagreader_bin_filter (GValueArray * array, const GstCaps * caps)
+{
+  GValueArray *result;
+  gint i;
+
+  result = g_value_array_new (0);
+
+  GST_DEBUG ("finding factories");
+
+  /* loop over all the factories */
+  for (i = 0; i < array->n_values; i++) {
+    GValue *value;
+    GstElementFactory *factory;
+    const GList *templates;
+    GList *walk;
+
+    value = g_value_array_get_nth (array, i);
+    factory = g_value_get_object (value);
+
+    /* get the templates from the element factory */
+    templates = gst_element_factory_get_static_pad_templates (factory);
+    for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
+      GstStaticPadTemplate *templ = walk->data;
+
+      /* we only care about the sink templates */
+      if (templ->direction == GST_PAD_SINK) {
+        GstCaps *intersect;
+        GstCaps *tmpl_caps;
+
+        /* try to intersect the caps with the caps of the template */
+        tmpl_caps = gst_static_caps_get (&templ->static_caps);
+
+        /* FIXME, intersect is not the right method, we ideally want to check
+        * for a subset here */
+        intersect = gst_caps_intersect (caps, tmpl_caps);
+        gst_caps_unref (tmpl_caps);
+
+        /* check if the intersection is empty */
+        if (!gst_caps_is_empty (intersect)) {
+          /* non empty intersection, we can use this element */
+          GValue resval = { 0, };
+          g_value_init (&resval, G_TYPE_OBJECT);
+          g_value_set_object (&resval, factory);
+          g_value_array_append (result, &resval);
+          g_value_unset (&resval);
+          gst_caps_unref (intersect);
+          break;
+        }
+        gst_caps_unref (intersect);
+      }
+    }
+  }
+  GST_DEBUG ("number of factories found: %d", result->n_values);
+
+  return result;
+}
+
+
+/*******************************************************************************
+ * Plugin initialization stuff
+ ******************************************************************************/
+
+gboolean
+gst_tagreader_bin_plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_tagreader_bin_debug, "tagreadbin", 0,
+                           "tag reader bin");
+
+  return gst_element_register (plugin, "tagreadbin", GST_RANK_NONE,
+                               GST_TYPE_TAGREADER_BIN);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+                   GST_VERSION_MINOR,
+                   "tagreadbin",
+                   "Tag Reader Bin", gst_tagreader_bin_plugin_init,
+                   VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
+
+
--- /dev/null	2008-11-17 13:44:35.000000000 +0200
+++ gst/tagread/tagreadbin.h	2008-10-23 16:12:02.000000000 +0300
@@ -0,0 +1,77 @@
+/* GStreamer Tag Reader bin
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * tagreader.h: Tag Reader interface design
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_TAGREADER_BIN_H__
+#define __GST_TAGREADER_BIN_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstTagReaderBin GstTagReaderBin;
+typedef struct _GstTagReaderBinClass GstTagReaderBinClass;
+
+#define GST_TYPE_TAGREADER_BIN             (gst_tagreader_bin_get_type())
+#define GST_TAGREADER_BIN_CAST(obj)        ((GstTagReaderBin*)(obj))
+#define GST_TAGREADER_BIN(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAGREADER_BIN,GstTagReaderBin))
+#define GST_TAGREADER_BIN_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAGREADER_BIN,GstTagReaderBinClass))
+#define GST_IS_TAGREADER_BIN(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAGREADER_BIN))
+#define GST_IS_TAGREADER_BIN_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAGREADER_BIN))
+
+/**
+ *  GstTagReaderBin:
+ *
+ *  The opaque #TagReaderBin data structure
+ */
+struct _GstTagReaderBin
+{
+  GstBin bin;                   /* we extend GstBin */
+
+  /* properties */
+  GstElement *typefind;         /* this holds the typefind object */
+  GstTagList *tags;             /* tags will be collected here */
+  GstBus     *bus;              /* internal bus being used by parsers */
+  GMutex     *finish_lock;      /* used for locking the finishing process */
+  GMutex     *tagmutex;         /* TagList is protected with this lock */
+
+  GValueArray *factories;       /* factories to use when selecting elements */
+  GList *done_elements;         /* elements that have sent the "done" message */
+
+  gboolean have_type;           /* if we received the have_type signal */
+  gboolean async_pending;       /* asynchronous state change pending */
+  gboolean finished;            /* has the tag reading finished already */
+  guint have_type_id;
+  guint n_parsers;              /* number of parser elements in the bin */
+  guint n_asyncs_done;          /* number of async-done messages received */
+  guint n_done;                 /* number of "tagreading-done" messages */
+};
+
+struct _GstTagReaderBinClass
+{
+  GstBinClass parent_class;
+};
+
+G_END_DECLS
+
+#endif /* __GST_TAGREADER_BIN_H__ */
+
--- /home/ensonic/projects/gstreamer/gst-plugins-base/tests/Makefile.am	2007-07-16 09:01:28.000000000 +0300
+++ ./tests/Makefile.am	2008-12-12 10:50:38.000000000 +0200
@@ -17,11 +17,13 @@ SUBDIRS_ICLES =
 endif
 
 SUBDIRS = 			\
+	benchmarks		\
 	$(SUBDIRS_CHECK)	\
 	$(SUBDIRS_EXAMPLES)	\
 	$(SUBDIRS_ICLES)
 
 DIST_SUBDIRS = 			\
+	benchmarks		\
 	check			\
 	examples		\
 	icles
--- /home/ensonic/projects/gstreamer/gst-plugins-base/tests/benchmarks/Makefile.am	1970-01-01 02:00:00.000000000 +0200
+++ ./tests/benchmarks/Makefile.am	2008-10-23 16:11:56.000000000 +0300
@@ -0,0 +1,4 @@
+noinst_PROGRAMS = tagread startup
+
+AM_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
+LDADD = $(GST_LIBS)
--- /dev/null	2008-11-17 13:44:35.000000000 +0200
+++ tests/benchmarks/tagread.c	2008-12-12 10:51:10.000000000 +0200
@@ -0,0 +1,234 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <gst/gst.h>
+#include <gst/interfaces/tagreader.h>
+
+guint fsinkcounter = 0;
+GList *fsinks = NULL;
+GMainLoop *loop = NULL;
+guint use_dbin = 0;
+
+//#define USE_CACHE_ELEMENT
+
+static GstClockTime
+gst_get_current_time (void)
+{
+  GTimeVal tv;
+
+  g_get_current_time (&tv);
+  return GST_TIMEVAL_TO_TIME (tv);
+}
+
+static void
+unlink_fsink (void *obj, void *user_data)
+{
+    GstElement *fsink = (GstElement *) obj;
+    GstElement *pipeline = (GstElement *) user_data;
+    GstElement *dbin = gst_bin_get_by_name (GST_BIN (pipeline), "dbin");
+    gst_element_unlink (dbin, fsink);
+    gst_bin_remove (GST_BIN (pipeline), fsink);
+    gst_element_set_state (fsink, GST_STATE_NULL);
+}
+
+void dbin_dpad_cb (GstElement* e, GstPad* pad, gboolean cont, gpointer user_data)
+{
+    GstElement *fsink, *pipeline;
+    GstPad *fsinkpad;
+
+    pipeline = (GstElement *) user_data;
+    fsink = gst_element_factory_make ("fakesink", NULL);
+
+    fsinks = g_list_append (fsinks, fsink);
+
+    gst_element_set_state (fsink, GST_STATE_PAUSED);
+
+    gst_bin_add (GST_BIN_CAST (pipeline), fsink);
+    fsinkpad = gst_element_get_static_pad (fsink, "sink");
+    gst_pad_link (pad, fsinkpad);
+    gst_object_unref (fsinkpad);
+}
+
+static void
+print_tag (const GstTagList *list, const gchar *tag, gpointer data)
+{
+    GType type;
+    gchar *str = NULL;
+    guint i, count;
+
+    count = gst_tag_list_get_tag_size (list, tag);
+
+    for (i=0; i<count; i++) {
+        type = gst_tag_get_type (tag);
+
+        if (type == G_TYPE_STRING) {
+            gst_tag_list_get_string_index (list, tag, i, &str);
+        } else if (type == G_TYPE_INT) {
+            gint val = 0;
+            gst_tag_list_get_int_index (list, tag, i, &val);
+            str = g_strdup_printf ("%d", val);
+        } else if (type == G_TYPE_UINT) {
+            guint uval = 0;
+            gst_tag_list_get_uint_index (list, tag, i, &uval);
+            str = g_strdup_printf ("%d", uval);
+        } else if (type == G_TYPE_UINT64) {
+            guint64 uval64 = 0;
+            gst_tag_list_get_uint64_index (list, tag, i, &uval64);
+            str = g_strdup_printf ("%llu", uval64);
+        } else if (type == G_TYPE_BOOLEAN) {
+            gboolean b = FALSE;
+            gst_tag_list_get_boolean_index (list, tag, i, &b);
+            str = g_strdup ( b ? "TRUE" : "FALSE" );
+        }
+        else if (type == GST_TYPE_DATE) {
+            GDate *date = NULL;
+            if (!gst_tag_list_get_date_index (list, tag, i, &date)) {
+                str = g_strdup ("INVALID DATE");
+            } else {
+                if (date == NULL || !g_date_valid (date)) {
+                    str = g_strdup ("9999-99-99");
+                } else {
+                    str = g_strdup_printf ("%04u-%02u-%02u",
+                                          g_date_get_year (date),
+                                          g_date_get_month (date),
+                                          g_date_get_day (date));
+                }
+            }
+            if (date) g_date_free (date);
+        }
+        printf ("        %s : %s\n", tag, str);
+        g_free (str);
+    }
+}
+
+static void
+show_tags (GstMessage *msg)
+{
+    GstTagList *new_tags = NULL;
+    gst_message_parse_tag (msg, &new_tags);
+    printf ("    TAGS (%s):\n", GST_ELEMENT_NAME(GST_MESSAGE_SRC(msg)));
+    gst_tag_list_foreach (new_tags, print_tag, NULL);
+    gst_tag_list_free (new_tags);
+}
+
+static gboolean
+metadata_bus_async_cb (GstBus *bus, GstMessage *msg, gpointer data)
+{
+    GError *error = NULL;
+    GstElement *pipeline = (GstElement *) data;
+    gboolean stop = FALSE;
+
+    switch (GST_MESSAGE_TYPE (msg)) {
+        case GST_MESSAGE_ERROR:
+            gst_message_parse_error (msg, &error, NULL);
+            printf ("ERROR: %s\n", error->message);
+            g_error_free (error);
+            stop = TRUE;
+            break;
+        case GST_MESSAGE_TAG:
+            show_tags (msg);
+            break;
+        case GST_MESSAGE_EOS:
+            printf ("    EOS\n");
+            stop = TRUE;
+            break;
+        case GST_MESSAGE_STATE_CHANGED:
+        {
+            GstElement *sender = (GstElement *) GST_MESSAGE_SRC (msg);
+            if (use_dbin && sender == pipeline) {
+                GstState newstate;
+                gst_message_parse_state_changed (msg, NULL, &newstate, NULL);
+                if (newstate == GST_STATE_PAUSED) {
+                    printf ("    PAUSED\n");
+                    stop = TRUE;
+                }
+            }
+        }
+        default:
+            break;
+    }
+
+    if (stop) {
+          gst_element_set_state (pipeline, GST_STATE_READY);
+          gst_element_get_state (pipeline, NULL, NULL, 5 * GST_SECOND);
+          g_list_foreach (fsinks, unlink_fsink, pipeline);
+          g_list_free (fsinks);
+          fsinks = NULL;
+          g_main_loop_quit (loop);
+    }
+
+    return TRUE;
+}
+
+int main(int argc, char **argv)
+{
+#ifdef USE_CACHE_ELEMENT
+    GstElement *cache;
+#endif
+    GstElement *pipeline, *filesrc, *dbin;
+    GstClockTime start, end;
+    GstBus *bus;
+    guint id, i;
+
+
+    gst_init (&argc, &argv);
+
+    if(argc < 2) {
+        g_print("USAGE: %s <use decodebin (0/1)> <list_of_files>\n", argv[0]);
+        exit (1);
+    }
+    use_dbin = atoi (argv[1]);
+
+    pipeline = gst_element_factory_make ("pipeline", NULL);
+    filesrc  = gst_element_factory_make ("filesrc",  NULL);
+#ifdef USE_CACHE_ELEMENT
+    cache    = gst_element_factory_make ("cache",    NULL);
+#endif
+
+    if (use_dbin) {
+        dbin = gst_element_factory_make ("decodebin", "dbin");
+        id = g_signal_connect (G_OBJECT (dbin), "new-decoded-pad",
+                               G_CALLBACK (dbin_dpad_cb), pipeline);
+    }
+    else {
+        dbin = gst_element_factory_make ("tagreadbin", "dbin");
+        id = 0;
+    }
+
+    /* add objects to the main pipeline */
+    gst_bin_add (GST_BIN (pipeline), filesrc);
+    gst_bin_add (GST_BIN (pipeline), dbin);
+
+#ifdef USE_CACHE_ELEMENT
+    gst_bin_add (GST_BIN (pipeline), cache);
+    if (! gst_element_link_many (filesrc, cache, dbin, NULL)) {
+#else
+    if (! gst_element_link (filesrc, dbin)) {
+#endif
+        g_print ("Can't link elements\n");
+        return 1;
+    }
+
+    loop = g_main_loop_new (NULL, FALSE);
+    bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+    gst_bus_add_watch (bus, metadata_bus_async_cb, pipeline);
+
+    start = gst_get_current_time ();
+    for (i=2; i< argc; i++) {
+      g_object_set (G_OBJECT (filesrc), "location", argv[i], NULL);
+      printf ("FILE: %s\n", argv[i]);
+
+      /* Flush the pipeline */
+      gst_bus_set_flushing (bus, TRUE);
+      gst_bus_set_flushing (bus, FALSE);
+      gst_element_set_state (pipeline, GST_STATE_PAUSED);
+      g_main_loop_run (loop);
+    }
+    end = gst_get_current_time ();
+    printf ("Running time: %" GST_TIME_FORMAT "\n",
+      GST_TIME_ARGS (end - start));
+
+    gst_element_set_state (pipeline, GST_STATE_NULL);
+    gst_object_unref (bus);
+    gst_object_unref (GST_OBJECT (pipeline));
+    return 0;
+}
