First THREADED backport attempt, focusing on adding locks and making sure the API...
[platform/upstream/gstreamer.git] / gst / gstbin.c
1 /* GStreamer
2  * 
3  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
4  *                    2004 Wim Taymans <wim@fluendo.com>
5  *
6  * gstbin.c: GstBin container object and support code
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  * MT safe.
24  */
25
26 #include "gst_private.h"
27
28 #include "gstevent.h"
29 #include "gstbin.h"
30 #include "gstmarshal.h"
31 #include "gstxml.h"
32 #include "gstinfo.h"
33 #include "gsterror.h"
34
35 #include "gstscheduler.h"
36 #include "gstindex.h"
37 #include "gstutils.h"
38
39 GST_DEBUG_CATEGORY_STATIC (bin_debug);
40 #define GST_CAT_DEFAULT bin_debug
41 #define GST_LOG_BIN_CONTENTS(bin, text) GST_LOG_OBJECT ((bin), \
42         text ": %d elements: %u PLAYING, %u PAUSED, %u READY, %u NULL, own state: %s", \
43         (bin)->numchildren, (guint) (bin)->child_states[3], \
44         (guint) (bin)->child_states[2], (bin)->child_states[1], \
45         (bin)->child_states[0], gst_element_state_get_name (GST_STATE (bin)))
46
47
48 static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin",
49     "Generic/Bin",
50     "Simple container object",
51     "Erik Walthinsen <omega@cse.ogi.edu>," "Wim Taymans <wim@fluendo.com>");
52
53 GType _gst_bin_type = 0;
54
55 static gboolean _gst_boolean_did_something_accumulator (GSignalInvocationHint *
56     ihint, GValue * return_accu, const GValue * handler_return, gpointer dummy);
57
58 static void gst_bin_dispose (GObject * object);
59
60 static GstElementStateReturn gst_bin_change_state (GstElement * element);
61 static GstElementStateReturn gst_bin_change_state_norecurse (GstBin * bin);
62
63 #ifndef GST_DISABLE_INDEX
64 static void gst_bin_set_index (GstElement * element, GstIndex * index);
65 #endif
66
67 static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
68 static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
69 static void gst_bin_child_state_change_func (GstBin * bin,
70     GstElementState oldstate, GstElementState newstate, GstElement * child);
71 GstElementStateReturn gst_bin_set_state (GstElement * element,
72     GstElementState state);
73
74 static GstClock *gst_bin_get_clock_func (GstElement * element);
75 static void gst_bin_set_clock_func (GstElement * element, GstClock * clock);
76
77 static gboolean gst_bin_iterate_func (GstBin * bin);
78
79 #ifndef GST_DISABLE_LOADSAVE
80 static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent);
81 static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self);
82 #endif
83
84 /* Bin signals and args */
85 enum
86 {
87   ELEMENT_ADDED,
88   ELEMENT_REMOVED,
89   ITERATE,
90   LAST_SIGNAL
91 };
92
93 enum
94 {
95   ARG_0
96       /* FILL ME */
97 };
98
99 static void gst_bin_base_init (gpointer g_class);
100 static void gst_bin_class_init (GstBinClass * klass);
101 static void gst_bin_init (GstBin * bin);
102
103 static GstElementClass *parent_class = NULL;
104 static guint gst_bin_signals[LAST_SIGNAL] = { 0 };
105
106 /**
107  * gst_bin_get_type:
108  *
109  * Returns: the type of #GstBin
110  */
111 GType
112 gst_bin_get_type (void)
113 {
114   if (!_gst_bin_type) {
115     static const GTypeInfo bin_info = {
116       sizeof (GstBinClass),
117       gst_bin_base_init,
118       NULL,
119       (GClassInitFunc) gst_bin_class_init,
120       NULL,
121       NULL,
122       sizeof (GstBin),
123       0,
124       (GInstanceInitFunc) gst_bin_init,
125       NULL
126     };
127
128     _gst_bin_type =
129         g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
130
131     GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD,
132         "debugging info for the 'bin' container element");
133   }
134   return _gst_bin_type;
135 }
136
137 static void
138 gst_bin_base_init (gpointer g_class)
139 {
140   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
141
142   gst_element_class_set_details (gstelement_class, &gst_bin_details);
143 }
144
145 static void
146 gst_bin_class_init (GstBinClass * klass)
147 {
148   GObjectClass *gobject_class;
149   GstObjectClass *gstobject_class;
150   GstElementClass *gstelement_class;
151
152   gobject_class = (GObjectClass *) klass;
153   gstobject_class = (GstObjectClass *) klass;
154   gstelement_class = (GstElementClass *) klass;
155
156   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
157
158   gst_bin_signals[ELEMENT_ADDED] =
159       g_signal_new ("element-added", G_TYPE_FROM_CLASS (klass),
160       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_added), NULL,
161       NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
162   gst_bin_signals[ELEMENT_REMOVED] =
163       g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass),
164       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL,
165       NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
166   gst_bin_signals[ITERATE] =
167       g_signal_new ("iterate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
168       G_STRUCT_OFFSET (GstBinClass, iterate),
169       _gst_boolean_did_something_accumulator, NULL, gst_marshal_BOOLEAN__VOID,
170       G_TYPE_BOOLEAN, 0);
171
172   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose);
173
174 #ifndef GST_DISABLE_LOADSAVE
175   gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
176   gstobject_class->restore_thyself =
177       GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
178 #endif
179
180   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state);
181   gstelement_class->set_state = GST_DEBUG_FUNCPTR (gst_bin_set_state);
182 #ifndef GST_DISABLE_INDEX
183   gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index);
184 #endif
185
186   klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
187   klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
188   klass->child_state_change =
189       GST_DEBUG_FUNCPTR (gst_bin_child_state_change_func);
190   klass->iterate = GST_DEBUG_FUNCPTR (gst_bin_iterate_func);
191 }
192
193 static gboolean
194 _gst_boolean_did_something_accumulator (GSignalInvocationHint * ihint,
195     GValue * return_accu, const GValue * handler_return, gpointer dummy)
196 {
197   gboolean did_something;
198
199   did_something = g_value_get_boolean (handler_return);
200   if (did_something) {
201     g_value_set_boolean (return_accu, TRUE);
202   }
203
204   /* always continue emission */
205   return TRUE;
206 }
207
208 static void
209 gst_bin_init (GstBin * bin)
210 {
211   bin->numchildren = 0;
212   bin->children = NULL;
213   bin->children_cookie = 0;
214 }
215
216 /**
217  * gst_bin_new:
218  * @name: name of new bin
219  *
220  * Create a new bin with given name.
221  *
222  * Returns: new bin
223  */
224 GstElement *
225 gst_bin_new (const gchar * name)
226 {
227   return gst_element_factory_make ("bin", name);
228 }
229
230 static GstClock *
231 gst_bin_get_clock_func (GstElement * element)
232 {
233   if (GST_ELEMENT_SCHEDULER (element))
234     return gst_scheduler_get_clock (GST_ELEMENT_SCHEDULER (element));
235
236   return NULL;
237 }
238
239 static void
240 gst_bin_set_clock_func (GstElement * element, GstClock * clock)
241 {
242   if (GST_ELEMENT_SCHEDULER (element))
243     gst_scheduler_use_clock (GST_ELEMENT_SCHEDULER (element), clock);
244 }
245
246 /**
247  * gst_bin_get_clock:
248  * @bin: a #GstBin to get the clock of
249  *
250  * Gets the current clock of the (scheduler of the) bin.
251  *
252  * Returns: the #GstClock of the bin
253  */
254 GstClock *
255 gst_bin_get_clock (GstBin * bin)
256 {
257   g_return_val_if_fail (bin != NULL, NULL);
258   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
259
260   return gst_bin_get_clock_func (GST_ELEMENT (bin));
261 }
262
263 /**
264  * gst_bin_use_clock:
265  * @bin: the bin to set the clock for
266  * @clock: the clock to use.
267  *
268  * Force the bin to use the given clock. Use NULL to 
269  * force it to use no clock at all.
270  */
271 void
272 gst_bin_use_clock (GstBin * bin, GstClock * clock)
273 {
274   g_return_if_fail (GST_IS_BIN (bin));
275
276   gst_bin_set_clock_func (GST_ELEMENT (bin), clock);
277 }
278
279 /**
280  * gst_bin_auto_clock:
281  * @bin: the bin to autoclock
282  *
283  * Let the bin select a clock automatically.
284  */
285 void
286 gst_bin_auto_clock (GstBin * bin)
287 {
288   g_return_if_fail (GST_IS_BIN (bin));
289
290   if (GST_ELEMENT_SCHEDULER (bin))
291     gst_scheduler_auto_clock (GST_ELEMENT_SCHEDULER (bin));
292 }
293
294 #ifndef GST_DISABLE_INDEX
295 static void
296 gst_bin_set_index (GstElement * element, GstIndex * index)
297 {
298   GstBin *bin;
299   GList *children;
300
301   bin = GST_BIN (element);
302
303   GST_LOCK (bin);
304   for (children = bin->children; children; children = g_list_next (children)) {
305     GstElement *child = GST_ELEMENT (children->data);
306
307     gst_element_set_index (child, index);
308   }
309   GST_UNLOCK (bin);
310 }
311 #endif
312
313 static void
314 gst_bin_set_element_sched (GstElement * element, GstScheduler * sched)
315 {
316   GST_CAT_LOG (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p",
317       GST_ELEMENT_NAME (element), sched);
318
319   /* if it's actually a Bin */
320   if (GST_IS_BIN (element)) {
321     if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
322       GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element,
323           "child is already a manager, not resetting sched");
324       if (GST_ELEMENT_SCHEDULER (element))
325         gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHEDULER (element));
326       return;
327     }
328
329     GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element,
330         "setting child bin's scheduler to be the same as the parent's");
331     gst_scheduler_add_element (sched, element);
332
333     /* set the children's schedule */
334     g_list_foreach (GST_BIN (element)->children,
335         (GFunc) gst_bin_set_element_sched, sched);
336   } else {
337     /* otherwise, if it's just a regular old element */
338     GList *pads;
339
340     gst_scheduler_add_element (sched, element);
341
342     if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) {
343       /* set the sched pointer in all the pads */
344       pads = element->pads;
345       while (pads) {
346         GstPad *pad;
347
348         pad = GST_PAD (pads->data);
349         pads = g_list_next (pads);
350
351         /* we only operate on real pads */
352         if (!GST_IS_REAL_PAD (pad))
353           continue;
354
355         /* if the peer element exists and is a candidate */
356         if (GST_PAD_PEER (pad)) {
357           if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
358             GST_CAT_LOG (GST_CAT_SCHEDULING,
359                 "peer is in same scheduler, telling scheduler");
360
361             if (GST_PAD_IS_SRC (pad))
362               gst_scheduler_pad_link (sched, pad, GST_PAD_PEER (pad));
363             else
364               gst_scheduler_pad_link (sched, GST_PAD_PEER (pad), pad);
365           }
366         }
367       }
368     }
369   }
370 }
371
372 static void
373 gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched)
374 {
375   if (GST_ELEMENT_SCHEDULER (element) == NULL) {
376     GST_CAT_DEBUG (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler",
377         GST_ELEMENT_NAME (element));
378     return;
379   }
380
381   GST_CAT_DEBUG (GST_CAT_SCHEDULING,
382       "removing element \"%s\" from its sched %p", GST_ELEMENT_NAME (element),
383       GST_ELEMENT_SCHEDULER (element));
384
385   /* if it's actually a Bin */
386   if (GST_IS_BIN (element)) {
387
388     if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
389       GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element,
390           "child is already a manager, not unsetting sched");
391       if (sched) {
392         gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHEDULER (element));
393       }
394       return;
395     }
396     /* for each child, remove them from their schedule */
397     g_list_foreach (GST_BIN (element)->children,
398         (GFunc) gst_bin_unset_element_sched, sched);
399
400     gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element);
401   } else {
402     /* otherwise, if it's just a regular old element */
403     GList *pads;
404
405     if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) {
406       /* unset the sched pointer in all the pads */
407       pads = element->pads;
408       while (pads) {
409         GstPad *pad;
410
411         pad = GST_PAD (pads->data);
412         pads = g_list_next (pads);
413
414         /* we only operate on real pads */
415         if (!GST_IS_REAL_PAD (pad))
416           continue;
417
418         /* if the peer element exists and is a candidate */
419         if (GST_PAD_PEER (pad)) {
420           if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
421             GST_CAT_LOG (GST_CAT_SCHEDULING,
422                 "peer is in same scheduler, telling scheduler");
423
424             if (GST_PAD_IS_SRC (pad))
425               gst_scheduler_pad_unlink (sched, pad, GST_PAD_PEER (pad));
426             else
427               gst_scheduler_pad_unlink (sched, GST_PAD_PEER (pad), pad);
428           }
429         }
430       }
431     }
432
433     gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element);
434   }
435 }
436
437 /* add an element to this bin
438  *
439  * MT safe 
440  */
441 static gboolean
442 gst_bin_add_func (GstBin * bin, GstElement * element)
443 {
444   gchar *elem_name;
445
446   /* we obviously can't add ourself to ourself */
447   if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
448     goto adding_itself;
449
450   /* get the element name to make sure it is unique in this bin, FIXME, another
451    * thread can change the name after the unlock. */
452   GST_LOCK (element);
453   elem_name = g_strdup (GST_ELEMENT_NAME (element));
454   GST_UNLOCK (element);
455
456   GST_LOCK (bin);
457   /* then check to see if the element's name is already taken in the bin,
458    * we can safely take the lock here. This check is probably bogus because
459    * you can safely change the element name after adding it to the bin. */
460   if (G_UNLIKELY (gst_object_check_uniqueness (bin->children,
461               elem_name) == FALSE))
462     goto duplicate_name;
463
464   /* set the element's parent and add the element to the bin's list of children */
465   if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT (element),
466               GST_OBJECT (bin))))
467     goto had_parent;
468
469   bin->children = g_list_prepend (bin->children, element);
470   bin->numchildren++;
471   bin->children_cookie++;
472
473   gst_element_set_scheduler (element, GST_ELEMENT_SCHEDULER (bin));
474
475   GST_UNLOCK (bin);
476
477   GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"",
478       elem_name);
479   g_free (elem_name);
480
481   g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
482
483   return TRUE;
484
485   /* ERROR handling here */
486 adding_itself:
487   GST_LOCK (bin);
488   g_warning ("Cannot add bin %s to itself", GST_ELEMENT_NAME (bin));
489   GST_UNLOCK (bin);
490   return FALSE;
491
492 duplicate_name:
493   g_warning ("Name %s is not unique in bin %s, not adding",
494       elem_name, GST_ELEMENT_NAME (bin));
495   GST_UNLOCK (bin);
496   g_free (elem_name);
497   return FALSE;
498
499 had_parent:
500   g_warning ("Element %s already has parent", elem_name);
501   GST_UNLOCK (bin);
502   g_free (elem_name);
503   return FALSE;
504 }
505
506 /**
507  * gst_bin_add:
508  * @bin: #GstBin to add element to
509  * @element: #GstElement to add to bin
510  *
511  * Adds the given element to the bin.  Sets the element's parent, and thus
512  * takes ownership of the element. An element can only be added to one bin.
513  *
514  * Returns: TRUE if the element could be added, FALSE on wrong parameters or
515  * the bin does not want to accept the element.
516  *
517  * MT safe.
518  */
519 gboolean
520 gst_bin_add (GstBin * bin, GstElement * element)
521 {
522   GstBinClass *bclass;
523   gboolean result;
524
525   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
526   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
527
528   bclass = GST_BIN_GET_CLASS (bin);
529
530   if (G_UNLIKELY (bclass->add_element == NULL))
531     goto no_function;
532
533   GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element %s to bin %s",
534       GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
535
536   result = bclass->add_element (bin, element);
537
538   return result;
539
540 no_function:
541   g_warning ("adding elements to bin %s is not supported",
542       GST_ELEMENT_NAME (bin));
543   return FALSE;
544 }
545
546 /* remove an element from the bin
547  *
548  * MT safe
549  */
550 static gboolean
551 gst_bin_remove_func (GstBin * bin, GstElement * element)
552 {
553   gchar *elem_name;
554
555   /* grab element name so we can print it */
556   GST_LOCK (element);
557   elem_name = g_strdup (GST_ELEMENT_NAME (element));
558   GST_UNLOCK (element);
559
560   GST_LOCK (bin);
561   /* the element must be in the bin's list of children */
562   if (G_UNLIKELY (g_list_find (bin->children, element) == NULL))
563     goto not_in_bin;
564
565   /* now remove the element from the list of elements */
566   bin->children = g_list_remove (bin->children, element);
567   bin->numchildren--;
568   bin->children_cookie++;
569   GST_UNLOCK (bin);
570
571   GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"",
572       elem_name);
573   g_free (elem_name);
574
575   gst_element_set_scheduler (element, NULL);
576
577   /* we ref here because after the _unparent() the element can be disposed
578    * and we still need it to fire a signal. */
579   gst_object_ref (GST_OBJECT (element));
580   gst_object_unparent (GST_OBJECT (element));
581
582   g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element);
583   /* element is really out of our control now */
584   gst_object_unref (GST_OBJECT (element));
585
586   return TRUE;
587
588 not_in_bin:
589   g_warning ("Element %s is not in bin %s", elem_name, GST_ELEMENT_NAME (bin));
590   GST_UNLOCK (bin);
591   g_free (elem_name);
592   return FALSE;
593 }
594
595 /**
596  * gst_bin_remove:
597  * @bin: #GstBin to remove element from
598  * @element: #GstElement to remove
599  *
600  * Remove the element from its associated bin, unparenting it as well.
601  * Unparenting the element means that the element will be dereferenced,
602  * so if the bin holds the only reference to the element, the element
603  * will be freed in the process of removing it from the bin.  If you
604  * want the element to still exist after removing, you need to call
605  * #gst_object_ref before removing it from the bin.
606  *
607  * Returns: TRUE if the element could be removed, FALSE on wrong parameters or
608  * the bin does not want to remove the element.
609  *
610  * MT safe.
611  */
612 gboolean
613 gst_bin_remove (GstBin * bin, GstElement * element)
614 {
615   GstBinClass *bclass;
616   gboolean result;
617
618   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
619   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
620
621   bclass = GST_BIN_GET_CLASS (bin);
622
623   if (G_UNLIKELY (bclass->remove_element == NULL))
624     goto no_function;
625
626   GST_CAT_DEBUG (GST_CAT_PARENTAGE, "removing element %s from bin %s",
627       GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
628
629   result = bclass->remove_element (bin, element);
630
631   return result;
632
633 no_function:
634   g_warning ("removing elements from bin %s is not supported",
635       GST_ELEMENT_NAME (bin));
636   return FALSE;
637 }
638
639 static GstIteratorItem
640 iterate_child (GstIterator * it, GstElement * child)
641 {
642   gst_object_ref (GST_OBJECT (child));
643   return GST_ITERATOR_ITEM_PASS;
644 }
645
646 /**
647  * gst_bin_iterate_elements:
648  * @bin: #Gstbin to iterate the elements of
649  *
650  * Get an iterator for the elements in this bin. 
651  * Each element will have its refcount increased, so unref 
652  * after usage.
653  *
654  * Returns: a #GstIterator of #GstElements. gst_iterator_free after
655  * use. returns NULL when passing bad parameters.
656  *
657  * MT safe.
658  */
659 GstIterator *
660 gst_bin_iterate_elements (GstBin * bin)
661 {
662   GstIterator *result;
663
664   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
665
666   GST_LOCK (bin);
667   /* add ref because the iterator refs the bin. When the iterator
668    * is freed it will unref the bin again using the provided dispose 
669    * function. */
670   gst_object_ref (GST_OBJECT (bin));
671   result = gst_iterator_new_list (GST_GET_LOCK (bin),
672       &bin->children_cookie,
673       &bin->children,
674       bin,
675       (GstIteratorItemFunction) iterate_child,
676       (GstIteratorDisposeFunction) gst_object_unref);
677   GST_UNLOCK (bin);
678
679   return result;
680 }
681
682 static GstIteratorItem
683 iterate_child_recurse (GstIterator * it, GstElement * child)
684 {
685   gst_object_ref (GST_OBJECT (child));
686   if (GST_IS_BIN (child)) {
687     GstIterator *other = gst_bin_iterate_recurse (GST_BIN (child));
688
689     gst_iterator_push (it, other);
690   }
691   return GST_ITERATOR_ITEM_PASS;
692 }
693
694 /**
695  * gst_bin_iterate_recurse:
696  * @bin: #Gstbin to iterate the elements of
697  *
698  * Get an iterator for the elements in this bin. 
699  * Each element will have its refcount increased, so unref 
700  * after usage. This iterator recurses into GstBin children.
701  *
702  * Returns: a #GstIterator of #GstElements. gst_iterator_free after
703  * use. returns NULL when passing bad parameters.
704  *
705  * MT safe.
706  */
707 GstIterator *
708 gst_bin_iterate_recurse (GstBin * bin)
709 {
710   GstIterator *result;
711
712   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
713
714   GST_LOCK (bin);
715   /* add ref because the iterator refs the bin. When the iterator
716    * is freed it will unref the bin again using the provided dispose 
717    * function. */
718   gst_object_ref (GST_OBJECT (bin));
719   result = gst_iterator_new_list (GST_GET_LOCK (bin),
720       &bin->children_cookie,
721       &bin->children,
722       bin,
723       (GstIteratorItemFunction) iterate_child_recurse,
724       (GstIteratorDisposeFunction) gst_object_unref);
725   GST_UNLOCK (bin);
726
727   return result;
728   return NULL;
729 }
730
731
732 /**
733  * gst_bin_child_state_change:
734  * @bin: #GstBin with the child
735  * @oldstate: The old child state
736  * @newstate: The new child state
737  * @child: #GstElement that signaled an changed state
738  *
739  * An internal function to inform the parent bin about a state change
740  * of a child.
741  */
742 void
743 gst_bin_child_state_change (GstBin * bin, GstElementState oldstate,
744     GstElementState newstate, GstElement * child)
745 {
746   GstBinClass *bclass;
747
748   g_return_if_fail (GST_IS_BIN (bin));
749   g_return_if_fail (GST_IS_ELEMENT (child));
750
751   GST_CAT_LOG (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s",
752       GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin),
753       gst_element_state_get_name (oldstate),
754       gst_element_state_get_name (newstate));
755
756   bclass = GST_BIN_GET_CLASS (bin);
757
758   if (bclass->child_state_change) {
759     bclass->child_state_change (bin, oldstate, newstate, child);
760   } else {
761     g_warning ("cannot signal state change of child %s to bin %s\n",
762         GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin));
763   }
764 }
765
766 static void
767 gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate,
768     GstElementState newstate, GstElement * child)
769 {
770   GstElementState old = 0, new = 0;
771   gint old_idx = 0, new_idx = 0, i;
772
773   old = oldstate;
774   new = newstate;
775   while ((old >>= 1) != 0)
776     old_idx++;
777   while ((new >>= 1) != 0)
778     new_idx++;
779
780   GST_LOCK (bin);
781   GST_LOG_BIN_CONTENTS (bin, "before child state change");
782   bin->child_states[old_idx]--;
783   bin->child_states[new_idx]++;
784
785   for (i = GST_NUM_STATES - 1; i >= 0; i--) {
786     if (bin->child_states[i] != 0) {
787       gint state = (1 << i);
788
789       /* We only change state on the parent if the state is not locked.
790        * State locking can occur if the bin itself set state on children,
791        * which should not recurse since it leads to infinite loops. */
792       if (GST_STATE (bin) != state &&
793           !GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED)) {
794         GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
795             "highest child state is %s, changing bin state accordingly",
796             gst_element_state_get_name (state));
797         GST_STATE_PENDING (bin) = state;
798         GST_UNLOCK (bin);
799         gst_bin_change_state_norecurse (bin);
800         if (state != GST_STATE (bin)) {
801           g_warning ("%s: state change in callback %d %d",
802               GST_ELEMENT_NAME (bin), state, GST_STATE (bin));
803         }
804         GST_LOG_BIN_CONTENTS (bin, "after child state change");
805         return;
806       }
807       break;
808     }
809   }
810   GST_LOG_BIN_CONTENTS (bin, "after child state change");
811   GST_UNLOCK (bin);
812 }
813
814 typedef gboolean (*GstBinForeachFunc) (GstBin * bin, GstElement * element,
815     gpointer data);
816
817 /*
818  * gst_bin_foreach:
819  * @bin: bin to traverse
820  * @func: function to call on each child
821  * @data: user data handed to each function call
822  *
823  * Calls @func on each child of the bin. If @func returns FALSE, 
824  * gst_bin_foreach() immediately returns.
825  * It is assumed that calling @func may alter the set of @bin's children. @func
826  * will only be called on children that were in @bin when gst_bin_foreach() was
827  * called, and that are still in @bin when the child is reached.
828  *
829  * Returns: TRUE if @func always returned TRUE, FALSE otherwise
830  **/
831 static gboolean
832 gst_bin_foreach (GstBin * bin, GstBinForeachFunc func, gpointer data)
833 {
834   GList *kids, *walk;
835
836   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
837   g_return_val_if_fail (func != NULL, FALSE);
838
839   kids = g_list_copy (bin->children);
840
841   for (walk = kids; walk; walk = g_list_next (walk)) {
842     GstElement *element = (GstElement *) walk->data;
843
844     if (g_list_find (bin->children, element)) {
845       gboolean res = func (bin, element, data);
846
847       if (!res) {
848         g_list_free (kids);
849         return FALSE;
850       }
851     }
852   }
853
854   g_list_free (kids);
855   return TRUE;
856 }
857
858 typedef struct
859 {
860   GstElementState pending;
861   GstElementStateReturn result;
862 }
863 SetKidStateData;
864 static int
865 set_kid_state_func (GstBin * bin, GstElement * child, gpointer user_data)
866 {
867   GstElementState old_child_state;
868   SetKidStateData *data = user_data;
869
870   if (GST_FLAG_IS_SET (child, GST_ELEMENT_LOCKED_STATE)) {
871     return TRUE;
872   }
873
874   old_child_state = GST_STATE (child);
875
876   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
877       "changing state of child %s from current %s to pending %s",
878       GST_ELEMENT_NAME (child), gst_element_state_get_name (old_child_state),
879       gst_element_state_get_name (data->pending));
880
881   switch (gst_element_set_state (child, data->pending)) {
882     case GST_STATE_FAILURE:
883       GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
884           "child '%s' failed to go to state %d(%s)",
885           GST_ELEMENT_NAME (child),
886           data->pending, gst_element_state_get_name (data->pending));
887
888       gst_element_set_state (child, old_child_state);
889       return FALSE;             /* error out to the caller */
890
891     case GST_STATE_ASYNC:
892       GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
893           "child '%s' is changing state asynchronously",
894           GST_ELEMENT_NAME (child));
895       data->result = GST_STATE_ASYNC;
896       return TRUE;
897
898     case GST_STATE_SUCCESS:
899       GST_CAT_DEBUG (GST_CAT_STATES,
900           "child '%s' changed state to %d(%s) successfully",
901           GST_ELEMENT_NAME (child), data->pending,
902           gst_element_state_get_name (data->pending));
903       return TRUE;
904
905     default:
906       g_assert_not_reached ();
907       return FALSE;             /* satisfy gcc */
908   }
909 }
910
911 static GstElementStateReturn
912 gst_bin_change_state (GstElement * element)
913 {
914   GstBin *bin;
915   GstElementStateReturn ret;
916   GstElementState old_state, pending;
917
918   g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
919
920   bin = GST_BIN (element);
921
922   old_state = GST_STATE (element);
923   pending = GST_STATE_PENDING (element);
924
925   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
926       "changing state of children from %s to %s",
927       gst_element_state_get_name (old_state),
928       gst_element_state_get_name (pending));
929
930   if (pending == GST_STATE_VOID_PENDING)
931     return GST_STATE_SUCCESS;
932
933   /* If we're changing state non-recursively (see _norecurse()),
934    * this flag is already set and we should not set children states. */
935   if (!GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED)) {
936     SetKidStateData data;
937
938     /* So now we use this flag to make sure that kids don't re-set our
939      * state, which would lead to infinite loops. */
940     GST_FLAG_SET (bin, GST_BIN_STATE_LOCKED);
941     data.pending = pending;
942     data.result = GST_STATE_SUCCESS;
943     if (!gst_bin_foreach (bin, set_kid_state_func, &data)) {
944       GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED);
945       GST_STATE_PENDING (element) = old_state;
946       return GST_STATE_FAILURE;
947     }
948     GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED);
949
950     GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
951         "done changing bin's state from %s to %s, now in %s",
952         gst_element_state_get_name (old_state),
953         gst_element_state_get_name (pending),
954         gst_element_state_get_name (GST_STATE (element)));
955
956     /* if we're async, the kids will change state later (when the
957      * lock-state flag is no longer held) and all will be fine. */
958     if (data.result == GST_STATE_ASYNC)
959       return GST_STATE_ASYNC;
960   } else {
961     GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
962         "Not recursing state change onto children");
963   }
964
965   /* FIXME: this should have been done by the children already, no? */
966   if (parent_class->change_state) {
967     ret = parent_class->change_state (element);
968   } else {
969     ret = GST_STATE_SUCCESS;
970   }
971   return ret;
972 }
973
974 GstElementStateReturn
975 gst_bin_set_state (GstElement * element, GstElementState state)
976 {
977   GstBin *bin = GST_BIN (element);
978
979   if (GST_STATE (bin) == state) {
980     SetKidStateData data;
981
982     data.pending = state;
983     data.result = GST_STATE_SUCCESS;
984     if (!gst_bin_foreach (bin, set_kid_state_func, &data)) {
985       return GST_STATE_FAILURE;
986     } else {
987       return data.result;
988     }
989   } else {
990     return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, set_state, (element,
991             state), GST_STATE_FAILURE);
992   }
993 }
994
995 static GstElementStateReturn
996 gst_bin_change_state_norecurse (GstBin * bin)
997 {
998   GstElementStateReturn ret;
999
1000   if (GST_ELEMENT_GET_CLASS (bin)->change_state) {
1001     GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state");
1002
1003     /* Non-recursive state change flag */
1004     GST_FLAG_SET (bin, GST_BIN_STATE_LOCKED);
1005     ret = GST_ELEMENT_GET_CLASS (bin)->change_state (GST_ELEMENT (bin));
1006     GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED);
1007
1008     return ret;
1009   } else
1010     return GST_STATE_FAILURE;
1011 }
1012
1013 static void
1014 gst_bin_dispose (GObject * object)
1015 {
1016   GstBin *bin = GST_BIN (object);
1017
1018   GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");
1019
1020   /* ref to not hit 0 again */
1021   gst_object_ref (GST_OBJECT (object));
1022
1023   while (bin->children) {
1024     gst_bin_remove (bin, GST_ELEMENT (bin->children->data));
1025   }
1026   GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose no children");
1027   g_assert (bin->children == NULL);
1028   g_assert (bin->numchildren == 0);
1029
1030   G_OBJECT_CLASS (parent_class)->dispose (object);
1031 }
1032
1033 static gint
1034 compare_name (GstElement * element, const gchar * name)
1035 {
1036   gint eq;
1037
1038   GST_LOCK (element);
1039   eq = strcmp (GST_ELEMENT_NAME (element), name) == 0;
1040   GST_UNLOCK (element);
1041
1042   if (eq != 0) {
1043     gst_object_unref (GST_OBJECT (element));
1044   }
1045   return eq;
1046 }
1047
1048 /**
1049  * gst_bin_get_by_name:
1050  * @bin: #Gstbin to search
1051  * @name: the element name to search for
1052  *
1053  * Get the element with the given name from this bin. This
1054  * function recurses into subbins.
1055  *
1056  * Returns: the element with the given name. Returns NULL if the
1057  * element is not found or when bad parameters were given. Unref after
1058  * usage.
1059  *
1060  * MT safe.
1061  */
1062 GstElement *
1063 gst_bin_get_by_name (GstBin * bin, const gchar * name)
1064 {
1065   GstIterator *children;
1066   GstIterator *result;
1067
1068   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
1069
1070   children = gst_bin_iterate_recurse (bin);
1071   result = gst_iterator_find_custom (children,
1072       (GCompareFunc) compare_name, (gpointer) name);
1073
1074   return GST_ELEMENT_CAST (result);
1075 }
1076
1077 /**
1078  * gst_bin_get_by_name_recurse_up:
1079  * @bin: #Gstbin to search
1080  * @name: the element name to search for
1081  *
1082  * Get the element with the given name from this bin. If the
1083  * element is not found, a recursion is performed on the parent bin.
1084  *
1085  * Returns: the element with the given name or NULL when the element
1086  * was not found or bad parameters were given. Unref after usage.
1087  *
1088  * MT safe.
1089  */
1090 GstElement *
1091 gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name)
1092 {
1093   GstElement *result;
1094
1095   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
1096   g_return_val_if_fail (name != NULL, NULL);
1097
1098   result = gst_bin_get_by_name (bin, name);
1099
1100   if (!result) {
1101     GstObject *parent;
1102
1103     parent = gst_object_get_parent (GST_OBJECT_CAST (bin));
1104
1105     if (parent && GST_IS_BIN (parent)) {
1106       result = gst_bin_get_by_name_recurse_up (GST_BIN_CAST (parent), name);
1107     }
1108     gst_object_unref (parent);
1109   }
1110
1111   return result;
1112 }
1113
1114 static gint
1115 compare_interface (GstElement * element, gpointer interface)
1116 {
1117   gint ret;
1118
1119   if (G_TYPE_CHECK_INSTANCE_TYPE (element, GPOINTER_TO_INT (interface))) {
1120     ret = 0;
1121   } else {
1122     /* we did not find the element, need to release the ref
1123      * added by the iterator */
1124     gst_object_unref (GST_OBJECT (element));
1125     ret = 1;
1126   }
1127   return ret;
1128 }
1129
1130 /**
1131  * gst_bin_get_by_interface:
1132  * @bin: bin to find element in
1133  * @interface: interface to be implemented by interface
1134  *
1135  * Looks for the first element inside the bin that implements the given
1136  * interface. If such an element is found, it returns the element. You can
1137  * cast this element to the given interface afterwards.
1138  * If you want all elements that implement the interface, use
1139  * gst_bin_iterate_all_by_interface(). The function recurses bins inside bins.
1140  *
1141  * Returns: An element inside the bin implementing the interface. Unref after
1142  *          usage.
1143  *
1144  * MT safe.
1145  */
1146 GstElement *
1147 gst_bin_get_by_interface (GstBin * bin, GType interface)
1148 {
1149   GstIterator *children;
1150   GstIterator *result;
1151
1152   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
1153
1154   children = gst_bin_iterate_recurse (bin);
1155   result = gst_iterator_find_custom (children, (GCompareFunc) compare_interface,
1156       GINT_TO_POINTER (interface));
1157
1158   return GST_ELEMENT_CAST (result);
1159 }
1160
1161 /**
1162  * gst_bin_get_all_by_interface:
1163  * @bin: bin to find elements in
1164  * @interface: interface to be implemented by interface
1165  *
1166  * Looks for all elements inside the bin that implements the given
1167  * interface. You can safely cast all returned elements to the given interface.
1168  * The function recurses bins inside bins. The iterator will return a series
1169  * of #GstElement that should be unreffed after usage.
1170  *
1171  * Returns: An iterator for the  elements inside the bin implementing the interface.
1172  *
1173  */
1174 GstIterator *
1175 gst_bin_iterate_all_by_interface (GstBin * bin, GType interface)
1176 {
1177   GstIterator *children;
1178   GstIterator *result;
1179
1180   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
1181
1182   children = gst_bin_iterate_recurse (bin);
1183   result = gst_iterator_filter (children, (GCompareFunc) compare_interface,
1184       GINT_TO_POINTER (interface));
1185
1186   return result;
1187 }
1188
1189 /**
1190  * gst_bin_sync_children_state:
1191  * @bin: #Gstbin to sync state
1192  *
1193  * Tries to set the state of the children of this bin to the same state of the
1194  * bin by calling gst_element_set_state for each child not already having a
1195  * synchronized state. 
1196  *
1197  * Returns: The worst return value of any gst_element_set_state. So if one child
1198  *          returns #GST_STATE_FAILURE while all others return #GST_STATE_SUCCESS
1199  *          this function returns #GST_STATE_FAILURE.
1200  */
1201 GstElementStateReturn
1202 gst_bin_sync_children_state (GstBin * bin)
1203 {
1204   GList *children;
1205   GstElement *element;
1206   GstElementState state;
1207   GstElementStateReturn ret = GST_STATE_SUCCESS;
1208
1209   g_return_val_if_fail (GST_IS_BIN (bin), GST_STATE_FAILURE);
1210
1211   state = GST_STATE (bin);
1212   children = bin->children;
1213   GST_CAT_INFO (GST_CAT_STATES,
1214       "syncing state of children with bin \"%s\"'s state %s",
1215       GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
1216
1217   while (children) {
1218     element = GST_ELEMENT (children->data);
1219     children = children->next;
1220     if (GST_STATE (element) != state) {
1221       switch (gst_element_set_state (element, state)) {
1222         case GST_STATE_SUCCESS:
1223           break;
1224         case GST_STATE_ASYNC:
1225           if (ret == GST_STATE_SUCCESS)
1226             ret = GST_STATE_ASYNC;
1227           break;
1228         case GST_STATE_FAILURE:
1229           ret = GST_STATE_FAILURE;
1230           break;
1231         default:
1232           /* make sure gst_element_set_state never returns this */
1233           g_assert_not_reached ();
1234       }
1235     }
1236   }
1237
1238   return ret;
1239 }
1240
1241 #ifndef GST_DISABLE_LOADSAVE
1242 static xmlNodePtr
1243 gst_bin_save_thyself (GstObject * object, xmlNodePtr parent)
1244 {
1245   GstBin *bin = GST_BIN (object);
1246   xmlNodePtr childlist, elementnode;
1247   GList *children;
1248   GstElement *child;
1249
1250   if (GST_OBJECT_CLASS (parent_class)->save_thyself)
1251     GST_OBJECT_CLASS (parent_class)->save_thyself (GST_OBJECT (bin), parent);
1252
1253   childlist = xmlNewChild (parent, NULL, "children", NULL);
1254
1255   GST_CAT_INFO (GST_CAT_XML, "[%s]: saving %d children",
1256       GST_ELEMENT_NAME (bin), bin->numchildren);
1257
1258   children = bin->children;
1259   while (children) {
1260     child = GST_ELEMENT (children->data);
1261     elementnode = xmlNewChild (childlist, NULL, "element", NULL);
1262     gst_object_save_thyself (GST_OBJECT (child), elementnode);
1263     children = g_list_next (children);
1264   }
1265   return childlist;
1266 }
1267
1268 static void
1269 gst_bin_restore_thyself (GstObject * object, xmlNodePtr self)
1270 {
1271   GstBin *bin = GST_BIN (object);
1272   xmlNodePtr field = self->xmlChildrenNode;
1273   xmlNodePtr childlist;
1274
1275   while (field) {
1276     if (!strcmp (field->name, "children")) {
1277       GST_CAT_INFO (GST_CAT_XML, "[%s]: loading children",
1278           GST_ELEMENT_NAME (object));
1279       childlist = field->xmlChildrenNode;
1280       while (childlist) {
1281         if (!strcmp (childlist->name, "element")) {
1282           GstElement *element =
1283               gst_xml_make_element (childlist, GST_OBJECT (bin));
1284
1285           /* it had to be parented to find the pads, now we ref and unparent so
1286            * we can add it to the bin */
1287           gst_object_ref (GST_OBJECT (element));
1288           gst_object_unparent (GST_OBJECT (element));
1289
1290           gst_bin_add (bin, element);
1291         }
1292         childlist = childlist->next;
1293       }
1294     }
1295
1296     field = field->next;
1297   }
1298   if (GST_OBJECT_CLASS (parent_class)->restore_thyself)
1299     (GST_OBJECT_CLASS (parent_class)->restore_thyself) (object, self);
1300 }
1301 #endif /* GST_DISABLE_LOADSAVE */
1302
1303 static GStaticRecMutex iterate_lock = G_STATIC_REC_MUTEX_INIT;
1304
1305 static gboolean
1306 gst_bin_iterate_func (GstBin * bin)
1307 {
1308   GstScheduler *sched = GST_ELEMENT_SCHEDULER (bin);
1309
1310   g_static_rec_mutex_unlock (&iterate_lock);
1311
1312   /* only iterate if this is the manager bin */
1313   if (sched && sched->parent == GST_ELEMENT (bin)) {
1314     GstSchedulerState state;
1315
1316     state = gst_scheduler_iterate (sched);
1317
1318     if (state == GST_SCHEDULER_STATE_RUNNING) {
1319       goto done;
1320     } else if (state == GST_SCHEDULER_STATE_ERROR) {
1321       gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
1322     } else if (state == GST_SCHEDULER_STATE_STOPPED) {
1323       /* check if we have children scheds that are still running */
1324       /* FIXME: remove in 0.9? autouseless because iterations gone? */
1325       GList *walk;
1326
1327       for (walk = sched->schedulers; walk; walk = g_list_next (walk)) {
1328         GstScheduler *test = walk->data;
1329
1330         g_return_val_if_fail (test->parent, FALSE);
1331         if (GST_STATE (test->parent) == GST_STATE_PLAYING) {
1332           GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, bin,
1333               "current bin is not iterating, but children are, "
1334               "so returning TRUE anyway...");
1335           g_usleep (1);
1336           goto done;
1337         }
1338       }
1339     }
1340   } else {
1341     g_warning ("bin \"%s\" is not the managing bin, can't be iterated on!\n",
1342         GST_ELEMENT_NAME (bin));
1343   }
1344
1345   g_static_rec_mutex_lock (&iterate_lock);
1346
1347   return FALSE;
1348
1349 done:
1350   g_static_rec_mutex_lock (&iterate_lock);
1351   return TRUE;
1352 }
1353
1354 /**
1355  * gst_bin_iterate:
1356  * @bin: a#GstBin to iterate.
1357  *
1358  * Iterates over the elements in this bin.
1359  *
1360  * Returns: TRUE if the bin did something useful. This value
1361  *          can be used to determine it the bin is in EOS.
1362  */
1363 gboolean
1364 gst_bin_iterate (GstBin * bin)
1365 {
1366   gboolean running;
1367
1368   g_return_val_if_fail (bin != NULL, FALSE);
1369   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
1370
1371   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, bin, "starting iteration");
1372   gst_object_ref (GST_OBJECT (bin));
1373
1374   g_static_rec_mutex_lock (&iterate_lock);
1375   running = FALSE;
1376   g_signal_emit (G_OBJECT (bin), gst_bin_signals[ITERATE], 0, &running);
1377   g_static_rec_mutex_unlock (&iterate_lock);
1378
1379   gst_object_unref (GST_OBJECT (bin));
1380   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, bin, "finished iteration");
1381
1382   return running;
1383 }