fix doc build fix autogen
[platform/upstream/gstreamer.git] / gst / gstbin.c
index ac35edc..7fd8811 100644 (file)
@@ -1,4 +1,5 @@
 /* GStreamer
+ * 
  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
  *                    2000 Wim Taymans <wtay@chello.be>
  *
  * Boston, MA 02111-1307, USA.
  */
 
-//#define GST_DEBUG_ENABLED
-#include "config.h"
 #include "gst_private.h"
 
+#include "gstevent.h"
 #include "gstbin.h"
+#include "gstxml.h"
+#include "gstinfo.h"
 
 #include "gstscheduler.h"
+#include "gstindex.h"
 
-GstElementDetails gst_bin_details = {
+static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS (
   "Generic bin",
-  "Bin",
+  "Generic/Bin",
   "Simple container object",
-  VERSION,
-  "Erik Walthinsen <omega@cse.ogi.edu>",
-  "(C) 1999",
-};
+  "Erik Walthinsen <omega@cse.ogi.edu>"
+);
+
+GType _gst_bin_type = 0;
 
+static gboolean _gst_boolean_did_something_accumulator (GSignalInvocationHint *ihint,
+    GValue *return_accu, const GValue *handler_return, gpointer dummy);
 
-static void                    gst_bin_real_destroy            (GtkObject *object);
+static void                    gst_bin_dispose                 (GObject * object);
 
 static GstElementStateReturn   gst_bin_change_state            (GstElement *element);
 static GstElementStateReturn   gst_bin_change_state_norecurse  (GstBin *bin);
-static gboolean                        gst_bin_change_state_type       (GstBin *bin,
-                                                                GstElementState state,
-                                                                GtkType type);
 
-static void                    gst_bin_create_plan_func        (GstBin *bin);
-static gboolean                        gst_bin_iterate_func            (GstBin *bin);
+#ifndef GST_DISABLE_INDEX
+static void                    gst_bin_set_index               (GstElement *element, GstIndex *index);
+#endif
+
+static void                    gst_bin_add_func                (GstBin *bin, GstElement *element);
+static void                    gst_bin_remove_func             (GstBin *bin, GstElement *element);
+static void                    gst_bin_child_state_change_func (GstBin *bin, GstElementState oldstate, 
+                                                                GstElementState newstate, GstElement *child);
+
+static GstClock*               gst_bin_get_clock_func          (GstElement *element);
+static void                    gst_bin_set_clock_func          (GstElement *element, GstClock *clock);
+
+static gboolean                gst_bin_iterate_func            (GstBin *bin);
 
-static xmlNodePtr              gst_bin_save_thyself            (GstObject *object, xmlNodePtr parent);
-static void                    gst_bin_restore_thyself         (GstObject *object, xmlNodePtr self);
+#ifndef GST_DISABLE_LOADSAVE
+static xmlNodePtr              gst_bin_save_thyself            (GstObject * object, xmlNodePtr parent);
+static void                    gst_bin_restore_thyself         (GstObject * object, xmlNodePtr self);
+#endif
 
 /* Bin signals and args */
