Some more work on cleanup.
[platform/upstream/gstreamer.git] / gst / gstbin.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstbin.c: GstBin container object and support code
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /* #define GST_DEBUG_ENABLED */
24 #include "gst_private.h"
25
26 #include "gstevent.h"
27 #include "gstbin.h"
28
29 #include "gstscheduler.h"
30
31 GstElementDetails gst_bin_details = {
32   "Generic bin",
33   "Bin",
34   "Simple container object",
35   VERSION,
36   "Erik Walthinsen <omega@cse.ogi.edu>",
37   "(C) 1999",
38 };
39
40 GType _gst_bin_type = 0;
41
42 static void                     gst_bin_dispose                 (GObject * object);
43
44 static GstElementStateReturn    gst_bin_change_state            (GstElement *element);
45 static GstElementStateReturn    gst_bin_change_state_norecurse  (GstBin *bin);
46 static gboolean                 gst_bin_change_state_type       (GstBin *bin,
47                                                                  GstElementState state,
48                                                                  GType type);
49 static void                     gst_bin_child_state_change      (GstBin *bin, GstElementState old, 
50                                                                  GstElementState new, GstElement *child);
51 static void                     gst_bin_send_event              (GstElement *element, GstEvent *event);
52
53 static gboolean                 gst_bin_iterate_func            (GstBin * bin);
54
55 #ifndef GST_DISABLE_LOADSAVE
56 static xmlNodePtr               gst_bin_save_thyself            (GstObject * object, xmlNodePtr parent);
57 static void                     gst_bin_restore_thyself         (GstObject * object, xmlNodePtr self);
58 #endif
59
60 /* Bin signals and args */
61 enum
62 {
63   OBJECT_ADDED,
64   LAST_SIGNAL
65 };
66
67 enum
68 {
69   ARG_0,
70   /* FILL ME */
71 };
72
73 static void                     gst_bin_class_init              (GstBinClass * klass);
74 static void                     gst_bin_init                    (GstBin * bin);
75
76 static GstElementClass *parent_class = NULL;
77 static guint gst_bin_signals[LAST_SIGNAL] = { 0 };
78
79 GType
80 gst_bin_get_type (void)
81 {
82   if (!_gst_bin_type) {
83     static const GTypeInfo bin_info = {
84       sizeof (GstBinClass),
85       NULL,
86       NULL,
87       (GClassInitFunc) gst_bin_class_init,
88       NULL,
89       NULL,
90       sizeof (GstBin),
91       8,
92       (GInstanceInitFunc) gst_bin_init,
93       NULL
94     };
95
96     _gst_bin_type = g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
97   }
98   return _gst_bin_type;
99 }
100
101 static void
102 gst_bin_class_init (GstBinClass * klass)
103 {
104   GObjectClass *gobject_class;
105   GstObjectClass *gstobject_class;
106   GstElementClass *gstelement_class;
107
108   gobject_class = (GObjectClass *) klass;
109   gstobject_class = (GstObjectClass *) klass;
110   gstelement_class = (GstElementClass *) klass;
111
112   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
113
114   gst_bin_signals[OBJECT_ADDED] =
115     g_signal_new ("object_added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
116                   G_STRUCT_OFFSET (GstBinClass, object_added), NULL, NULL,
117                   gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
118
119   klass->change_state_type =            GST_DEBUG_FUNCPTR (gst_bin_change_state_type);
120   klass->iterate =                      GST_DEBUG_FUNCPTR (gst_bin_iterate_func);
121
122   gobject_class->dispose                = GST_DEBUG_FUNCPTR (gst_bin_dispose);
123 #ifndef GST_DISABLE_LOADSAVE
124   gstobject_class->save_thyself         = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
125   gstobject_class->restore_thyself      = GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
126 #endif
127
128   gstelement_class->change_state        = GST_DEBUG_FUNCPTR (gst_bin_change_state);
129   gstelement_class->send_event          = GST_DEBUG_FUNCPTR (gst_bin_send_event);
130
131   klass->change_state_type              = GST_DEBUG_FUNCPTR (gst_bin_change_state_type);
132   klass->iterate                        = GST_DEBUG_FUNCPTR (gst_bin_iterate_func);
133 }
134
135 static void
136 gst_bin_init (GstBin * bin)
137 {
138   /* in general, we prefer to use cothreads for most things */
139   GST_FLAG_SET (bin, GST_BIN_FLAG_PREFER_COTHREADS);
140
141   bin->numchildren = 0;
142   bin->children = NULL;
143   bin->eoscond = g_cond_new ();
144 }
145
146 /**
147  * gst_bin_new:
148  * @name: name of new bin
149  *
150  * Create a new bin with given name.
151  *
152  * Returns: new bin
153  */
154 GstElement *
155 gst_bin_new (const gchar * name)
156 {
157   return gst_elementfactory_make ("bin", name);
158 }
159
160 static inline void
161 gst_bin_reset_element_sched (GstElement * element, GstScheduler * sched)
162 {
163   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "resetting element's scheduler");
164
165   gst_element_set_sched (element, sched);
166 }
167
168 static void
169 gst_bin_set_element_sched (GstElement * element, GstScheduler * sched)
170 {
171   GList *children;
172   GstElement *child;
173
174   g_return_if_fail (element != NULL);
175   g_return_if_fail (GST_IS_ELEMENT (element));
176   g_return_if_fail (sched != NULL);
177   g_return_if_fail (GST_IS_SCHEDULER (sched));
178
179   GST_INFO (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p", GST_ELEMENT_NAME (element),
180             sched);
181
182   /* if it's actually a Bin */
183   if (GST_IS_BIN (element)) {
184     if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
185       GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "child is already a manager, not resetting");
186       return;
187     }
188
189     GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting children's schedule to parent's");
190     gst_scheduler_add_element (sched, element);
191
192     /* set the children's schedule */
193     children = GST_BIN (element)->children;
194     while (children) {
195       child = GST_ELEMENT (children->data);
196       children = g_list_next (children);
197
198       gst_bin_set_element_sched (child, sched);
199     }
200
201   }
202   /* otherwise, if it's just a regular old element */
203   else {
204     gst_scheduler_add_element (sched, element);
205   }
206 }
207
208
209 static void
210 gst_bin_unset_element_sched (GstElement * element)
211 {
212   GList *children;
213   GstElement *child;
214
215   g_return_if_fail (element != NULL);
216   g_return_if_fail (GST_IS_ELEMENT (element));
217
218   if (GST_ELEMENT_SCHED (element) == NULL) {
219     GST_INFO (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler",
220               GST_ELEMENT_NAME (element));
221     return;
222   }
223   
224   GST_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from it sched %p",
225             GST_ELEMENT_NAME (element), GST_ELEMENT_SCHED (element));
226
227   /* if it's actually a Bin */
228   if (GST_IS_BIN (element)) {
229
230     if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
231       GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element,
232                         "child is already a manager, not unsetting sched");
233       return;
234     }
235
236     gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element);
237
238     /* for each child, remove them from their schedule */
239     children = GST_BIN (element)->children;
240     while (children) {
241       child = GST_ELEMENT (children->data);
242       children = g_list_next (children);
243
244       gst_bin_unset_element_sched (child);
245     }
246
247   }
248   /* otherwise, if it's just a regular old element */
249   else {
250     gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element);
251   }
252 }
253
254
255 /**
256  * gst_bin_add:
257  * @bin: #GstBin to add element to
258  * @element: #GstElement to add to bin
259  *
260  * Add the given element to the bin.  Set the elements parent, and thus
261  * add a reference.
262  */
263 void
264 gst_bin_add (GstBin * bin, GstElement * element)
265 {
266   gint state_idx = 0;
267   GstElementState state;
268
269   g_return_if_fail (bin != NULL);
270   g_return_if_fail (GST_IS_BIN (bin));
271   g_return_if_fail (element != NULL);
272   g_return_if_fail (GST_IS_ELEMENT (element));
273
274   GST_DEBUG (GST_CAT_PARENTAGE, "adding element \"%s\" to bin \"%s\"\n",
275              GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
276
277   /* must be not be in PLAYING state in order to modify bin */
278   g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING);
279
280   /* the element must not already have a parent */
281   g_return_if_fail (GST_ELEMENT_PARENT (element) == NULL);
282
283   /* then check to see if the element's name is already taken in the bin */
284   g_return_if_fail (gst_object_check_uniqueness (bin->children, GST_ELEMENT_NAME (element)) ==
285                     TRUE);
286
287   /* set the element's parent and add the element to the bin's list of children */
288   gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin));
289
290   bin->children = g_list_append (bin->children, element);
291   bin->numchildren++;
292
293   /* bump our internal state counter */
294   state = GST_STATE (element);
295   while (state >>= 1) state_idx++;
296   bin->child_states[state_idx]++;
297
298   /* now we have to deal with manager stuff 
299    * we can only do this if there's a scheduler: 
300    * if we're not a manager, and aren't attached to anything, we have no sched (yet) */
301   if (GST_IS_BIN (element) && GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
302     GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "child is a manager");
303   }
304   else if (GST_ELEMENT_SCHED (bin) != NULL) {
305     gst_bin_set_element_sched (element, GST_ELEMENT_SCHED (bin));
306   }
307
308   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child \"%s\"", GST_ELEMENT_NAME (element));
309
310   g_signal_emit (G_OBJECT (bin), gst_bin_signals[OBJECT_ADDED], 0, element);
311 }
312
313 /**
314  * gst_bin_remove:
315  * @bin: #GstBin to remove element from
316  * @element: #GstElement to remove
317  *
318  * Remove the element from its associated bin, unparenting as well.
319  */
320 void
321 gst_bin_remove (GstBin * bin, GstElement * element)
322 {
323   gint state_idx = 0;
324   GstElementState state;
325
326   g_return_if_fail (bin != NULL);
327   g_return_if_fail (GST_IS_BIN (bin));
328   g_return_if_fail (element != NULL);
329   g_return_if_fail (GST_IS_ELEMENT (element));
330   g_return_if_fail (bin->children != NULL);
331
332   /* must not be in PLAYING state in order to modify bin */
333   g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING);
334
335   /* the element must have its parent set to the current bin */
336   g_return_if_fail (GST_ELEMENT_PARENT (element) == (GstObject *) bin);
337
338   /* the element must be in the bin's list of children */
339   if (g_list_find (bin->children, element) == NULL) {
340     g_warning ("no element \"%s\" in bin \"%s\"\n", GST_ELEMENT_NAME (element),
341                GST_ELEMENT_NAME (bin));
342     return;
343   }
344
345   /* remove this element from the list of managed elements */
346   gst_bin_unset_element_sched (element);
347
348   /* now remove the element from the list of elements */
349   bin->children = g_list_remove (bin->children, element);
350   bin->numchildren--;
351
352   /* bump our internal state counter */
353   state = GST_STATE (element);
354   while (state >>= 1) state_idx++;
355   bin->child_states[state_idx]--;
356
357   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "removed child %s", GST_ELEMENT_NAME (element));
358
359   gst_object_unparent (GST_OBJECT (element));
360
361   /* if we're down to zero children, force state to NULL */
362   if (bin->numchildren == 0 && GST_ELEMENT_SCHED (bin) != NULL)
363     gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL);
364 }
365
366 static void
367 gst_bin_child_state_change (GstBin * bin, GstElementState old, GstElementState new,
368                             GstElement * child)
369 {
370   gint old_idx = 0, new_idx = 0, i;
371
372   GST_INFO (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s",
373             GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin),
374             gst_element_statename (old), gst_element_statename (new));
375
376   while (old >>= 1) old_idx++;
377   while (new >>= 1) new_idx++;
378
379   GST_LOCK (bin);
380   bin->child_states[old_idx]--;
381   bin->child_states[new_idx]++;
382
383   for (i = GST_NUM_STATES - 1; i >= 0; i--) {
384     if (bin->child_states[i] != 0) {
385       if (GST_STATE (bin) != (1 << i)) {
386         GST_INFO (GST_CAT_STATES, "bin %s need state change to %s",
387                   GST_ELEMENT_NAME (bin), gst_element_statename (1 << i));
388         GST_STATE_PENDING (bin) = (1 << i);
389         gst_bin_change_state_norecurse (bin);
390       }
391       break;
392     }
393   }
394   GST_UNLOCK (bin);
395 }
396
397 static void
398 gst_bin_send_event (GstElement *element, GstEvent *event)
399 {
400   GST_DEBUG (GST_CAT_EVENT, "event from %s in %s\n", 
401            gst_element_get_name (GST_ELEMENT (GST_EVENT_SRC (event))),
402            gst_element_get_name (element));
403
404   if (GST_ELEMENT (GST_EVENT_SRC (event)) == element) {
405     GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
406     return;
407   }
408
409   switch (GST_EVENT_TYPE (event)) {
410     case GST_EVENT_STATE_CHANGE:
411       gst_bin_child_state_change (GST_BIN (element), GST_EVENT_STATE_OLD (event),
412                                   GST_EVENT_STATE_NEW (event), GST_ELEMENT (GST_EVENT_SRC (event)));
413       gst_event_free (event);
414       break;
415     default:
416       GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
417       break;
418   }
419 }
420
421 static GstElementStateReturn
422 gst_bin_change_state (GstElement * element)
423 {
424   GstBin *bin;
425   GList *children;
426   GstElement *child;
427   GstElementStateReturn ret;
428   GstElementState old_state, pending;
429   gboolean have_async = FALSE;
430
431   g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
432
433   bin = GST_BIN (element);
434
435   old_state = GST_STATE (element);
436   pending = GST_STATE_PENDING (element);
437
438   GST_INFO_ELEMENT (GST_CAT_STATES, element, "changing childrens' state from %s to %s",
439                     gst_element_statename (old_state), gst_element_statename (pending));
440
441   children = bin->children;
442   while (children) {
443     child = GST_ELEMENT (children->data);
444     children = g_list_next (children);
445
446     switch (gst_element_set_state (child, pending)) {
447       case GST_STATE_FAILURE:
448         GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
449         GST_DEBUG (GST_CAT_STATES, "child '%s' failed to go to state %d(%s)\n",
450                    GST_ELEMENT_NAME (child), pending, gst_element_statename (pending));
451
452         gst_element_set_state (child, old_state);
453         if (GST_ELEMENT_SCHED (child) == GST_ELEMENT_SCHED (element)) {
454           return GST_STATE_FAILURE;
455         }
456         break;
457       case GST_STATE_ASYNC:
458         GST_DEBUG (GST_CAT_STATES, "child '%s' is changing state asynchronously\n",
459                    GST_ELEMENT_NAME (child));
460         have_async = TRUE;
461         break;
462     }
463   }
464
465   GST_INFO_ELEMENT (GST_CAT_STATES, element, "done changing bin's state from %s to %s, now in %s",
466                 gst_element_statename (old_state),
467                 gst_element_statename (pending),
468                 gst_element_statename (GST_STATE (element)));
469
470   if (have_async)
471     ret = GST_STATE_ASYNC;
472   else
473     ret = GST_STATE_SUCCESS;
474
475   return ret;
476 }
477
478
479 static GstElementStateReturn
480 gst_bin_change_state_norecurse (GstBin * bin)
481 {
482   if (GST_ELEMENT_CLASS (parent_class)->change_state) {
483     GST_DEBUG_ELEMENT (GST_CAT_STATES, bin, "setting bin's own state\n");
484     return GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT (bin));
485   }
486   else
487     return GST_STATE_FAILURE;
488 }
489
490 static gboolean
491 gst_bin_change_state_type (GstBin * bin, GstElementState state, GType type)
492 {
493   GList *children;
494   GstElement *child;
495
496   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
497   g_return_val_if_fail (bin->numchildren != 0, FALSE);
498
499   children = bin->children;
500   while (children) {
501     child = GST_ELEMENT (children->data);
502     if (GST_IS_BIN (child)) {
503       if (!gst_bin_set_state_type (GST_BIN (child), state, type))
504         return FALSE;
505     }
506     else if (G_TYPE_CHECK_INSTANCE_TYPE (child, type)) {
507       if (!gst_element_set_state (child, state))
508         return FALSE;
509     }
510     children = g_list_next (children);
511   }
512   if (type == GST_TYPE_BIN)
513     gst_element_set_state (GST_ELEMENT (bin), state);
514
515   return TRUE;
516 }
517
518 /**
519  * gst_bin_set_state_type:
520  * @bin: #GstBin to set the state
521  * @state: the new state to set the elements to
522  * @type: the type of elements to change
523  *
524  * Sets the state of only those objects of the given type.
525  *
526  * Returns: indication if the state change was successfull
527  */
528 gboolean
529 gst_bin_set_state_type (GstBin * bin, GstElementState state, GType type)
530 {
531   GstBinClass *oclass;
532
533   GST_DEBUG (GST_CAT_STATES, "gst_bin_set_state_type(\"%s\",%d,%d)\n",
534              GST_ELEMENT_NAME (bin), state, type);
535
536   g_return_val_if_fail (bin != NULL, FALSE);
537   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
538
539   oclass = GST_BIN_CLASS (G_OBJECT_GET_CLASS (bin));
540
541   if (oclass->change_state_type)
542     (oclass->change_state_type) (bin, state, type);
543   return TRUE;
544 }
545
546 static void
547 gst_bin_dispose (GObject * object)
548 {
549   GstBin *bin = GST_BIN (object);
550   GList *children, *orig;
551   GstElement *child;
552
553   GST_DEBUG (GST_CAT_REFCOUNTING, "dispose\n");
554
555   if (bin->children) {
556     orig = children = g_list_copy (bin->children);
557     while (children) {
558       child = GST_ELEMENT (children->data);
559       gst_bin_remove (bin, child);
560       children = g_list_next (children);
561     }
562     g_list_free (bin->children);
563     g_list_free (orig);
564   }
565   bin->children = NULL;
566   bin->numchildren = 0;
567
568   g_cond_free (bin->eoscond);
569
570   G_OBJECT_CLASS (parent_class)->dispose (object);
571 }
572
573 /**
574  * gst_bin_get_by_name:
575  * @bin: #Gstbin to search
576  * @name: the element name to search for
577  *
578  * Get the element with the given name from this bin.
579  *
580  * Returns: the element with the given name
581  */
582 GstElement *
583 gst_bin_get_by_name (GstBin * bin, const gchar * name)
584 {
585   GList *children;
586   GstElement *child;
587
588   g_return_val_if_fail (bin != NULL, NULL);
589   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
590   g_return_val_if_fail (name != NULL, NULL);
591
592   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "looking up child element %s", name);
593
594   children = bin->children;
595   while (children) {
596     child = GST_ELEMENT (children->data);
597     if (!strcmp (GST_OBJECT_NAME (child), name))
598       return child;
599     if (GST_IS_BIN (child)) {
600       GstElement *res = gst_bin_get_by_name (GST_BIN (child), name);
601
602       if (res)
603         return res;
604     }
605     children = g_list_next (children);
606   }
607
608   return NULL;
609 }
610
611 /**
612  * gst_bin_get_by_name_recurse_up:
613  * @bin: #Gstbin to search
614  * @name: the element name to search for
615  *
616  * Get the element with the given name from this bin. If the
617  * element is not found, a recursion is performed on the parent bin.
618  *
619  * Returns: the element with the given name
620  */
621 GstElement *
622 gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name)
623 {
624   GstElement *result = NULL;
625   GstObject *parent;
626
627   g_return_val_if_fail (bin != NULL, NULL);
628   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
629   g_return_val_if_fail (name != NULL, NULL);
630
631   result = gst_bin_get_by_name (bin, name);
632
633   if (!result) {
634     parent = gst_object_get_parent (GST_OBJECT (bin));
635
636     if (parent && GST_IS_BIN (parent)) {
637       result = gst_bin_get_by_name_recurse_up (GST_BIN (parent), name);
638     }
639   }
640
641   return result;
642 }
643
644 /**
645  * gst_bin_get_list:
646  * @bin: #Gstbin to get the list from
647  *
648  * Get the list of elements in this bin.
649  *
650  * Returns: a GList of elements
651  */
652 GList *
653 gst_bin_get_list (GstBin * bin)
654 {
655   g_return_val_if_fail (bin != NULL, NULL);
656   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
657
658   return bin->children;
659 }
660
661 #ifndef GST_DISABLE_LOADSAVE
662 static xmlNodePtr
663 gst_bin_save_thyself (GstObject * object, xmlNodePtr parent)
664 {
665   GstBin *bin = GST_BIN (object);
666   xmlNodePtr childlist, elementnode;
667   GList *children;
668   GstElement *child;
669
670   if (GST_OBJECT_CLASS (parent_class)->save_thyself)
671     GST_OBJECT_CLASS (parent_class)->save_thyself (GST_OBJECT (bin), parent);
672
673   childlist = xmlNewChild (parent, NULL, "children", NULL);
674
675   GST_INFO_ELEMENT (GST_CAT_XML, bin, "saving %d children", bin->numchildren);
676
677   children = bin->children;
678   while (children) {
679     child = GST_ELEMENT (children->data);
680     elementnode = xmlNewChild (childlist, NULL, "element", NULL);
681     gst_object_save_thyself (GST_OBJECT (child), elementnode);
682     children = g_list_next (children);
683   }
684   return childlist;
685 }
686
687 static void
688 gst_bin_restore_thyself (GstObject * object, xmlNodePtr self)
689 {
690   GstBin *bin = GST_BIN (object);
691   xmlNodePtr field = self->xmlChildrenNode;
692   xmlNodePtr childlist;
693
694   while (field) {
695     if (!strcmp (field->name, "children")) {
696       GST_INFO_ELEMENT (GST_CAT_XML, GST_ELEMENT (object), "loading children");
697       childlist = field->xmlChildrenNode;
698       while (childlist) {
699         if (!strcmp (childlist->name, "element")) {
700           GstElement *element = gst_element_restore_thyself (childlist, GST_OBJECT (bin));
701
702           gst_bin_add (bin, element);
703         }
704         childlist = childlist->next;
705       }
706     }
707
708     field = field->next;
709   }
710 }
711 #endif /* GST_DISABLE_LOADSAVE */
712
713 static gboolean
714 gst_bin_iterate_func (GstBin * bin)
715 {
716   /* only iterate if this is the manager bin */
717   if (GST_ELEMENT_SCHED (bin)->parent == GST_ELEMENT (bin)) {
718     return gst_scheduler_iterate (GST_ELEMENT_SCHED (bin));
719   }
720   else {
721     g_warning ("bin \"%d\" can't be iterated on!\n", GST_ELEMENT_NAME (bin));
722   }
723
724   return FALSE;
725 }
726
727 /**
728  * gst_bin_iterate:
729  * @bin: #Gstbin to iterate
730  *
731  * Iterates over the elements in this bin.
732  *
733  * Returns: TRUE if the bin did something usefull. This value
734  *          can be used to determine it the bin is in EOS.
735  */
736 gboolean
737 gst_bin_iterate (GstBin * bin)
738 {
739   GstBinClass *oclass;
740   gboolean running = TRUE;
741
742   GST_DEBUG_ENTER ("(\"%s\")", GST_ELEMENT_NAME (bin));
743
744   oclass = GST_BIN_CLASS (G_OBJECT_GET_CLASS (bin));
745
746   if (oclass->iterate)
747     running = (oclass->iterate) (bin);
748
749   GST_DEBUG_LEAVE ("(\"%s\") %d", GST_ELEMENT_NAME (bin), running);
750
751   if (!running) {
752     if (GST_STATE (bin) == GST_STATE_PLAYING && GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING) {
753       GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, bin,
754                          "waiting for child shutdown after useless iteration\n");
755       gst_element_wait_state_change (GST_ELEMENT (bin));
756       GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, bin, "child shutdown\n");
757     }
758   }
759
760   return running;
761 }