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