-enum {
-  OBJECT_ADDED,
+enum
+{
+  ELEMENT_ADDED,
+  ELEMENT_REMOVED,
+  ITERATE,
   LAST_SIGNAL
 };
 
-enum {
-  ARG_0,
+enum
+{
+  ARG_0
   /* FILL ME */
 };
 
-
-static void gst_bin_class_init (GstBinClass *klass);
-static void gst_bin_init       (GstBin *bin);
-
+static void                    gst_bin_base_init               (gpointer g_class);
+static void                    gst_bin_class_init              (GstBinClass * klass);
+static void                    gst_bin_init                    (GstBin * bin);
 
 static GstElementClass *parent_class = NULL;
 static guint gst_bin_signals[LAST_SIGNAL] = { 0 };
 
-GtkType
+GType
 gst_bin_get_type (void)
 {
-  static GtkType bin_type = 0;
-
-  if (!bin_type) {
-    static const GtkTypeInfo bin_info = {
-      "GstBin",
-      sizeof(GstBin),
-      sizeof(GstBinClass),
-      (GtkClassInitFunc)gst_bin_class_init,
-      (GtkObjectInitFunc)gst_bin_init,
-      (GtkArgSetFunc)NULL,
-      (GtkArgGetFunc)NULL,
-      (GtkClassInitFunc)NULL,
+  if (!_gst_bin_type) {
+    static const GTypeInfo bin_info = {
+      sizeof (GstBinClass),
+      gst_bin_base_init,
+      NULL,
+      (GClassInitFunc) gst_bin_class_init,
+      NULL,
+      NULL,
+      sizeof (GstBin),
+      8,
+      (GInstanceInitFunc) gst_bin_init,
+      NULL
     };
-    bin_type = gtk_type_unique (GST_TYPE_ELEMENT, &bin_info);
+
+    _gst_bin_type = g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
   }
-  return bin_type;
+  return _gst_bin_type;
 }
 
 static void
-gst_bin_class_init (GstBinClass *klass)
+gst_bin_base_init (gpointer g_class)
 {
-  GtkObjectClass *gtkobject_class;
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+  
+  gst_element_class_set_details (gstelement_class, &gst_bin_details);
+}
+
+static void
+gst_bin_class_init (GstBinClass * klass)
+{
+  GObjectClass *gobject_class;
   GstObjectClass *gstobject_class;
   GstElementClass *gstelement_class;
 
-  gtkobject_class = (GtkObjectClass*)klass;
-  gstobject_class = (GstObjectClass*)klass;
-  gstelement_class = (GstElementClass*)klass;
-
-  parent_class = gtk_type_class (GST_TYPE_ELEMENT);
+  gobject_class = (GObjectClass *) klass;
+  gstobject_class = (GstObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  gst_bin_signals[ELEMENT_ADDED] =
+    g_signal_new ("element_added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
+                 G_STRUCT_OFFSET (GstBinClass, element_added), NULL, NULL,
+                 gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+  gst_bin_signals[ELEMENT_REMOVED] =
+    g_signal_new ("element_removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
+                 G_STRUCT_OFFSET (GstBinClass, element_removed), NULL, NULL,
+                 gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+  gst_bin_signals[ITERATE] =
+    g_signal_new ("iterate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GstBinClass, iterate),
+                  _gst_boolean_did_something_accumulator, NULL,
+                 gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN, 0);
+
+  gobject_class->dispose               = GST_DEBUG_FUNCPTR (gst_bin_dispose);
+
+#ifndef GST_DISABLE_LOADSAVE
+  gstobject_class->save_thyself        = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
+  gstobject_class->restore_thyself     = GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
+#endif
 
-  gst_bin_signals[OBJECT_ADDED] =
-    gtk_signal_new ("object_added", GTK_RUN_FIRST, gtkobject_class->type,
-                    GTK_SIGNAL_OFFSET (GstBinClass, object_added),
-                    gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1,
-                    GST_TYPE_ELEMENT);
-  gtk_object_class_add_signals (gtkobject_class, gst_bin_signals, LAST_SIGNAL);
+  gstelement_class->change_state       = GST_DEBUG_FUNCPTR (gst_bin_change_state);
+#ifndef GST_DISABLE_INDEX
+  gstelement_class->set_index          = GST_DEBUG_FUNCPTR (gst_bin_set_index);
+#endif
 
-  klass->change_state_type =           gst_bin_change_state_type;
-  klass->create_plan =                 gst_bin_create_plan_func;
-  klass->schedule =                    gst_bin_schedule_func;
-  klass->iterate =                     gst_bin_iterate_func;
+  klass->add_element                   = GST_DEBUG_FUNCPTR (gst_bin_add_func);
+  klass->remove_element                = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
+  klass->child_state_change            = GST_DEBUG_FUNCPTR (gst_bin_child_state_change_func);
+  klass->iterate                       = GST_DEBUG_FUNCPTR (gst_bin_iterate_func);
+}
 
-  gstobject_class->save_thyself =      gst_bin_save_thyself;
-  gstobject_class->restore_thyself =   gst_bin_restore_thyself;
+static gboolean
+_gst_boolean_did_something_accumulator (GSignalInvocationHint *ihint,
+    GValue *return_accu, const GValue *handler_return, gpointer dummy)
+{
+  gboolean did_something;
 
-  gstelement_class->change_state =     gst_bin_change_state;
+  did_something = g_value_get_boolean (handler_return);
+  if (did_something) {
+    g_value_set_boolean (return_accu, TRUE);
+  }
 
-  gtkobject_class->destroy =           gst_bin_real_destroy;
+  /* always continue emission */
+  return TRUE;
 }
 
 static void
-gst_bin_init (GstBin *bin)
+gst_bin_init (GstBin * bin)
 {
-  // in general, we prefer to use cothreads for most things
+  /* in general, we prefer to use cothreads for most things */
   GST_FLAG_SET (bin, GST_BIN_FLAG_PREFER_COTHREADS);
 
   bin->numchildren = 0;
   bin->children = NULL;
-  bin->eos_providers = NULL;
-  bin->num_eos_providers = 0;
-  bin->chains = NULL;
-  bin->eoscond = g_cond_new ();
-// FIXME temporary testing measure
-//  bin->use_cothreads = TRUE;
 }
 
 /**
@@ -149,10 +197,274 @@ gst_bin_init (GstBin *bin)
  *
  * Returns: new bin
  */
-GstElement*
-gst_bin_new (const gchar *name)
+GstElement *
+gst_bin_new (const gchar * name)
+{
+  return gst_element_factory_make ("bin", name);
+}
+
+static GstClock*
+gst_bin_get_clock_func (GstElement *element)
+{
+  if (GST_ELEMENT_SCHED (element)) 
+    return gst_scheduler_get_clock (GST_ELEMENT_SCHED (element));
+
+  return NULL;
+}
+
+static void
+gst_bin_set_clock_func (GstElement *element, GstClock *clock)
+{
+  if (GST_ELEMENT_SCHED (element)) 
+    gst_scheduler_use_clock (GST_ELEMENT_SCHED (element), clock);
+}
+
+/**
+ * gst_bin_get_clock:
+ * @bin: a #GstBin to get the clock of
+ *
+ * Gets the current clock of the (scheduler of the) bin.
+ *
+ * Returns: the #GstClock of the bin
+ */
+GstClock*
+gst_bin_get_clock (GstBin *bin)
+{
+  g_return_val_if_fail (bin != NULL, NULL);
+  g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+  return gst_bin_get_clock_func (GST_ELEMENT (bin));
+}
+
+/**
+ * gst_bin_use_clock:
+ * @bin: the bin to set the clock for
+ * @clock: the clock to use.
+ *
+ * Force the bin to use the given clock. Use NULL to 
+ * force it to use no clock at all.
+ */
+void
+gst_bin_use_clock (GstBin *bin, GstClock *clock)
 {
-  return gst_elementfactory_make ("bin", name);
+  g_return_if_fail (GST_IS_BIN (bin));
+
+  gst_bin_set_clock_func (GST_ELEMENT (bin), clock);
+}
+
+/**
+ * gst_bin_auto_clock:
+ * @bin: the bin to autoclock
+ *
+ * Let the bin select a clock automatically.
+ */
+void
+gst_bin_auto_clock (GstBin *bin)
+{
+  g_return_if_fail (GST_IS_BIN (bin));
+
+  if (GST_ELEMENT_SCHED (bin)) 
+    gst_scheduler_auto_clock (GST_ELEMENT_SCHED (bin));
+}
+
+#ifndef GST_DISABLE_INDEX
+static void
+gst_bin_set_index (GstElement *element, GstIndex *index)
+{
+  GstBin *bin = GST_BIN (element);
+  
+  g_return_if_fail (GST_IS_BIN (bin));
+
+  g_list_foreach (bin->children, (GFunc) gst_element_set_index, index);
+}
+#endif
+
+static void
+gst_bin_set_element_sched (GstElement *element, GstScheduler *sched)
+{
+  GST_CAT_INFO (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p", GST_ELEMENT_NAME (element),
+           sched);
+
+  /* if it's actually a Bin */
+  if (GST_IS_BIN (element)) {
+    if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
+      GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: child is already a manager, not resetting", GST_ELEMENT_NAME (element));
+      if (GST_ELEMENT_SCHED (element))
+        gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHED (element));
+      return;
+    }
+
+    GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: setting children's schedule to parent's", GST_ELEMENT_NAME (element));
+    gst_scheduler_add_element (sched, element);
+
+    /* set the children's schedule */
+    g_list_foreach (GST_BIN (element)->children, (GFunc) gst_bin_set_element_sched, sched);
+  }
+  /* otherwise, if it's just a regular old element */
+  else {
+    GList *pads;
+
+    gst_scheduler_add_element (sched, element);
+
+    /* set the sched pointer in all the pads */
+    pads = element->pads;
+    while (pads) {
+      GstPad *pad;
+
+      pad = GST_PAD (pads->data);
+      pads = g_list_next (pads);
+
+      /* we only operate on real pads */
+      if (!GST_IS_REAL_PAD (pad))
+        continue;
+
+      /* if the peer element exists and is a candidate */
+      if (GST_PAD_PEER (pad)) {
+        if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
+          GST_CAT_INFO (GST_CAT_SCHEDULING,
+                   "peer is in same scheduler, telling scheduler");
+
+          if (GST_PAD_IS_SRC (pad))
+            gst_scheduler_pad_link (sched, pad, GST_PAD_PEER (pad));
+          else
+            gst_scheduler_pad_link (sched, GST_PAD_PEER (pad), pad);
+        }
+      }
+    }
+  }
+}
+
+
+static void
+gst_bin_unset_element_sched (GstElement *element, GstScheduler *sched)
+{
+  if (GST_ELEMENT_SCHED (element) == NULL) {
+    GST_CAT_INFO (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler",
+             GST_ELEMENT_NAME (element));
+    return;
+  }
+
+  GST_CAT_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from its sched %p",
+           GST_ELEMENT_NAME (element), GST_ELEMENT_SCHED (element));
+
+  /* if it's actually a Bin */
+  if (GST_IS_BIN (element)) {
+
+    if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
+      GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: child is already a manager, not unsetting sched",
+                    GST_ELEMENT_NAME (element));
+      if (sched) {
+        gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHED (element));
+      }
+      return;
+    }
+    /* for each child, remove them from their schedule */
+    g_list_foreach (GST_BIN (element)->children, (GFunc) gst_bin_unset_element_sched, sched);
+
+    gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element);
+  }
+  /* otherwise, if it's just a regular old element */
+  else {
+    GList *pads;
+
+    /* set the sched pointer in all the pads */
+    pads = element->pads;
+    while (pads) {
+      GstPad *pad;
+
+      pad = GST_PAD (pads->data);
+      pads = g_list_next (pads);
+
+      /* we only operate on real pads */
+      if (!GST_IS_REAL_PAD (pad))
+        continue;
+
+      /* if the peer element exists and is a candidate */
+      if (GST_PAD_PEER (pad)) {
+        if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
+          GST_CAT_INFO (GST_CAT_SCHEDULING, "peer is in same scheduler, telling scheduler");
+
+          if (GST_PAD_IS_SRC (pad))
+            gst_scheduler_pad_unlink (sched, pad, GST_PAD_PEER (pad));
+          else
+            gst_scheduler_pad_unlink (sched, GST_PAD_PEER (pad), pad);
+        }
+      }
+    }
+    gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element);
+  }
+}
+
+
+/**
+ * gst_bin_add_many:
+ * @bin: the bin to add the elements to
+ * @element_1: the first element to add to the bin
+ * @...: NULL-terminated list of elements to add to the bin
+ * 
+ * Add a list of elements to a bin. Uses #gst_bin_add.
+ */
+void
+gst_bin_add_many (GstBin *bin, GstElement *element_1, ...)
+{
+  va_list args;
+
+  g_return_if_fail (GST_IS_BIN (bin));
+  g_return_if_fail (GST_IS_ELEMENT (element_1));
+
+  va_start (args, element_1);
+
+  while (element_1) {
+    gst_bin_add (bin, element_1);
+    
+    element_1 = va_arg (args, GstElement*);
+  }
+
+  va_end (args);
+}
+
+static void
+gst_bin_add_func (GstBin *bin, GstElement *element)
+{
+  gint state_idx = 0;
+  GstElementState state;
+  GstScheduler *sched;
+
+  /* the element must not already have a parent */
+  g_return_if_fail (GST_ELEMENT_PARENT (element) == NULL);
+
+  /* then check to see if the element's name is already taken in the bin */
+  if (gst_object_check_uniqueness (bin->children, 
+                                  GST_ELEMENT_NAME (element)) == FALSE)
+  {
+    g_warning ("Name %s is not unique in bin %s, not adding\n",
+              GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
+    return;
+  }
+
+  /* set the element's parent and add the element to the bin's list of children */
+  gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin));
+
+  bin->children = g_list_append (bin->children, element);
+  bin->numchildren++;
+
+  /* bump our internal state counter */
+  state = GST_STATE (element);
+  while (state >>= 1) state_idx++;
+  bin->child_states[state_idx]++;
+
+  /* now we have to deal with manager stuff 
+   * we can only do this if there's a scheduler: 
+   * if we're not a manager, and aren't attached to anything, we have no sched (yet) */
+  sched = GST_ELEMENT_SCHED (bin);
+  if (sched) {
+    gst_bin_set_element_sched (element, sched);
+  }
+
+  GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: added child \"%s\"", 
+                GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (element));
+
+  g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
 }
 
 /**
@@ -164,29 +476,70 @@ gst_bin_new (const gchar *name)
  * add a reference.
  */
 void
