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