/* 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;
}
/**
*
* 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);
}
/**
* 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));
}
/**
* @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);
}
/**
*
* 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;
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);
}
*
* 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;
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;
*
* 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;
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) {
}
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;
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;
}