-gst_bin_add (GstBin *bin,
-            GstElement *element)
+gst_bin_add (GstBin *bin, GstElement *element)
 {
-  g_return_if_fail (bin != NULL);
+  GstBinClass *bclass;
+  
   g_return_if_fail (GST_IS_BIN (bin));
-  g_return_if_fail (element != NULL);
   g_return_if_fail (GST_IS_ELEMENT (element));
 
-  // must be NULL or PAUSED state in order to modify bin
-  g_return_if_fail ((GST_STATE (bin) == GST_STATE_NULL) ||
-                   (GST_STATE (bin) == GST_STATE_PAUSED));
+  GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element \"%s\" to bin \"%s\"",
+            GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
 
-  bin->children = g_list_append (bin->children, element);
-  bin->numchildren++;
-  gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin));
+  bclass = GST_BIN_GET_CLASS (bin);
 
-  GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child %s", GST_ELEMENT_NAME (element));
+  if (bclass->add_element) {
+    bclass->add_element (bin, element);
+  }
+  else {
+    g_warning ("cannot add elements to bin %s\n", GST_ELEMENT_NAME (bin));
+  }
+}
+
+static void
+gst_bin_remove_func (GstBin *bin, GstElement *element)
+{
+  gint state_idx = 0;
+  GstElementState state;
 
-  /* we know we have at least one child, we just added one... */
-//  if (GST_STATE(element) < GST_STATE_READY)
-//    gst_bin_change_state_norecurse(bin,GST_STATE_READY);
+  /* the element must have its parent set to the current bin */
+  g_return_if_fail (GST_ELEMENT_PARENT (element) == (GstObject *) bin);
 
-  gtk_signal_emit (GTK_OBJECT (bin), gst_bin_signals[OBJECT_ADDED], element);
+  /* the element must be in the bin's list of children */
+  if (g_list_find (bin->children, element) == NULL) {
+    g_warning ("no element \"%s\" in bin \"%s\"\n", GST_ELEMENT_NAME (element),
+              GST_ELEMENT_NAME (bin));
+    return;
+  }
+
+  /* remove this element from the list of managed elements */
+  gst_bin_unset_element_sched (element, GST_ELEMENT_SCHED (bin));
+
+  /* now remove the element from the list of elements */
+  bin->children = g_list_remove (bin->children, element);
+  bin->numchildren--;
+
+  /* bump our internal state counter */
+  state = GST_STATE (element);
+  while (state >>= 1) state_idx++;
+  bin->child_states[state_idx]--;
+
+  GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: removed child %s", 
+                GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (element));
+
+  /* ref as we're going to emit a signal */
+  gst_object_ref (GST_OBJECT (element));
+  gst_object_unparent (GST_OBJECT (element));
+
+  /* if we're down to zero children, force state to NULL */
+  if (bin->numchildren == 0 && GST_ELEMENT_SCHED (bin) != NULL) {
+    GST_STATE_PENDING (bin) = GST_STATE_NULL;
+    gst_bin_change_state_norecurse (bin);
+  }
+  g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element);
+
+  /* element is really out of our control now */
+  gst_object_unref (GST_OBJECT (element));
 }
 
 /**
@@ -195,195 +548,259 @@ gst_bin_add (GstBin *bin,
  * @element: #GstElement to remove
  *
  * Remove the element from its associated bin, unparenting as well.
+ * The element will also be unreferenced so there's no need to call
+ * gst_object_unref on it.
+ * If you want the element to still exist after removing, you need to call
+ * #gst_object_ref before removing it from the bin.
  */
 void
