c7c637affeb12ec990763064f66e076684d324ed
[platform/upstream/gstreamer.git] / gst / gstbin.c
1 /* GStreamer
2  * 
3  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
4  *                    2000 Wim Taymans <wtay@chello.be>
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
24 /* #define GST_DEBUG_ENABLED */
25 #include "gst_private.h"
26
27 #include "gstevent.h"
28 #include "gstbin.h"
29 #include "gstxml.h"
30 #include "gstlog.h"
31
32 #include "gstscheduler.h"
33 #include "gstindex.h"
34
35 GstElementDetails gst_bin_details = {
36   "Generic bin",
37   "Generic/Bin",
38   "LGPL",
39   "Simple container object",
40   VERSION,
41   "Erik Walthinsen <omega@cse.ogi.edu>",
42   "(C) 1999",
43 };
44
45 GType _gst_bin_type = 0;
46
47 static void                     gst_bin_dispose                 (GObject * object);
48
49 static GstElementStateReturn    gst_bin_change_state            (GstElement *element);
50 static GstElementStateReturn    gst_bin_change_state_norecurse  (GstBin *bin);
51
52 #ifndef GST_DISABLE_INDEX
53 static void                     gst_bin_set_index               (GstElement *element, GstIndex *index);
54 #endif
55
56 static void                     gst_bin_add_func                (GstBin *bin, GstElement *element);
57 static void                     gst_bin_remove_func             (GstBin *bin, GstElement *element);
58 static void                     gst_bin_child_state_change_func (GstBin *bin, GstElementState oldstate, 
59                                                                  GstElementState newstate, GstElement *child);
60
61 static GstClock*                gst_bin_get_clock_func          (GstElement *element);
62 static void                     gst_bin_set_clock_func          (GstElement *element, GstClock *clock);
63
64 static gboolean                 gst_bin_iterate_func            (GstBin *bin);
65
66 #ifndef GST_DISABLE_LOADSAVE
67 static xmlNodePtr               gst_bin_save_thyself            (GstObject * object, xmlNodePtr parent);
68 static void                     gst_bin_restore_thyself         (GstObject * object, xmlNodePtr self);
69 #endif
70
71 /* Bin signals and args */
72 enum
73 {
74   ELEMENT_ADDED,
75   ELEMENT_REMOVED,
76   LAST_SIGNAL
77 };
78
79 enum
80 {
81   ARG_0,
82   /* FILL ME */
83 };
84
85 static void                     gst_bin_class_init              (GstBinClass * klass);
86 static void                     gst_bin_init                    (GstBin * bin);
87
88 static GstElementClass *parent_class = NULL;
89 static guint gst_bin_signals[LAST_SIGNAL] = { 0 };
90
91 GType
92 gst_bin_get_type (void)
93 {
94   if (!_gst_bin_type) {
95     static const GTypeInfo bin_info = {
96       sizeof (GstBinClass),
97       NULL,
98       NULL,
99       (GClassInitFunc) gst_bin_class_init,
100       NULL,
101       NULL,
102       sizeof (GstBin),
103       8,
104       (GInstanceInitFunc) gst_bin_init,
105       NULL
106     };
107
108     _gst_bin_type = g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
109   }
110   return _gst_bin_type;
111 }
112
113 static void
114 gst_bin_class_init (GstBinClass * klass)
115 {
116   GObjectClass *gobject_class;
117   GstObjectClass *gstobject_class;
118   GstElementClass *gstelement_class;
119
120   gobject_class = (GObjectClass *) klass;
121   gstobject_class = (GstObjectClass *) klass;
122   gstelement_class = (GstElementClass *) klass;
123
124   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
125
126   gst_bin_signals[ELEMENT_ADDED] =
127     g_signal_new ("element_added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
128                   G_STRUCT_OFFSET (GstBinClass, element_added), NULL, NULL,
129                   gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
130   gst_bin_signals[ELEMENT_REMOVED] =
131     g_signal_new ("element_removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
132                   G_STRUCT_OFFSET (GstBinClass, element_removed), NULL, NULL,
133                   gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
134
135   gobject_class->dispose                = GST_DEBUG_FUNCPTR (gst_bin_dispose);
136
137 #ifndef GST_DISABLE_LOADSAVE
138   gstobject_class->save_thyself         = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
139   gstobject_class->restore_thyself      = GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
140 #endif
141
142   gstelement_class->change_state        = GST_DEBUG_FUNCPTR (gst_bin_change_state);
143 #ifndef GST_DISABLE_INDEX
144   gstelement_class->set_index           = GST_DEBUG_FUNCPTR (gst_bin_set_index);
145 #endif
146
147   klass->add_element                    = GST_DEBUG_FUNCPTR (gst_bin_add_func);
148   klass->remove_element                 = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
149   klass->child_state_change             = GST_DEBUG_FUNCPTR (gst_bin_child_state_change_func);
150   klass->iterate                        = GST_DEBUG_FUNCPTR (gst_bin_iterate_func);
151 }
152
153 static void
154 gst_bin_init (GstBin * bin)
155 {
156   /* in general, we prefer to use cothreads for most things */
157   GST_FLAG_SET (bin, GST_BIN_FLAG_PREFER_COTHREADS);
158
159   bin->numchildren = 0;
160   bin->children = NULL;
161   
162   bin->pre_iterate_func = NULL;
163   bin->post_iterate_func = NULL;
164   bin->pre_iterate_data = NULL;
165   bin->post_iterate_data = NULL;
166 }
167
168 /**
169  * gst_bin_new:
170  * @name: name of new bin
171  *
172  * Create a new bin with given name.
173  *
174  * Returns: new bin
175  */
176 GstElement *
177 gst_bin_new (const gchar * name)
178 {
179   return gst_element_factory_make ("bin", name);
180 }
181
182 static GstClock*
183 gst_bin_get_clock_func (GstElement *element)
184 {
185   if (GST_ELEMENT_SCHED (element)) 
186     return gst_scheduler_get_clock (GST_ELEMENT_SCHED (element));
187
188   return NULL;
189 }
190
191 static void
192 gst_bin_set_clock_func (GstElement *element, GstClock *clock)
193 {
194   if (GST_ELEMENT_SCHED (element)) 
195     gst_scheduler_use_clock (GST_ELEMENT_SCHED (element), clock);
196 }
197
198 /**
199  * gst_bin_get_clock:
200  * @bin: a #GstBin to get the clock of
201  *
202  * Gets the current clock of the (scheduler of the) bin.
203  *
204  * Returns: the #GstClock of the bin
205  */
206 GstClock*
207 gst_bin_get_clock (GstBin *bin)
208 {
209   g_return_val_if_fail (bin != NULL, NULL);
210   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
211
212   return gst_bin_get_clock_func (GST_ELEMENT (bin));
213 }
214
215 /**
216  * gst_bin_use_clock:
217  * @bin: the bin to set the clock for
218  * @clock: the clock to use.
219  *
220  * Force the bin to use the given clock. Use NULL to 
221  * force it to use no clock at all.
222  */
223 void
224 gst_bin_use_clock (GstBin *bin, GstClock *clock)
225 {
226   g_return_if_fail (GST_IS_BIN (bin));
227
228   gst_bin_set_clock_func (GST_ELEMENT (bin), clock);
229 }
230
231 /**
232  * gst_bin_auto_clock:
233  * @bin: the bin to autoclock
234  *
235  * Let the bin select a clock automatically.
236  */
237 void
238 gst_bin_auto_clock (GstBin *bin)
239 {
240   g_return_if_fail (GST_IS_BIN (bin));
241
242   if (GST_ELEMENT_SCHED (bin)) 
243     gst_scheduler_auto_clock (GST_ELEMENT_SCHED (bin));
244 }
245
246 #ifndef GST_DISABLE_INDEX
247 static void
248 gst_bin_set_index (GstElement *element, GstIndex *index)
249 {
250   GstBin *bin = GST_BIN (element);
251   
252   g_return_if_fail (GST_IS_BIN (bin));
253
254   g_list_foreach (bin->children, (GFunc) gst_element_set_index, index);
255 }
256 #endif
257
258 static void
259 gst_bin_set_element_sched (GstElement *element, GstScheduler *sched)
260 {
261   GST_INFO (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p", GST_ELEMENT_NAME (element),
262             sched);
263
264   /* if it's actually a Bin */
265   if (GST_IS_BIN (element)) {
266     if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
267       GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "child is already a manager, not resetting");
268       if (GST_ELEMENT_SCHED (element))
269         gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHED (element));
270       return;
271     }
272
273     GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting children's schedule to parent's");
274     gst_scheduler_add_element (sched, element);
275
276     /* set the children's schedule */
277     g_list_foreach (GST_BIN (element)->children, (GFunc) gst_bin_set_element_sched, sched);
278   }
279   /* otherwise, if it's just a regular old element */
280   else {
281     GList *pads;
282
283     gst_scheduler_add_element (sched, element);
284
285     /* set the sched pointer in all the pads */
286     pads = element->pads;
287     while (pads) {
288       GstPad *pad;
289
290       pad = GST_PAD (pads->data);
291       pads = g_list_next (pads);
292
293       /* we only operate on real pads */
294       if (!GST_IS_REAL_PAD (pad))
295         continue;
296
297       /* if the peer element exists and is a candidate */
298       if (GST_PAD_PEER (pad)) {
299         if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
300           GST_INFO (GST_CAT_SCHEDULING,
301                     "peer is in same scheduler, telling scheduler");
302
303           if (GST_PAD_IS_SRC (pad))
304             gst_scheduler_pad_link (sched, pad, GST_PAD_PEER (pad));
305           else
306             gst_scheduler_pad_link (sched, GST_PAD_PEER (pad), pad);
307         }
308       }
309     }
310   }
311 }
312
313
314 static void
315 gst_bin_unset_element_sched (GstElement *element, GstScheduler *sched)
316 {
317   if (GST_ELEMENT_SCHED (element) == NULL) {
318     GST_INFO (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler",
319               GST_ELEMENT_NAME (element));
320     return;
321   }
322
323   GST_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from its sched %p",
324             GST_ELEMENT_NAME (element), GST_ELEMENT_SCHED (element));
325
326   /* if it's actually a Bin */
327   if (GST_IS_BIN (element)) {
328
329     if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
330       GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element,
331                         "child is already a manager, not unsetting sched");
332       if (sched) {
333         gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHED (element));
334       }
335       return;
336     }
337     /* for each child, remove them from their schedule */
338     g_list_foreach (GST_BIN (element)->children, (GFunc) gst_bin_unset_element_sched, sched);
339
340     gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element);
341   }
342   /* otherwise, if it's just a regular old element */
343   else {
344     GList *pads;
345
346     /* set the sched pointer in all the pads */
347     pads = element->pads;
348     while (pads) {
349       GstPad *pad;
350
351       pad = GST_PAD (pads->data);
352       pads = g_list_next (pads);
353
354       /* we only operate on real pads */
355       if (!GST_IS_REAL_PAD (pad))
356         continue;
357
358       /* if the peer element exists and is a candidate */
359       if (GST_PAD_PEER (pad)) {
360         if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
361           GST_INFO (GST_CAT_SCHEDULING, "peer is in same scheduler, telling scheduler");
362
363           if (GST_PAD_IS_SRC (pad))
364             gst_scheduler_pad_unlink (sched, pad, GST_PAD_PEER (pad));
365           else
366             gst_scheduler_pad_unlink (sched, GST_PAD_PEER (pad), pad);
367         }
368       }
369     }
370     gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element);
371   }
372 }
373
374
375 /**
376  * gst_bin_add_many:
377  * @bin: the bin to add the elements to
378  * @element_1: the first element to add to the bin
379  * @...: NULL-terminated list of elements to add to the bin
380  * 
381  * Add a list of elements to a bin. Uses #gst_bin_add.
382  */
383 void
384 gst_bin_add_many (GstBin *bin, GstElement *element_1, ...)
385 {
386   va_list args;
387
388   g_return_if_fail (GST_IS_BIN (bin));
389   g_return_if_fail (GST_IS_ELEMENT (element_1));
390
391   va_start (args, element_1);
392
393   while (element_1) {
394     gst_bin_add (bin, element_1);
395     
396     element_1 = va_arg (args, GstElement*);
397   }
398
399   va_end (args);
400 }
401
402 static void
403 gst_bin_add_func (GstBin *bin, GstElement *element)
404 {
405   gint state_idx = 0;
406   GstElementState state;
407   GstScheduler *sched;
408
409   /* the element must not already have a parent */
410   g_return_if_fail (GST_ELEMENT_PARENT (element) == NULL);
411
412   /* must be not be in PLAYING state in order to modify bin */
413   g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING);
414
415   /* then check to see if the element's name is already taken in the bin */
416   if (gst_object_check_uniqueness (bin->children, 
417                                    GST_ELEMENT_NAME (element)) == FALSE)
418   {
419     g_warning ("Name %s is not unique in bin %s, not adding\n",
420                GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
421     return;
422   }
423
424   /* set the element's parent and add the element to the bin's list of children */
425   gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin));
426
427   bin->children = g_list_append (bin->children, element);
428   bin->numchildren++;
429
430   /* bump our internal state counter */
431   state = GST_STATE (element);
432   while (state >>= 1) state_idx++;
433   bin->child_states[state_idx]++;
434
435   /* now we have to deal with manager stuff 
436    * we can only do this if there's a scheduler: 
437    * if we're not a manager, and aren't attached to anything, we have no sched (yet) */
438   sched = GST_ELEMENT_SCHED (bin);
439   if (sched) {
440     gst_bin_set_element_sched (element, sched);
441   }
442
443   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child \"%s\"", GST_ELEMENT_NAME (element));
444
445   g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
446 }
447
448 /**
449  * gst_bin_add:
450  * @bin: #GstBin to add element to
451  * @element: #GstElement to add to bin
452  *
453  * Add the given element to the bin.  Set the elements parent, and thus
454  * add a reference.
455  */
456 void
457 gst_bin_add (GstBin *bin, GstElement *element)
458 {
459   GstBinClass *bclass;
460   
461   g_return_if_fail (GST_IS_BIN (bin));
462   g_return_if_fail (GST_IS_ELEMENT (element));
463
464   GST_DEBUG (GST_CAT_PARENTAGE, "adding element \"%s\" to bin \"%s\"",
465              GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
466
467   bclass = GST_BIN_GET_CLASS (bin);
468
469   if (bclass->add_element) {
470     bclass->add_element (bin, element);
471   }
472   else {
473     g_warning ("cannot add elements to bin %s\n", GST_ELEMENT_NAME (bin));
474   }
475 }
476
477 static void
478 gst_bin_remove_func (GstBin *bin, GstElement *element)
479 {
480   gint state_idx = 0;
481   GstElementState state;
482
483   /* the element must have its parent set to the current bin */
484   g_return_if_fail (GST_ELEMENT_PARENT (element) == (GstObject *) bin);
485
486   /* the element must be in the bin's list of children */
487   if (g_list_find (bin->children, element) == NULL) {
488     g_warning ("no element \"%s\" in bin \"%s\"\n", GST_ELEMENT_NAME (element),
489                GST_ELEMENT_NAME (bin));
490     return;
491   }
492
493   /* remove this element from the list of managed elements */
494   gst_bin_unset_element_sched (element, GST_ELEMENT_SCHED (bin));
495
496   /* now remove the element from the list of elements */
497   bin->children = g_list_remove (bin->children, element);
498   bin->numchildren--;
499
500   /* bump our internal state counter */
501   state = GST_STATE (element);
502   while (state >>= 1) state_idx++;
503   bin->child_states[state_idx]--;
504
505   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "removed child %s", GST_ELEMENT_NAME (element));
506
507   /* ref as we're going to emit a signal */
508   gst_object_ref (GST_OBJECT (element));
509   gst_object_unparent (GST_OBJECT (element));
510
511   /* if we're down to zero children, force state to NULL */
512   if (bin->numchildren == 0 && GST_ELEMENT_SCHED (bin) != NULL) {
513     GST_STATE_PENDING (bin) = GST_STATE_NULL;
514     gst_bin_change_state_norecurse (bin);
515   }
516   g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element);
517
518   /* element is really out of our control now */
519   gst_object_unref (GST_OBJECT (element));
520 }
521
522 /**
523  * gst_bin_remove:
524  * @bin: #GstBin to remove element from
525  * @element: #GstElement to remove
526  *
527  * Remove the element from its associated bin, unparenting as well.
528  * The element will also be unreferenced so there's no need to call
529  * gst_object_unref on it.
530  * If you want the element to still exist after removing, you need to call
531  * #gst_object_ref before removing it from the bin.
532  */
533 void
534 gst_bin_remove (GstBin *bin, GstElement *element)
535 {
536   GstBinClass *bclass;
537
538   GST_DEBUG_ELEMENT (GST_CAT_PARENTAGE, bin, "trying to remove child %s", GST_ELEMENT_NAME (element));
539
540   g_return_if_fail (GST_IS_BIN (bin));
541   g_return_if_fail (GST_IS_ELEMENT (element));
542   g_return_if_fail (bin->children != NULL);
543
544   /* must not be in PLAYING state in order to modify bin */
545   g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING);
546
547   bclass = GST_BIN_GET_CLASS (bin);
548
549   if (bclass->remove_element) {
550     bclass->remove_element (bin, element);
551   }
552   else {
553     g_warning ("cannot remove elements from bin %s\n", GST_ELEMENT_NAME (bin));
554   }
555 }
556
557 /**
558  * gst_bin_remove_many:
559  * @bin: the bin to remove the elements from
560  * @element_1: the first element to remove from the bin
561  * @...: NULL-terminated list of elements to remove from the bin
562  * 
563  * Remove a list of elements from a bin. Uses #gst_bin_remove.
564  */
565 void
566 gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...)
567 {
568   va_list args;
569
570   g_return_if_fail (GST_IS_BIN (bin));
571   g_return_if_fail (GST_IS_ELEMENT (element_1));
572
573   va_start (args, element_1);
574
575   while (element_1) {
576     gst_bin_remove (bin, element_1);
577     
578     element_1 = va_arg (args, GstElement*);
579   }
580
581   va_end (args);
582 }
583
584 /**
585  * gst_bin_child_state_change:
586  * @bin: #GstBin with the child
587  * @oldstate: The old child state
588  * @newstate: The new child state
589  * @child: #GstElement that signaled an changed state
590  *
591  * An internal function to inform the parent bin about a state change
592  * of a child.
593  */
594 void
595 gst_bin_child_state_change (GstBin *bin, GstElementState oldstate, 
596                             GstElementState newstate, GstElement *child)
597 {
598   GstBinClass *bclass;
599   
600   g_return_if_fail (GST_IS_BIN (bin));
601   g_return_if_fail (GST_IS_ELEMENT (child));
602
603   GST_INFO (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s",
604             GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin),
605             gst_element_state_get_name (oldstate), gst_element_state_get_name (newstate));
606
607   bclass = GST_BIN_GET_CLASS (bin);
608
609   if (bclass->child_state_change) {
610     bclass->child_state_change (bin, oldstate, newstate, child);
611   }
612   else {
613     g_warning ("cannot signal state change of child %s to bin %s\n", 
614                GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin));
615   }
616 }
617
618 static void
619 gst_bin_child_state_change_func (GstBin *bin, GstElementState oldstate, 
620                                  GstElementState newstate, GstElement *child)
621 {
622   gint old_idx = 0, new_idx = 0, i;
623
624   while (oldstate >>= 1) old_idx++;
625   while (newstate >>= 1) new_idx++;
626
627   GST_LOCK (bin);
628   bin->child_states[old_idx]--;
629   bin->child_states[new_idx]++;
630   
631   for (i = GST_NUM_STATES - 1; i >= 0; i--) {
632     if (bin->child_states[i] != 0) {
633       gint state = (1 << i);
634       if (GST_STATE (bin) != state) {
635         GST_INFO (GST_CAT_STATES, "bin %s need state change to %s",
636                   GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
637         GST_STATE_PENDING (bin) = state;
638         GST_UNLOCK (bin);
639         gst_bin_change_state_norecurse (bin);
640         if (state != GST_STATE (bin)) {
641           g_warning ("%s: state change in cllback %d %d", 
642                           GST_ELEMENT_NAME (bin),
643                           state, GST_STATE (bin));
644         }
645         return;
646       }
647       break;
648     }
649   }
650   GST_UNLOCK (bin);
651 }
652
653 static GstElementStateReturn
654 gst_bin_change_state (GstElement * element)
655 {
656   GstBin *bin;
657   GList *children;
658   GstElement *child;
659   GstElementStateReturn ret;
660   GstElementState old_state, pending;
661   gint transition;
662   gboolean have_async = FALSE;
663
664   g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
665
666   bin = GST_BIN (element);
667
668   old_state = GST_STATE (element);
669   pending = GST_STATE_PENDING (element);
670   transition = GST_STATE_TRANSITION (element);
671
672   GST_INFO_ELEMENT (GST_CAT_STATES, element, "changing childrens' state from %s to %s",
673                     gst_element_state_get_name (old_state), gst_element_state_get_name (pending));
674
675   if (pending == GST_STATE_VOID_PENDING)
676     return GST_STATE_SUCCESS;
677
678   children = bin->children;
679
680   while (children) {
681     GstElementState old_child_state;
682
683     child = GST_ELEMENT (children->data);
684     children = g_list_next (children);
685
686     if (GST_FLAG_IS_SET (child, GST_ELEMENT_LOCKED_STATE))
687       continue;
688
689     old_child_state = GST_STATE (child);
690
691     switch (gst_element_set_state (child, pending)) {
692       case GST_STATE_FAILURE:
693         GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
694         GST_DEBUG (GST_CAT_STATES, "child '%s' failed to go to state %d(%s)",
695                    GST_ELEMENT_NAME (child), pending, gst_element_state_get_name (pending));
696
697         gst_element_set_state (child, old_child_state);
698         if (GST_ELEMENT_SCHED (child) == GST_ELEMENT_SCHED (element)) {
699           /* try to reset it to what is was */
700           GST_STATE_PENDING (element) = old_state;
701           gst_bin_change_state (element);
702
703           return GST_STATE_FAILURE;
704         }
705         break;
706       case GST_STATE_ASYNC:
707         GST_DEBUG (GST_CAT_STATES, "child '%s' is changing state asynchronously",
708                    GST_ELEMENT_NAME (child));
709         have_async = TRUE;
710         break;
711       case GST_STATE_SUCCESS:
712         break;  
713     }
714   }
715
716   GST_INFO_ELEMENT (GST_CAT_STATES, element, "done changing bin's state from %s to %s, now in %s",
717                 gst_element_state_get_name (old_state),
718                 gst_element_state_get_name (pending),
719                 gst_element_state_get_name (GST_STATE (element)));
720
721   if (have_async)
722     ret = GST_STATE_ASYNC;
723   else {
724     if (parent_class->change_state) {
725       ret = parent_class->change_state(element);
726     }
727     else
728       ret = GST_STATE_SUCCESS;
729   }
730   return ret;
731 }
732
733
734 static GstElementStateReturn
735 gst_bin_change_state_norecurse (GstBin * bin)
736 {
737   GstElementStateReturn ret;
738
739   if (parent_class->change_state) {
740     GST_DEBUG_ELEMENT (GST_CAT_STATES, bin, "setting bin's own state");
741     ret = parent_class->change_state (GST_ELEMENT (bin));
742
743     return ret;
744   }
745   else
746     return GST_STATE_FAILURE;
747 }
748
749 static void
750 gst_bin_dispose (GObject * object)
751 {
752   GstBin *bin = GST_BIN (object);
753   GList *children, *orig;
754   GstElement *child;
755
756   GST_DEBUG (GST_CAT_REFCOUNTING, "dispose");
757
758   if (gst_element_get_state (GST_ELEMENT (object)) == GST_STATE_PLAYING)
759     gst_element_set_state (GST_ELEMENT (object), GST_STATE_PAUSED);
760
761   if (bin->children) {
762     orig = children = g_list_copy (bin->children);
763     while (children) {
764       child = GST_ELEMENT (children->data);
765       gst_bin_remove (bin, child);
766       children = g_list_next (children);
767     }
768     g_list_free (bin->children);
769     g_list_free (orig);
770   }
771   bin->children = NULL;
772   bin->numchildren = 0;
773
774   G_OBJECT_CLASS (parent_class)->dispose (object);
775 }
776
777 /**
778  * gst_bin_get_by_name:
779  * @bin: #Gstbin to search
780  * @name: the element name to search for
781  *
782  * Get the element with the given name from this bin.
783  *
784  * Returns: the element with the given name
785  */
786 GstElement *
787 gst_bin_get_by_name (GstBin * bin, const gchar * name)
788 {
789   GList *children;
790   GstElement *child;
791
792   g_return_val_if_fail (bin != NULL, NULL);
793   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
794   g_return_val_if_fail (name != NULL, NULL);
795
796   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "looking up child element %s", name);
797
798   children = bin->children;
799   while (children) {
800     child = GST_ELEMENT (children->data);
801     if (!strcmp (GST_OBJECT_NAME (child), name))
802       return child;
803     if (GST_IS_BIN (child)) {
804       GstElement *res = gst_bin_get_by_name (GST_BIN (child), name);
805
806       if (res)
807         return res;
808     }
809     children = g_list_next (children);
810   }
811
812   return NULL;
813 }
814
815 /**
816  * gst_bin_get_by_name_recurse_up:
817  * @bin: #Gstbin to search
818  * @name: the element name to search for
819  *
820  * Get the element with the given name from this bin. If the
821  * element is not found, a recursion is performed on the parent bin.
822  *
823  * Returns: the element with the given name
824  */
825 GstElement *
826 gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name)
827 {
828   GstElement *result = NULL;
829   GstObject *parent;
830
831   g_return_val_if_fail (bin != NULL, NULL);
832   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
833   g_return_val_if_fail (name != NULL, NULL);
834
835   result = gst_bin_get_by_name (bin, name);
836
837   if (!result) {
838     parent = gst_object_get_parent (GST_OBJECT (bin));
839
840     if (parent && GST_IS_BIN (parent)) {
841       result = gst_bin_get_by_name_recurse_up (GST_BIN (parent), name);
842     }
843   }
844
845   return result;
846 }
847
848 /**
849  * gst_bin_get_list:
850  * @bin: #Gstbin to get the list from
851  *
852  * Get the list of elements in this bin.
853  *
854  * Returns: a GList of elements
855  */
856 const GList *
857 gst_bin_get_list (GstBin * bin)
858 {
859   g_return_val_if_fail (GST_IS_BIN (bin), NULL);
860
861   return bin->children;
862 }
863
864 /**
865  * gst_bin_sync_children_state:
866  * @bin: #Gstbin to sync state
867  *
868  * Tries to set the state of the children of this bin to the same state of the
869  * bin by calling gst_element_set_state for each child not already having a
870  * synchronized state. 
871  *
872  * Returns: The worst return value of any gst_element_set_state. So if one child
873  *          returns #GST_STATE_FAILURE while all others return #GST_STATE_SUCCESS
874  *          this function returns #GST_STATE_FAILURE.
875  */
876 GstElementStateReturn
877 gst_bin_sync_children_state (GstBin *bin)
878 {
879   GList *children;
880   GstElement *element;
881   GstElementState state;
882   GstElementStateReturn ret = GST_STATE_SUCCESS;
883
884   g_return_val_if_fail (GST_IS_BIN (bin), GST_STATE_FAILURE);
885
886   state = GST_STATE (bin);
887   children = bin->children;
888   GST_INFO (GST_CAT_STATES, "syncing state of children with bin \"%s\"'s state %s",
889             GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
890
891   while (children) {
892     element = GST_ELEMENT (children->data);
893     children = children->next;
894     if (GST_STATE(element) != state) {
895       switch (gst_element_set_state (element, state)) {
896       case GST_STATE_SUCCESS:
897         break;
898       case GST_STATE_ASYNC:
899         if (ret == GST_STATE_SUCCESS)
900           ret = GST_STATE_ASYNC;
901         break;
902       case GST_STATE_FAILURE:
903         ret = GST_STATE_FAILURE;
904       default:
905         /* make sure gst_element_set_state never returns this */
906         g_assert_not_reached ();
907       }
908     }
909   }
910
911   return ret;
912 }
913 #ifndef GST_DISABLE_LOADSAVE
914 static xmlNodePtr
915 gst_bin_save_thyself (GstObject * object, xmlNodePtr parent)
916 {
917   GstBin *bin = GST_BIN (object);
918   xmlNodePtr childlist, elementnode;
919   GList *children;
920   GstElement *child;
921
922   if (GST_OBJECT_CLASS (parent_class)->save_thyself)
923     GST_OBJECT_CLASS (parent_class)->save_thyself (GST_OBJECT (bin), parent);
924
925   childlist = xmlNewChild (parent, NULL, "children", NULL);
926
927   GST_INFO_ELEMENT (GST_CAT_XML, bin, "saving %d children", bin->numchildren);
928
929   children = bin->children;
930   while (children) {
931     child = GST_ELEMENT (children->data);
932     elementnode = xmlNewChild (childlist, NULL, "element", NULL);
933     gst_object_save_thyself (GST_OBJECT (child), elementnode);
934     children = g_list_next (children);
935   }
936   return childlist;
937 }
938
939 static void
940 gst_bin_restore_thyself (GstObject * object, xmlNodePtr self)
941 {
942   GstBin *bin = GST_BIN (object);
943   xmlNodePtr field = self->xmlChildrenNode;
944   xmlNodePtr childlist;
945
946   while (field) {
947     if (!strcmp (field->name, "children")) {
948       GST_INFO_ELEMENT (GST_CAT_XML, GST_ELEMENT (object), "loading children");
949       childlist = field->xmlChildrenNode;
950       while (childlist) {
951         if (!strcmp (childlist->name, "element")) {
952           GstElement *element = gst_xml_make_element (childlist, GST_OBJECT (bin));
953           
954           /* it had to be parented to find the pads, now we ref and unparent so
955            * we can add it to the bin */
956           gst_object_ref (GST_OBJECT (element));
957           gst_object_unparent (GST_OBJECT (element));
958           
959           gst_bin_add (bin, element);
960         }
961         childlist = childlist->next;
962       }
963     }
964
965     field = field->next;
966   }
967 }
968 #endif /* GST_DISABLE_LOADSAVE */
969
970 static gboolean
971 gst_bin_iterate_func (GstBin * bin)
972 {
973   /* only iterate if this is the manager bin */
974   if (GST_ELEMENT_SCHED (bin) &&
975       GST_ELEMENT_SCHED (bin)->parent == GST_ELEMENT (bin)) {
976     GstSchedulerState state;
977
978     state = gst_scheduler_iterate (GST_ELEMENT_SCHED (bin));
979
980     if (state == GST_SCHEDULER_STATE_RUNNING) {
981       return TRUE;
982     }
983     else if (state == GST_SCHEDULER_STATE_ERROR) {
984       gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
985     }
986   }
987   else {
988     g_warning ("bin \"%s\" is not the managing bin, can't be iterated on!\n", 
989                GST_ELEMENT_NAME (bin));
990   }
991
992   return FALSE;
993 }
994
995 /**
996  * gst_bin_iterate:
997  * @bin: a#GstBin to iterate.
998  *
999  * Iterates over the elements in this bin.
1000  *
1001  * Returns: TRUE if the bin did something useful. This value
1002  *          can be used to determine it the bin is in EOS.
1003  */
1004 gboolean
1005 gst_bin_iterate (GstBin *bin)
1006 {
1007   GstBinClass *oclass;
1008   gboolean running = TRUE;
1009
1010   GST_DEBUG_ENTER ("(\"%s\")", GST_ELEMENT_NAME (bin));
1011
1012   g_return_val_if_fail (bin != NULL, FALSE);
1013   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
1014
1015   oclass = GST_BIN_GET_CLASS (bin);
1016
1017   gst_object_ref (GST_OBJECT (bin));
1018
1019   if (bin->pre_iterate_func)
1020     (bin->pre_iterate_func) (bin, bin->pre_iterate_data);
1021
1022   if (oclass->iterate)
1023     running = (oclass->iterate) (bin);
1024
1025   if (bin->post_iterate_func)
1026     (bin->post_iterate_func) (bin, bin->post_iterate_data);
1027
1028   GST_DEBUG_LEAVE ("(\"%s\") %d", GST_ELEMENT_NAME (bin), running);
1029
1030   if (!running) {
1031     if (GST_STATE (bin) == GST_STATE_PLAYING && 
1032         GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING) {
1033       GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, bin,
1034                          "polling for child shutdown after useless iteration");
1035       usleep (1);
1036       //gst_element_wait_state_change (GST_ELEMENT (bin));
1037       running = TRUE;
1038     }
1039   }
1040   gst_object_unref (GST_OBJECT (bin));
1041
1042   return running;
1043 }
1044
1045 /**
1046  * gst_bin_set_pre_iterate_function:
1047  * @bin: #Gstbin to attach to
1048  * @func: callback function to call
1049  * @user_data: user data to put in the function call
1050  *
1051  * Attaches a callback which will be run before every iteration of the bin
1052  *
1053  */
1054 void
1055 gst_bin_set_pre_iterate_function (GstBin *bin, GstBinPrePostIterateFunction func, gpointer user_data)
1056 {
1057   g_return_if_fail (GST_IS_BIN (bin));
1058
1059   if (!GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER))
1060     g_warning ("setting pre_iterate on a non MANAGER bin has no effect");
1061   
1062   bin->pre_iterate_func = func;
1063   bin->pre_iterate_data = user_data;
1064 }
1065
1066 /**
1067  * gst_bin_set_post_iterate_function:
1068  * @bin: #Gstbin to attach to
1069  * @func: callback function to call
1070  * @user_data: user data to put in the function call
1071  *
1072  * Attaches a callback which will be run after every iteration of the bin
1073  *
1074  */
1075 void
1076 gst_bin_set_post_iterate_function (GstBin *bin, GstBinPrePostIterateFunction func, gpointer user_data)
1077 {
1078   g_return_if_fail (GST_IS_BIN (bin));
1079
1080   if (!GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER))
1081     g_warning ("setting post_iterate on a non MANAGER bin has no effect");
1082
1083   bin->post_iterate_func = func;
1084   bin->post_iterate_data = user_data;
1085 }