source: trunk/third/gstreamer/gst/gstbin.c @ 21448

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