-gst_bin_remove (GstBin *bin,
-               GstElement *element)
+gst_bin_remove (GstBin *bin, GstElement *element)
 {
-  g_return_if_fail (bin != NULL);
+  GstBinClass *bclass;
+
+  GST_CAT_DEBUG (GST_CAT_PARENTAGE, "[%s]: trying to remove child %s", GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (element));
+
   g_return_if_fail (GST_IS_BIN (bin));
-  g_return_if_fail (element != NULL);
   g_return_if_fail (GST_IS_ELEMENT (element));
   g_return_if_fail (bin->children != NULL);
 
-  // must be NULL or PAUSED state in order to modify bin
-  g_return_if_fail ((GST_STATE (bin) == GST_STATE_NULL) ||
-                   (GST_STATE (bin) == GST_STATE_PAUSED));
+  bclass = GST_BIN_GET_CLASS (bin);
 
-  if (g_list_find(bin->children, element) == NULL) {
-    // FIXME this should be a warning!!!
-    GST_ERROR_OBJECT(bin,element,"no such element in bin");
-    return;
+  if (bclass->remove_element) {
+    bclass->remove_element (bin, element);
+  }
+  else {
+    g_warning ("cannot remove elements from bin %s\n", GST_ELEMENT_NAME (bin));
   }
+}
 
-  gst_object_unparent (GST_OBJECT (element));
-  bin->children = g_list_remove (bin->children, element);
-  bin->numchildren--;
+/**
+ * gst_bin_remove_many:
+ * @bin: the bin to remove the elements from
+ * @element_1: the first element to remove from the bin
+ * @...: NULL-terminated list of elements to remove from the bin
+ * 
+ * Remove a list of elements from a bin. Uses #gst_bin_remove.
+ */
+void
+gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...)
+{
+  va_list args;
 
-  GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "removed child %s", GST_ELEMENT_NAME (element));
+  g_return_if_fail (GST_IS_BIN (bin));
+  g_return_if_fail (GST_IS_ELEMENT (element_1));
 
-  /* if we're down to zero children, force state to NULL */
-  if (bin->numchildren == 0)
-    gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL);
+  va_start (args, element_1);
+
+  while (element_1) {
+    gst_bin_remove (bin, element_1);
+    
+    element_1 = va_arg (args, GstElement*);
+  }
+
+  va_end (args);
 }
 
+/**
+ * gst_bin_child_state_change:
+ * @bin: #GstBin with the child
+ * @oldstate: The old child state
+ * @newstate: The new child state
+ * @child: #GstElement that signaled an changed state
+ *
+ * An internal function to inform the parent bin about a state change
+ * of a child.
+ */
+void
+gst_bin_child_state_change (GstBin *bin, GstElementState oldstate, 
+                            GstElementState newstate, GstElement *child)
+{
+  GstBinClass *bclass;
+  
+  g_return_if_fail (GST_IS_BIN (bin));
+  g_return_if_fail (GST_IS_ELEMENT (child));
+
+  GST_CAT_INFO (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s",
+           GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin),
+           gst_element_state_get_name (oldstate), gst_element_state_get_name (newstate));
+
+  bclass = GST_BIN_GET_CLASS (bin);
+
+  if (bclass->child_state_change) {
+    bclass->child_state_change (bin, oldstate, newstate, child);
+  }
+  else {
+    g_warning ("cannot signal state change of child %s to bin %s\n", 
+               GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin));
+  }
+}
+
+static void
+gst_bin_child_state_change_func (GstBin *bin, GstElementState oldstate, 
+                                 GstElementState newstate, GstElement *child)
+{
+  gint old_idx = 0, new_idx = 0, i;
+
+  while (oldstate >>= 1) old_idx++;
+  while (newstate >>= 1) new_idx++;
+
+  GST_LOCK (bin);
+  bin->child_states[old_idx]--;
+  bin->child_states[new_idx]++;
+  
+  for (i = GST_NUM_STATES - 1; i >= 0; i--) {
+    if (bin->child_states[i] != 0) {
+      gint state = (1 << i);
+      if (GST_STATE (bin) != state) {
+       GST_CAT_INFO (GST_CAT_STATES, "bin %s need state change to %s",
+                 GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
+       GST_STATE_PENDING (bin) = state;
+        GST_UNLOCK (bin);
+       gst_bin_change_state_norecurse (bin);
+       if (state != GST_STATE (bin)) {
+          g_warning ("%s: state change in cllback %d %d", 
+                         GST_ELEMENT_NAME (bin),
+                         state, GST_STATE (bin));
+       }
+       return;
+      }
+      break;
+    }
+  }
+  GST_UNLOCK (bin);
+}
 
 static GstElementStateReturn
-gst_bin_change_state (GstElement *element)
+gst_bin_change_state (GstElement * element)
 {
   GstBin *bin;
   GList *children;
   GstElement *child;
-
-  GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME  (element));
+  GstElementStateReturn ret;
+  GstElementState old_state, pending;
+  gint transition;
+  gboolean have_async = FALSE;
 
   g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
 
   bin = GST_BIN (element);
 
-//  GST_DEBUG (0,"currently %d(%s), %d(%s) pending\n",GST_STATE (element),
-//          _gst_print_statename (GST_STATE (element)), GST_STATE_PENDING (element),
-//          _gst_print_statename (GST_STATE_PENDING (element)));
-
-  GST_INFO_ELEMENT (GST_CAT_STATES, element, "changing bin's state from %s to %s",
-                _gst_print_statename (GST_STATE (element)),
-                _gst_print_statename (GST_STATE_PENDING (element)));
+  old_state = GST_STATE (element);
+  pending = GST_STATE_PENDING (element);
+  transition = GST_STATE_TRANSITION (element);
 
-//  g_return_val_if_fail(bin->numchildren != 0, GST_STATE_FAILURE);
+  GST_CAT_INFO (GST_CAT_STATES, "[%s]: changing childrens' state from %s to %s",
+               GST_ELEMENT_NAME (element),
+               gst_element_state_get_name (old_state), gst_element_state_get_name (pending));
 
-  switch (GST_STATE_TRANSITION (element)) {
-    case GST_STATE_NULL_TO_READY:
-    {
-      GstObject *parent;
+  if (pending == GST_STATE_VOID_PENDING)
+    return GST_STATE_SUCCESS;
 
-      parent = gst_object_get_parent (GST_OBJECT (element));
-
-      if (!parent || !GST_IS_BIN (parent))
-        gst_bin_create_plan (bin);
-
-      break;
-    }
-    default:
-      break;
+  if (old_state == pending)
+  {
+    GST_CAT_INFO (GST_CAT_STATES, "[%s]: old and pending state are both %s, returning",
+                  GST_ELEMENT_NAME (element), gst_element_state_get_name (pending));
+    return GST_STATE_SUCCESS;
   }
 
-//  g_print("-->\n");
   children = bin->children;
+
   while (children) {
+    GstElementState old_child_state;
+
     child = GST_ELEMENT (children->data);
-    GST_DEBUG (0,"setting state on '%s'\n",GST_ELEMENT_NAME  (child));
-    switch (gst_element_set_state (child, GST_STATE_PENDING (element))) {
-      case GST_STATE_FAILURE:
-        GST_STATE_PENDING (element) = GST_STATE_NONE_PENDING;
-        GST_DEBUG (0,"child '%s' failed to go to state %d(%s)\n", GST_ELEMENT_NAME  (child),
-              GST_STATE_PENDING (element), _gst_print_statename (GST_STATE_PENDING (element)));
-        return GST_STATE_FAILURE;
-        break;
-      case GST_STATE_ASYNC:
-        GST_DEBUG (0,"child '%s' is changing state asynchronously\n", GST_ELEMENT_NAME  (child));
-        break;
-    }
-//    g_print("\n");
     children = g_list_next (children);
-  }
-//  g_print("<-- \"%s\"\n",gst_object_get_name(GST_OBJECT(bin)));
-
-
-  return gst_bin_change_state_norecurse (bin);
-}
 
+    if (GST_FLAG_IS_SET (child, GST_ELEMENT_LOCKED_STATE))
+      continue;
 
-static GstElementStateReturn
-gst_bin_change_state_norecurse (GstBin *bin)
-{
+    old_child_state = GST_STATE (child);
 
-  if (GST_ELEMENT_CLASS (parent_class)->change_state)
-    return GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT (bin));
-  else
-    return GST_STATE_FAILURE;
-}
-
-static gboolean
-gst_bin_change_state_type(GstBin *bin,
-                          GstElementState state,
-                          GtkType type)
-{
-  GList *children;
-  GstElement *child;
-
-//  g_print("gst_bin_change_state_type(\"%s\",%d,%d);\n",
-//          gst_object_get_name(GST_OBJECT(bin)),state,type);
+    switch (gst_element_set_state (child, pending)) {
+      case GST_STATE_FAILURE:
+       GST_CAT_DEBUG (GST_CAT_STATES, "child '%s' failed to go to state %d(%s)",
+                  GST_ELEMENT_NAME (child), pending, gst_element_state_get_name (pending));
 
-  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
-  g_return_val_if_fail (bin->numchildren != 0, FALSE);
+       gst_element_set_state (child, old_child_state);
+       /* FIXME, this is legacy code, a failed state change of a child should
+        * return a failure in all cases */
+       if (GST_ELEMENT_SCHED (child) == GST_ELEMENT_SCHED (element)) {
+          /* try to reset it to what is was */
+          GST_STATE_PENDING (element) = old_state;
 
-//  g_print("-->\n");
-  children = bin->children;
-  while (children) {
-    child = GST_ELEMENT (children->data);
-    if (GST_IS_BIN (child)) {
-      if (!gst_bin_set_state_type (GST_BIN (child), state,type))
-        return FALSE;
-    } else if (GTK_CHECK_TYPE (child,type)) {
-      if (!gst_element_set_state (child,state))
-        return FALSE;
+         return GST_STATE_FAILURE;
+       }
+       break;
+      case GST_STATE_ASYNC:
+       GST_CAT_DEBUG (GST_CAT_STATES, "child '%s' is changing state asynchronously",
+                  GST_ELEMENT_NAME (child));
+       have_async = TRUE;
+       break;
+      case GST_STATE_SUCCESS:
+        break; 
     }
-//    g_print("\n");
-    children = g_list_next (children);
   }
-  if (type == GST_TYPE_BIN)
-    gst_element_set_state (GST_ELEMENT (bin),state);
 
-  return TRUE;
+  GST_CAT_INFO (GST_CAT_STATES, "[%s]: done changing bin's state from %s to %s, now in %s",
+               GST_ELEMENT_NAME (element),
+                gst_element_state_get_name (old_state),
+                gst_element_state_get_name (pending),
+                gst_element_state_get_name (GST_STATE (element)));
+
+  if (have_async)
+    ret = GST_STATE_ASYNC;
+  else {
+    if (parent_class->change_state) {
+      ret = parent_class->change_state(element);
+    }
+    else
+      ret = GST_STATE_SUCCESS;
+  }
+  return ret;
 }
 
-/**
- * gst_bin_set_state_type:
- * @bin: #GstBin to set the state
- * @state: the new state to set the elements to
- * @type: the type of elements to change
- *
- * Sets the state of only those objects of the given type.
- *
- * Returns: indication if the state change was successfull
- */
-gboolean
-gst_bin_set_state_type (GstBin *bin,
-                        GstElementState state,
-                        GtkType type)
-{
-  GstBinClass *oclass;
-
-  GST_DEBUG (0,"gst_bin_set_state_type(\"%s\",%d,%d)\n",
-          GST_ELEMENT_NAME (bin), state,type);
 
-  g_return_val_if_fail (bin != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
+static GstElementStateReturn
+gst_bin_change_state_norecurse (GstBin * bin)
+{
+  GstElementStateReturn ret;
 
-  oclass = GST_BIN_CLASS (GTK_OBJECT (bin)->klass);
+  if (parent_class->change_state) {
+    GST_CAT_DEBUG (GST_CAT_STATES, "[%s]: setting bin's own state", GST_ELEMENT_NAME (bin));
+    ret = parent_class->change_state (GST_ELEMENT (bin));
 
-  if (oclass->change_state_type)
-    (oclass->change_state_type) (bin,state,type);
-  return TRUE;
+    return ret;
+  }
+  else
+    return GST_STATE_FAILURE;
 }
 
 static void
-gst_bin_real_destroy (GtkObject *object)
+gst_bin_dispose (GObject * object)
 {
   GstBin *bin = GST_BIN (object);
-  GList *children;
+  GList *children, *orig;
   GstElement *child;
 
-  GST_DEBUG (0,"in gst_bin_real_destroy()\n");
+  GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "dispose");
 
-  children = bin->children;
-  while (children) {
-    child = GST_ELEMENT (children->data);
-    gst_element_destroy (child);
-    children = g_list_next (children);
+  if (gst_element_get_state (GST_ELEMENT (object)) == GST_STATE_PLAYING)
+    gst_element_set_state (GST_ELEMENT (object), GST_STATE_PAUSED);
+
+  if (bin->children) {
+    orig = children = g_list_copy (bin->children);
+    while (children) {
+      child = GST_ELEMENT (children->data);
+      gst_bin_remove (bin, child);
+      children = g_list_next (children);
+    }
+    g_list_free (bin->children);
+    g_list_free (orig);
   }
+  bin->children = NULL;
+  bin->numchildren = 0;
 
-  g_list_free (bin->children);
+  G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 /**
@@ -395,9 +812,8 @@ gst_bin_real_destroy (GtkObject *object)
  *
  * Returns: the element with the given name
  */
-GstElement*
-gst_bin_get_by_name (GstBin *bin,
-                    const gchar *name)
+GstElement *
+gst_bin_get_by_name (GstBin * bin, const gchar * name)
 {
   GList *children;
   GstElement *child;
@@ -406,17 +822,19 @@ gst_bin_get_by_name (GstBin *bin,
   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
   g_return_val_if_fail (name != NULL, NULL);
 
-  GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "looking up child element %s", name);
+  GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s", 
+                GST_ELEMENT_NAME (bin), name);
 
   children = bin->children;
   while (children) {
     child = GST_ELEMENT (children->data);
-    if (!strcmp (gst_object_get_name (GST_OBJECT (child)),name))
+    if (!strcmp (GST_OBJECT_NAME (child), name))
       return child;
     if (GST_IS_BIN (child)) {
       GstElement *res = gst_bin_get_by_name (GST_BIN (child), name);
+
       if (res)
-        return res;
+       return res;
     }
     children = g_list_next (children);
   }
@@ -434,9 +852,8 @@ gst_bin_get_by_name (GstBin *bin,
  *
  * Returns: the element with the given name
  */
-GstElement*
-gst_bin_get_by_name_recurse_up (GstBin *bin,
-                               const gchar *name)
+GstElement *
+gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name)
 {
   GstElement *result = NULL;
   GstObject *parent;
@@ -447,13 +864,12 @@ gst_bin_get_by_name_recurse_up (GstBin *bin,
 
   result = gst_bin_get_by_name (bin, name);
 
-  if (result)
-    return result;
+  if (!result) {
+    parent = gst_object_get_parent (GST_OBJECT (bin));
 
-  parent = gst_object_get_parent (GST_OBJECT (bin));
-
-  if (parent && GST_IS_BIN (parent)) {
-    result = gst_bin_get_by_name_recurse_up (GST_BIN (parent), name);
+    if (parent && GST_IS_BIN (parent)) {
+      result = gst_bin_get_by_name_recurse_up (GST_BIN (parent), name);
+    }
   }
 
   return result;
@@ -467,18 +883,137 @@ gst_bin_get_by_name_recurse_up (GstBin *bin,
  *
  * Returns: a GList of elements
  */
-GList*
-gst_bin_get_list (GstBin *bin)
+const GList *
+gst_bin_get_list (GstBin * bin)
 {
-  g_return_val_if_fail (bin != NULL, NULL);
   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
 
   return bin->children;
 }
 
+/**
+ * gst_bin_get_by_interface:
+ * @bin: bin to find element in
+ * @interface: interface to be implemented by interface
+ *
+ * Looks for the first element inside the bin that implements the given 
+ * interface. If such an element is found, it returns the element. You can
+ * cast this element to the given interface afterwards.
+ * If you want all elements that implement the interface, use 
+ * gst_bin_get_all_by_interface(). The function recurses bins inside bins.
+ *
+ * Returns: An element inside the bin implementing the interface.
+ */
+GstElement *
+gst_bin_get_by_interface (GstBin *bin, const GType interface)
+{
+  GList *walk;
+  
+  g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL);
+
+  walk = bin->children;
+  while (walk) {
+    if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface))
+      return GST_ELEMENT (walk->data);
+    if (GST_IS_BIN (walk->data)) {
+      GstElement *ret;
+      ret = gst_bin_get_by_interface (GST_BIN (walk->data), interface);
+      if (ret)
+       return ret;
+    }
+    walk = g_list_next (walk);
+  }
+
+  return NULL;
+}
+
+/**
+ * gst_bin_get_all_by_interface:
+ * @bin: bin to find elements in
+ * @interface: interface to be implemented by interface
+ *
+ * Looks for all element inside the bin that implements the given 
+ * interface. You can safely cast all returned elements to the given interface.
+ * The function recurses bins inside bins. You need to free the list using 
+ * g_list_free() after use.
+ *
+ * Returns: An element inside the bin implementing the interface.
+ */
+GList *
+gst_bin_get_all_by_interface (GstBin *bin, const GType interface)
+{
+  GList *walk, *ret = NULL;
+    
+  g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL);
+
+  walk = bin->children;
+  while (walk) {
+    if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface))
+      ret = g_list_prepend (ret, walk->data);
+    if (GST_IS_BIN (walk->data)) {
+      ret = g_list_concat (ret, 
+         gst_bin_get_all_by_interface (GST_BIN (walk->data), interface));
+    }
+    walk = g_list_next (walk);
+  }
+
+  return ret; 
+}
+
+/**
+ * gst_bin_sync_children_state:
+ * @bin: #Gstbin to sync state
+ *
+ * Tries to set the state of the children of this bin to the same state of the
+ * bin by calling gst_element_set_state for each child not already having a
+ * synchronized state. 
+ *
+ * Returns: The worst return value of any gst_element_set_state. So if one child
+ *          returns #GST_STATE_FAILURE while all others return #GST_STATE_SUCCESS
+ *          this function returns #GST_STATE_FAILURE.
+ */
+GstElementStateReturn
+gst_bin_sync_children_state (GstBin *bin)
+{
+  GList *children;
+  GstElement *element;
+  GstElementState state;
+  GstElementStateReturn ret = GST_STATE_SUCCESS;
+
+  g_return_val_if_fail (GST_IS_BIN (bin), GST_STATE_FAILURE);
+
+  state = GST_STATE (bin);
+  children = bin->children;
+  GST_CAT_INFO (GST_CAT_STATES, "syncing state of children with bin \"%s\"'s state %s",
+            GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
+
+  while (children) {
+    element = GST_ELEMENT (children->data);
+    children = children->next;
+    if (GST_STATE(element) != state) {
+      switch (gst_element_set_state (element, state)) {
+      case GST_STATE_SUCCESS:
+       break;
+      case GST_STATE_ASYNC:
+       if (ret == GST_STATE_SUCCESS)
+         ret = GST_STATE_ASYNC;
+       break;
+      case GST_STATE_FAILURE:
+       ret = GST_STATE_FAILURE;
+      default:
+        /* make sure gst_element_set_state never returns this */
+        g_assert_not_reached ();
+      }
+    }
+  }
+
+  return ret;
+}
+#ifndef GST_DISABLE_LOADSAVE
 static xmlNodePtr
-gst_bin_save_thyself (GstObject *object,
-                     xmlNodePtr parent)
+gst_bin_save_thyself (GstObject * object, xmlNodePtr parent)
 {
   GstBin *bin = GST_BIN (object);
   xmlNodePtr childlist, elementnode;
@@ -490,7 +1025,8 @@ gst_bin_save_thyself (GstObject *object,
 
   childlist = xmlNewChild (parent, NULL, "children", NULL);
 
-  GST_INFO_ELEMENT (GST_CAT_XML, bin, "saving %d children", bin->numchildren);
+  GST_CAT_INFO (GST_CAT_XML, "[%s]: saving %d children", 
+                GST_ELEMENT_NAME (bin), bin->numchildren);
 
   children = bin->children;
   while (children) {
@@ -503,8 +1039,7 @@ gst_bin_save_thyself (GstObject *object,
 }
 
 static void
-gst_bin_restore_thyself (GstObject *object,
-                        xmlNodePtr self)
+gst_bin_restore_thyself (GstObject * object, xmlNodePtr self)
 {
   GstBin *bin = GST_BIN (object);
   xmlNodePtr field = self->xmlChildrenNode;
@@ -512,356 +1047,90 @@ gst_bin_restore_thyself (GstObject *object,
 
   while (field) {
     if (!strcmp (field->name, "children")) {
-      GST_INFO_ELEMENT (GST_CAT_XML, GST_ELEMENT (object), "loading children");
+      GST_CAT_INFO (GST_CAT_XML, "[%s]: loading children", GST_ELEMENT_NAME (object));
       childlist = field->xmlChildrenNode;
       while (childlist) {
-        if (!strcmp (childlist->name, "element")) {
-          GstElement *element = gst_element_load_thyself (childlist, GST_OBJECT (bin));
-
+       if (!strcmp (childlist->name, "element")) {
+         GstElement *element = gst_xml_make_element (childlist, GST_OBJECT (bin));
+          
+          /* it had to be parented to find the pads, now we ref and unparent so
+           * we can add it to the bin */
+          gst_object_ref (GST_OBJECT (element));
+          gst_object_unparent (GST_OBJECT (element));
+          
          gst_bin_add (bin, element);
        }
-        childlist = childlist->next;
+       childlist = childlist->next;
       }
     }
 
     field = field->next;
   }
 }
+#endif /* GST_DISABLE_LOADSAVE */
 
-void
-gst_bin_use_cothreads (GstBin *bin,
-                      gboolean enabled)
+static gboolean
+gst_bin_iterate_func (GstBin * bin)
 {
-  g_return_if_fail (GST_IS_BIN (bin));
+  /* only iterate if this is the manager bin */
+  if (GST_ELEMENT_SCHED (bin) &&
+      GST_ELEMENT_SCHED (bin)->parent == GST_ELEMENT (bin)) {
+    GstSchedulerState state;
 
-  bin->use_cothreads = enabled;
+    state = gst_scheduler_iterate (GST_ELEMENT_SCHED (bin));
+
+    if (state == GST_SCHEDULER_STATE_RUNNING) {
+      return TRUE;
+    }
+    else if (state == GST_SCHEDULER_STATE_ERROR) {
+      gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
+    }
+  }
+  else {
+    g_warning ("bin \"%s\" is not the managing bin, can't be iterated on!\n", 
+              GST_ELEMENT_NAME (bin));
+  }
+
+  return FALSE;
 }
 
 /**
  * gst_bin_iterate:
- * @bin: #Gstbin to iterate
+ * @bin: a#GstBin to iterate.
  *
  * Iterates over the elements in this bin.
  *
- * Returns: TRUE if the bin did something usefull. This value
+ * Returns: TRUE if the bin did something useful. This value
  *          can be used to determine it the bin is in EOS.
  */
 gboolean
 gst_bin_iterate (GstBin *bin)
 {
-  GstBinClass *oclass;
-  gboolean eos = TRUE;
-
-  GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME (bin));
-
-  oclass = GST_BIN_CLASS (GTK_OBJECT (bin)->klass);
-
-  if (oclass->iterate)
-    eos = (oclass->iterate) (bin);
-
-  GST_DEBUG_LEAVE("(\"%s\")",GST_ELEMENT_NAME (bin));
-
-  return eos;
-}
+  gboolean running;
 
-/**
- * gst_bin_create_plan:
- * @bin: #GstBin to create the plan for
- *
- * Let the bin figure out how to handle its children.
- */
-void
-gst_bin_create_plan (GstBin *bin)
-{
-  GstBinClass *oclass;
-
-  oclass = GST_BIN_CLASS (GTK_OBJECT (bin)->klass);
-
-  if (oclass->create_plan)
-    (oclass->create_plan) (bin);
-}
-
-/* out internal element fired EOS, we decrement the number of pending EOS childs */
-static void
-gst_bin_received_eos (GstElement *element, GstBin *bin)
-{
-  GST_INFO_ELEMENT (GST_CAT_PLANNING, bin, "child %s fired eos, pending %d\n", GST_ELEMENT_NAME (element),
-                 bin->num_eos_providers);
-
-  GST_LOCK (bin);
-  if (bin->num_eos_providers) {
-    bin->num_eos_providers--;
-    g_cond_signal (bin->eoscond);
-  }
-  GST_UNLOCK (bin);
-}
-
-/**
- * gst_bin_schedule:
- * @bin: #GstBin to schedule
- *
- * Let the bin figure out how to handle its children.
- */
-void
-gst_bin_schedule (GstBin *bin)
-{
-  GstBinClass *oclass;
-
-  oclass = GST_BIN_CLASS (GTK_OBJECT (bin)->klass);
-
-  if (oclass->schedule)
-    (oclass->schedule) (bin);
-}
-
-typedef struct {
-  gulong offset;
-  gulong size;
-} region_struct;
-
-
-static void
-gst_bin_create_plan_func (GstBin *bin)
-{
-  GstElement *manager;
-  GList *elements;
-  GstElement *element;
-#ifdef GST_DEBUG_ENABLED
-  const gchar *elementname;
-#endif
-  GSList *pending = NULL;
-  GstBin *pending_bin;
-
-  GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME  (bin));
-
-  GST_INFO_ELEMENT (GST_CAT_PLANNING, bin, "creating plan");
-
-  // first figure out which element is the manager of this and all child elements
-  // if we're a managing bin ourselves, that'd be us
-  if (GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER)) {
-    manager = GST_ELEMENT (bin);
-    GST_DEBUG (0,"setting manager to self\n");
-  // otherwise, it's what our parent says it is
-  } else {
-    manager = gst_element_get_manager (GST_ELEMENT (bin));
-    if (!manager) {
-      GST_DEBUG (0,"manager not set for element \"%s\" assuming manager is self\n", GST_ELEMENT_NAME (bin));
-      manager = GST_ELEMENT (bin);
-      GST_FLAG_SET (bin, GST_BIN_FLAG_MANAGER);
-    }
-    GST_DEBUG (0,"setting manager to \"%s\"\n", GST_ELEMENT_NAME (manager));
-  }
-  gst_element_set_manager (GST_ELEMENT (bin), manager);
-
-  // perform the first recursive pass of plan generation
-  // we set the manager of every element but those who manage themselves
-  // the need for cothreads is also determined recursively
-  GST_DEBUG (0,"performing first-phase recursion\n");
-  bin->need_cothreads = bin->use_cothreads;
-  if (bin->need_cothreads)
-    GST_DEBUG (0,"requiring cothreads because we're forced to\n");
-
-  elements = bin->children;
-  while (elements) {
-    element = GST_ELEMENT (elements->data);
-    elements = g_list_next (elements);
-#ifdef GST_DEBUG_ENABLED
-    elementname = GST_ELEMENT_NAME  (element);
-#endif
-    GST_DEBUG (0,"have element \"%s\"\n",elementname);
-
-    // first set their manager
-    GST_DEBUG (0,"setting manager of \"%s\" to \"%s\"\n",elementname,GST_ELEMENT_NAME (manager));
-    gst_element_set_manager (element, manager);
-
-    // we do recursion and such for Bins
-    if (GST_IS_BIN (element)) {
-      // recurse into the child Bin
-      GST_DEBUG (0,"recursing into child Bin \"%s\" with manager \"%s\"\n",elementname,
-                     GST_ELEMENT_NAME (element->manager));
-      gst_bin_create_plan (GST_BIN (element));
-      GST_DEBUG (0,"after recurse got manager \"%s\"\n",
-                     GST_ELEMENT_NAME (element->manager));
-      // check to see if it needs cothreads and isn't self-managing
-      if (((GST_BIN (element))->need_cothreads) && !GST_FLAG_IS_SET(element,GST_BIN_FLAG_MANAGER)) {
-        GST_DEBUG (0,"requiring cothreads because child bin \"%s\" does\n",elementname);
-        bin->need_cothreads = TRUE;
-      }
-    } else {
-      // then we need to determine whether they need cothreads
-      // if it's a loop-based element, use cothreads
-      if (element->loopfunc != NULL) {
-        GST_DEBUG (0,"requiring cothreads because \"%s\" is a loop-based element\n",elementname);
-        GST_FLAG_SET (element, GST_ELEMENT_USE_COTHREAD);
-      // if it's a 'complex' element, use cothreads
-      } else if (GST_FLAG_IS_SET (element, GST_ELEMENT_COMPLEX)) {
-        GST_DEBUG (0,"requiring cothreads because \"%s\" is complex\n",elementname);
-        GST_FLAG_SET (element, GST_ELEMENT_USE_COTHREAD);
-      // if the element has more than one sink pad, use cothreads
-      } else if (element->numsinkpads > 1) {
-        GST_DEBUG (0,"requiring cothreads because \"%s\" has more than one sink pad\n",elementname);
-        GST_FLAG_SET (element, GST_ELEMENT_USE_COTHREAD);
-      }
-      if (GST_FLAG_IS_SET (element, GST_ELEMENT_USE_COTHREAD))
-        bin->need_cothreads = TRUE;
-    }
-  }
-
-
-  // if we're not a manager thread, we're done.
-  if (!GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER)) {
-    GST_DEBUG_LEAVE("(\"%s\")",GST_ELEMENT_NAME (bin));
-    return;
-  }
-
-  // clear previous plan state
-  g_list_free (bin->managed_elements);
-  bin->managed_elements = NULL;
-  bin->num_managed_elements = 0;
-
-  // find all the managed children
-  // here we pull off the trick of walking an entire arbitrary tree without recursion
-  GST_DEBUG (0,"attempting to find all the elements to manage\n");
-  pending = g_slist_prepend (pending, bin);
-  do {
-    // retrieve the top of the stack and pop it
-    pending_bin = GST_BIN (pending->data);
-    pending = g_slist_remove (pending, pending_bin);
-
-    // walk the list of elements, find bins, and do stuff
-    GST_DEBUG (0,"checking Bin \"%s\" for managed elements\n",
-          GST_ELEMENT_NAME  (pending_bin));
-    elements = pending_bin->children;
-    while (elements) {
-      element = GST_ELEMENT (elements->data);
-      elements = g_list_next (elements);
-#ifdef GST_DEBUG_ENABLED
-      elementname = GST_ELEMENT_NAME  (element);
-#endif
-
-      // if it's ours, add it to the list
-      if (element->manager == GST_ELEMENT(bin)) {
-        // if it's a Bin, add it to the list of Bins to check
-        if (GST_IS_BIN (element)) {
-          GST_DEBUG (0,"flattened recurse into \"%s\"\n",elementname);
-          pending = g_slist_prepend (pending, element);
-
-        // otherwise add it to the list of elements
-        } else {
-          GST_DEBUG (0,"found element \"%s\" that I manage\n",elementname);
-          bin->managed_elements = g_list_prepend (bin->managed_elements, element);
-          bin->num_managed_elements++;
-        }
-      }
-      // else it's not ours and we need to wait for EOS notifications
-      else {
-        gtk_signal_connect (GTK_OBJECT (element), "eos", gst_bin_received_eos, bin);
-        bin->eos_providers = g_list_prepend (bin->eos_providers, element);
-        bin->num_eos_providers++;
-      }
-    }
-  } while (pending);
+  g_return_val_if_fail (bin != NULL, FALSE);
+  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
 
-  GST_DEBUG (0,"have %d elements to manage, implementing plan\n",bin->num_managed_elements);
+  GST_CAT_LOG_OBJECT (GST_CAT_DATAFLOW, bin, "starting iteration");
 
-  gst_bin_schedule(bin);
+  gst_object_ref (GST_OBJECT (bin));
 
-//  g_print ("gstbin \"%s\", eos providers:%d\n",
-//               GST_ELEMENT_NAME (bin),
-//               bin->num_eos_providers);
+  running = FALSE;
+  g_signal_emit (G_OBJECT (bin), gst_bin_signals[ITERATE], 0, &running);
 
-  GST_DEBUG_LEAVE("(\"%s\")",GST_ELEMENT_NAME (bin));
-}
+  GST_CAT_LOG_OBJECT (GST_CAT_DATAFLOW, bin, "finished iteration");
 
-static gboolean
-gst_bin_iterate_func (GstBin *bin)
-{
-  GList *chains;
-  _GstBinChain *chain;
-  GList *entries;
-  GstElement *entry;
-  GList *pads;
-  GstPad *pad;
-  GstBuffer *buf = NULL;
-  gint num_scheduled = 0;
-  gboolean eos = FALSE;
-
-  GST_DEBUG_ENTER("(\"%s\")", GST_ELEMENT_NAME (bin));
-
-  g_return_val_if_fail (bin != NULL, TRUE);
-  g_return_val_if_fail (GST_IS_BIN (bin), TRUE);
-  g_return_val_if_fail (GST_STATE (bin) == GST_STATE_PLAYING, TRUE);
-
-  // step through all the chains
-  chains = bin->chains;
-  while (chains) {
-    chain = (_GstBinChain *)(chains->data);
-    chains = g_list_next (chains);
-
-    if (!chain->need_scheduling) continue;
-
-    if (chain->need_cothreads) {
-      // all we really have to do is switch to the first child
-      // FIXME this should be lots more intelligent about where to start
-      GST_DEBUG (0,"starting iteration via cothreads\n");
-
-      entry = GST_ELEMENT (chain->elements->data);
-      GST_FLAG_SET (entry, GST_ELEMENT_COTHREAD_STOPPING);
-      GST_DEBUG (0,"set COTHREAD_STOPPING flag on \"%s\"(@%p)\n",
-            GST_ELEMENT_NAME (entry),entry);
-      cothread_switch (entry->threadstate);
-
-    } else {
-      GST_DEBUG (0,"starting iteration via chain-functions\n");
-
-      entries = chain->entries;
-
-      g_assert (entries != NULL);
-
-      while (entries) {
-        entry = GST_ELEMENT (entries->data);
-        entries = g_list_next (entries);
-
-        GST_DEBUG (0,"have entry \"%s\"\n",GST_ELEMENT_NAME (entry));
-
-        if (GST_IS_BIN (entry)) {
-          gst_bin_iterate (GST_BIN (entry));
-        } else {
-          pads = entry->pads;
-          while (pads) {
-            pad = GST_PAD (pads->data);
-            if (GST_RPAD_DIRECTION(pad) == GST_PAD_SRC) {
-              GST_DEBUG (0,"calling getfunc of %s:%s\n",GST_DEBUG_PAD_NAME(pad));
-              if (GST_REAL_PAD(pad)->getfunc == NULL)
-                fprintf(stderr, "error, no getfunc in \"%s\"\n", GST_ELEMENT_NAME  (entry));
-              else
-                buf = (GST_REAL_PAD(pad)->getfunc)(pad);
-              if (buf) gst_pad_push(pad,buf);
-            }
-            pads = g_list_next (pads);
-          }
-        }
-      }
-    }
-    num_scheduled++;
-  }
-
-  // check if nothing was scheduled that was ours..
-  if (!num_scheduled) {
-    // are there any other elements that are still busy?
-    if (bin->num_eos_providers) {
-      GST_LOCK (bin);
-      GST_DEBUG (0,"waiting for eos providers\n");
-      g_cond_wait (bin->eoscond, GST_OBJECT(bin)->lock);
-      GST_DEBUG (0,"num eos providers %d\n", bin->num_eos_providers);
-      GST_UNLOCK (bin);
-    }
-    else {
-      gst_element_signal_eos (GST_ELEMENT (bin));
-      eos = TRUE;
+  if (!running) {
+    if (GST_STATE (bin) == GST_STATE_PLAYING && 
+       GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING) {
+      GST_CAT_DEBUG (GST_CAT_DATAFLOW, "[%s]: polling for child shutdown after useless iteration",
+                    GST_ELEMENT_NAME (bin));
+      g_usleep (1);
+      running = TRUE;
     }
   }
+  gst_object_unref (GST_OBJECT (bin));
 
-  GST_DEBUG_LEAVE("(%s)", GST_ELEMENT_NAME (bin));
-  return !eos;
+  return running;
 }