gstbin.c 98.2 KB
Newer Older
1
/* GStreamer
2
 *
3
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
4
 *                    2004 Wim Taymans <wim@fluendo.com>
5
6
 *
 * gstbin.c: GstBin container object and support code
Erik Walthinsen's avatar
Erik Walthinsen committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
22
23
 *
 * MT safe.
Erik Walthinsen's avatar
Erik Walthinsen committed
24
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
25

26
27
/**
 * SECTION:gstbin
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
28
 * @short_description: Base class and element that can contain other elements
29
 *
Wim Taymans's avatar
Wim Taymans committed
30
 * #GstBin is an element that can contain other #GstElement, allowing them to be
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
31
 * managed as a group.
Wim Taymans's avatar
Wim Taymans committed
32
 * Pads from the child elements can be ghosted to the bin, see #GstGhostPad.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
33
34
 * This makes the bin look like any other elements and enables creation of
 * higher-level abstraction elements.
35
 *
Wim Taymans's avatar
Wim Taymans committed
36
 * A new #GstBin is created with gst_bin_new(). Use a #GstPipeline instead if you
Wim Taymans's avatar
Wim Taymans committed
37
38
 * want to create a toplevel bin because a normal bin doesn't have a bus or
 * handle clock distribution of its own.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
39
 *
40
41
42
43
44
45
46
 * After the bin has been created you will typically add elements to it with
 * gst_bin_add(). You can remove elements with gst_bin_remove().
 *
 * An element can be retrieved from a bin with gst_bin_get_by_name(), using the
 * elements name. gst_bin_get_by_name_recurse_up() is mainly used for internal
 * purposes and will query the parent bins when the element is not found in the
 * current bin.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
47
48
 *
 * An iterator of elements in a bin can be retrieved with
Wim Taymans's avatar
Wim Taymans committed
49
50
 * gst_bin_iterate_elements(). Various other iterators exist to retrieve the
 * elements in a bin.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
51
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52
 * gst_object_unref() is used to drop your reference to the bin.
53
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54
55
56
57
 * The <link linkend="GstBin-element-added">element-added</link> signal is
 * fired whenever a new element is added to the bin. Likewise the <link
 * linkend="GstBin-element-removed">element-removed</link> signal is fired
 * whenever an element is removed from the bin.
58
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59
60
 * <refsect2><title>Notes</title>
 * <para>
Wim Taymans's avatar
Wim Taymans committed
61
 * A #GstBin internally intercepts every #GstMessage posted by its children and
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
62
63
64
65
66
67
68
69
70
71
72
 * implements the following default behaviour for each of them:
 * <variablelist>
 *   <varlistentry>
 *     <term>GST_MESSAGE_EOS</term>
 *     <listitem><para>This message is only posted by sinks in the PLAYING
 *     state. If all sinks posted the EOS message, this bin will post and EOS
 *     message upwards.</para></listitem>
 *   </varlistentry>
 *   <varlistentry>
 *     <term>GST_MESSAGE_SEGMENT_START</term>
 *     <listitem><para>just collected and never forwarded upwards.
73
 *     The messages are used to decide when all elements have completed playback
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74
75
76
77
 *     of their segment.</para></listitem>
 *   </varlistentry>
 *   <varlistentry>
 *     <term>GST_MESSAGE_SEGMENT_DONE</term>
Wim Taymans's avatar
Wim Taymans committed
78
 *     <listitem><para> Is posted by #GstBin when all elements that posted
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
79
80
81
 *     a SEGMENT_START have posted a SEGMENT_DONE.</para></listitem>
 *   </varlistentry>
 *   <varlistentry>
Wim Taymans's avatar
Wim Taymans committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 *     <term>GST_MESSAGE_DURATION</term>
 *     <listitem><para> Is posted by an element that detected a change
 *     in the stream duration. The default bin behaviour is to clear any
 *     cached duration values so that the next duration query will perform
 *     a full duration recalculation. The duration change is posted to the
 *     application so that it can refetch the new duration with a duration
 *     query.
 *     </para></listitem>
 *   </varlistentry>
 *   <varlistentry>
 *     <term>GST_MESSAGE_CLOCK_LOST</term>
 *     <listitem><para> This message is posted by an element when it
 *     can no longer provide a clock. The default bin behaviour is to
 *     check if the lost clock was the one provided by the bin. If so and
 *     the bin is currently in the PLAYING state, the message is forwarded to
 *     the bin parent.
 *     This message is also generated when a clock provider is removed from
 *     the bin. If this message is received by the application, it should
 *     PAUSE the pipeline and set it back to PLAYING to force a new clock
 *     distribution.
 *     </para></listitem>
 *   </varlistentry>
 *   <varlistentry>
 *     <term>GST_MESSAGE_CLOCK_PROVIDE</term>
 *     <listitem><para> This message is generated when an element
107
108
 *     can provide a clock. This mostly happens when a new clock
 *     provider is added to the bin. The default behaviour of the bin is to
Wim Taymans's avatar
Wim Taymans committed
109
110
111
112
113
114
115
 *     mark the currently selected clock as dirty, which will perform a clock
 *     recalculation the next time the bin is asked to provide a clock.
 *     This message is never sent tot the application but is forwarded to
 *     the parent of the bin.
 *     </para></listitem>
 *   </varlistentry>
 *   <varlistentry>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
116
117
118
119
 *     <term>OTHERS</term>
 *     <listitem><para> posted upwards.</para></listitem>
 *   </varlistentry>
 * </variablelist>
120
 *
Wim Taymans's avatar
Wim Taymans committed
121
 *
Wim Taymans's avatar
Wim Taymans committed
122
 * A #GstBin implements the following default behaviour for answering to a
123
 * #GstQuery:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
124
125
126
 * <variablelist>
 *   <varlistentry>
 *     <term>GST_QUERY_DURATION</term>
Wim Taymans's avatar
Wim Taymans committed
127
128
 *     <listitem><para>If the query has been asked before with the same format
 *     and the bin is a toplevel bin (ie. has no parent),
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129
130
 *     use the cached previous value. If no previous value was cached, the
 *     query is sent to all sink elements in the bin and the MAXIMUM of all
131
 *     values is returned. If the bin is a toplevel bin the value is cached.
Wim Taymans's avatar
Wim Taymans committed
132
133
 *     If no sinks are available in the bin, the query fails.
 *     </para></listitem>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
135
 *   </varlistentry>
 *   <varlistentry>
136
137
138
139
140
141
142
 *     <term>GST_QUERY_POSITION</term>
 *     <listitem><para>The query is sent to all sink elements in the bin and the
 *     MAXIMUM of all values is returned. If no sinks are available in the bin,
 *     the query fails.
 *     </para></listitem>
 *   </varlistentry>
 *   <varlistentry>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143
144
145
146
147
148
 *     <term>OTHERS</term>
 *     <listitem><para>the query is forwarded to all sink elements, the result
 *     of the first sink that answers the query successfully is returned. If no
 *     sink is in the bin, the query fails.</para></listitem>
 *   </varlistentry>
 * </variablelist>
Wim Taymans's avatar
Wim Taymans committed
149
150
 *
 * A #GstBin will by default forward any event sent to it to all sink elements.
Stefan Kost's avatar
Stefan Kost committed
151
152
 * If all the sinks return TRUE, the bin will also return TRUE, else FALSE is
 * returned. If no sinks are in the bin, the event handler will return TRUE.
Wim Taymans's avatar
Wim Taymans committed
153
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
154
155
 * </para>
 * </refsect2>
156
 *
Stefan Kost's avatar
Stefan Kost committed
157
 * Last reviewed on 2006-04-28 (0.10.6)
158
 */
Erik Walthinsen's avatar
Erik Walthinsen committed
159

160
#include "gst_private.h"
161

162
#include "gstevent.h"
163
#include "gstbin.h"
164
#include "gstmarshal.h"
165
#include "gstxml.h"
166
#include "gstinfo.h"
167
#include "gsterror.h"
168

Wim Taymans's avatar
Wim Taymans committed
169
#include "gstindex.h"
170
#include "gstindexfactory.h"
171
#include "gstutils.h"
172
#include "gstchildproxy.h"
173

174
/* enable for DURATION caching.
175
 * FIXME currently too many elements don't update
176
177
 * their duration when it changes so we return inaccurate values. */
#undef DURATION_CACHING
178

179
/* latency is by default enabled now.
180
181
182
183
184
 * live-preroll and no-live-preroll in the environment var GST_COMPAT
 * to enables or disable it respectively.
 */
static gboolean enable_latency = TRUE;

185
186
187
GST_DEBUG_CATEGORY_STATIC (bin_debug);
#define GST_CAT_DEFAULT bin_debug

Stefan Kost's avatar
Stefan Kost committed
188
189
static const GstElementDetails gst_bin_details =
GST_ELEMENT_DETAILS ("Generic bin",
190
191
    "Generic/Bin",
    "Simple container object",
192
    "Erik Walthinsen <omega@cse.ogi.edu>," "Wim Taymans <wim@fluendo.com>");
Erik Walthinsen's avatar
Erik Walthinsen committed
193

194
195
196
197
/* a bin is toplevel if it has no parent or when it is configured to behave like
 * a toplevel bin */
#define BIN_IS_TOPLEVEL(bin) ((GST_OBJECT_PARENT (bin) == NULL) || bin->priv->asynchandling)

198
199
200
201
202
struct _GstBinPrivate
{
  gboolean asynchandling;
};

203
204
205
206
207
208
209
typedef struct
{
  GstBin *bin;
  guint32 cookie;
  GstState pending;
} BinContinueData;

210
static void gst_bin_dispose (GObject * object);
Erik Walthinsen's avatar
Erik Walthinsen committed
211

212
213
214
215
216
static void gst_bin_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_bin_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

217
static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
218
    GstStateChange transition);
219
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
220
    GstState * state, GstState * pending, GstClockTime timeout);
221
static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret);
222
static void bin_handle_async_start (GstBin * bin, gboolean new_base_time);
223
static void bin_push_state_continue (BinContinueData * data);
Erik Walthinsen's avatar
Erik Walthinsen committed
224

Wim Taymans's avatar
Wim Taymans committed
225
226
227
static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);

Wim Taymans's avatar
Wim Taymans committed
228
#ifndef GST_DISABLE_INDEX
Wim Taymans's avatar
Wim Taymans committed
229
static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
Wim Taymans's avatar
Wim Taymans committed
230
#endif
231
static GstClock *gst_bin_provide_clock_func (GstElement * element);
232
static gboolean gst_bin_set_clock_func (GstElement * element, GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
233

234
static void gst_bin_handle_message_func (GstBin * bin, GstMessage * message);
Wim Taymans's avatar
Wim Taymans committed
235
static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
236
237
static GstBusSyncReply bin_bus_handler (GstBus * bus,
    GstMessage * message, GstBin * bin);
238
static gboolean gst_bin_query (GstElement * element, GstQuery * query);
Erik Walthinsen's avatar
Erik Walthinsen committed
239

240
#ifndef GST_DISABLE_LOADSAVE
241
242
static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent);
static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self);
243
#endif
Erik Walthinsen's avatar
Erik Walthinsen committed
244

245
246
static void bin_remove_messages (GstBin * bin, GstObject * src,
    GstMessageType types);
247
static void gst_bin_continue_func (BinContinueData * data);
248
static gint bin_element_is_sink (GstElement * child, GstBin * bin);
249
static gint bin_element_is_src (GstElement * child, GstBin * bin);
250

251
252
static GstIterator *gst_bin_sort_iterator_new (GstBin * bin);

253
/* Bin signals and properties */
254
255
enum
{
Wim Taymans's avatar
Wim Taymans committed
256
257
  ELEMENT_ADDED,
  ELEMENT_REMOVED,
Erik Walthinsen's avatar
Erik Walthinsen committed
258
259
260
  LAST_SIGNAL
};

261
262
enum
{
263
264
  PROP_0,
  PROP_ASYNC_HANDLING
265
      /* FILL ME */
Erik Walthinsen's avatar
Erik Walthinsen committed
266
267
};

268
269
270
static void gst_bin_base_init (gpointer g_class);
static void gst_bin_class_init (GstBinClass * klass);
static void gst_bin_init (GstBin * bin);
271
static void gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data);
Erik Walthinsen's avatar
Erik Walthinsen committed
272
273
274
275

static GstElementClass *parent_class = NULL;
static guint gst_bin_signals[LAST_SIGNAL] = { 0 };

276
277
278
279
280
/**
 * gst_bin_get_type:
 *
 * Returns: the type of #GstBin
 */
281
GType
282
gst_bin_get_type (void)
283
{
284
  static GType gst_bin_type = 0;
285
  const gchar *compat;
286

287
  if (G_UNLIKELY (gst_bin_type == 0)) {
288
    static const GTypeInfo bin_info = {
289
      sizeof (GstBinClass),
290
      gst_bin_base_init,
291
      NULL,
292
      (GClassInitFunc) gst_bin_class_init,
293
294
      NULL,
      NULL,
295
      sizeof (GstBin),
296
      0,
297
      (GInstanceInitFunc) gst_bin_init,
298
      NULL
Erik Walthinsen's avatar
Erik Walthinsen committed
299
    };
300
301
302
303
304
    static const GInterfaceInfo child_proxy_info = {
      gst_bin_child_proxy_init,
      NULL,
      NULL
    };
305

306
    gst_bin_type =
307
        g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
308

309
    g_type_add_interface_static (gst_bin_type, GST_TYPE_CHILD_PROXY,
310
311
        &child_proxy_info);

312
313
    GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD,
        "debugging info for the 'bin' container element");
314
315
316
317
318
319
320
321
322

    /* compatibility stuff */
    compat = g_getenv ("GST_COMPAT");
    if (compat != NULL) {
      if (strstr (compat, "no-live-preroll"))
        enable_latency = FALSE;
      else if (strstr (compat, "live-preroll"))
        enable_latency = TRUE;
    }
Erik Walthinsen's avatar
Erik Walthinsen committed
323
  }
324
  return gst_bin_type;
Erik Walthinsen's avatar
Erik Walthinsen committed
325
326
}

327
328
329
330
static void
gst_bin_base_init (gpointer g_class)
{
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
331

332
333
334
  gst_element_class_set_details (gstelement_class, &gst_bin_details);
}

335
336
337
338
static GstObject *
gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
    guint index)
{
339
340
341
342
343
  GstObject *res;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

344
  GST_OBJECT_LOCK (bin);
345
346
  if ((res = g_list_nth_data (bin->children, index)))
    gst_object_ref (res);
347
  GST_OBJECT_UNLOCK (bin);
348
349

  return res;
350
351
352
353
354
}

guint
gst_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
{
355
356
357
358
359
  guint num;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

360
  GST_OBJECT_LOCK (bin);
361
  num = bin->numchildren;
362
  GST_OBJECT_UNLOCK (bin);
363
364

  return num;
365
366
367
368
369
370
371
372
373
374
375
}

static void
gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data)
{
  GstChildProxyInterface *iface = g_iface;

  iface->get_children_count = gst_bin_child_proxy_get_children_count;
  iface->get_child_by_index = gst_bin_child_proxy_get_child_by_index;
}

Erik Walthinsen's avatar
Erik Walthinsen committed
376
static void
377
gst_bin_class_init (GstBinClass * klass)
378
{
379
  GObjectClass *gobject_class;
380
  GstObjectClass *gstobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
381
  GstElementClass *gstelement_class;
382
  GError *err;
Erik Walthinsen's avatar
Erik Walthinsen committed
383

384
385
386
  gobject_class = (GObjectClass *) klass;
  gstobject_class = (GstObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Erik Walthinsen's avatar
Erik Walthinsen committed
387

388
  parent_class = g_type_class_peek_parent (klass);
Erik Walthinsen's avatar
Erik Walthinsen committed
389

390
391
392
393
394
395
396
397
  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_bin_set_property);
  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_bin_get_property);

  /**
   * GstBin:async-handling
   *
   * If set to #TRUE, the bin will handle asynchronous state changes.
   * This should be used only if the bin subclass is modifying the state
398
   * of its children on its own.
399
400
   *
   * Since: 0.10.13
401
   */
402
403
404
405
406
  g_object_class_install_property (gobject_class, PROP_ASYNC_HANDLING,
      g_param_spec_boolean ("async-handling", "Async Handling",
          "The bin will handle Asynchronous state changes",
          FALSE, G_PARAM_READWRITE));

407
408
  /**
   * GstBin::element-added:
409
410
   * @bin: the #GstBin
   * @element: the #GstElement that was added to the bin
411
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
412
   * Will be emitted after the element was added to the bin.
413
   */
Wim Taymans's avatar
Wim Taymans committed
414
  gst_bin_signals[ELEMENT_ADDED] =
415
416
417
      g_signal_new ("element-added", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_added), NULL,
      NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
418
419
  /**
   * GstBin::element-removed:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420
   * @bin: the #GstBin
421
   * @element: the #GstElement that was removed from the bin
422
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
423
   * Will be emitted after the element was removed from the bin.
424
   */
Wim Taymans's avatar
Wim Taymans committed
425
  gst_bin_signals[ELEMENT_REMOVED] =
426
427
428
      g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL,
      NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
429

430
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose);
431

432
#ifndef GST_DISABLE_LOADSAVE
433
434
435
  gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
  gstobject_class->restore_thyself =
      GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
436
#endif
437

438
439
440
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_bin_change_state_func);
  gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func);
Wim Taymans's avatar
Wim Taymans committed
441
#ifndef GST_DISABLE_INDEX
Wim Taymans's avatar
Wim Taymans committed
442
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
Wim Taymans's avatar
Wim Taymans committed
443
#endif
444
445
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_bin_provide_clock_func);
Wim Taymans's avatar
Wim Taymans committed
446
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
447

Wim Taymans's avatar
Wim Taymans committed
448
  gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
449
  gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query);
Wim Taymans's avatar
Wim Taymans committed
450

451
452
  klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
  klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
453
  klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
454
455
456
457

  GST_DEBUG ("creating bin thread pool");
  err = NULL;
  klass->pool =
458
      g_thread_pool_new ((GFunc) gst_bin_continue_func, NULL, -1, FALSE, &err);
459
460
461
  if (err != NULL) {
    g_critical ("could alloc threadpool %s", err->message);
  }
462
463
}

464
static void
465
gst_bin_init (GstBin * bin)
466
{
467
468
  GstBus *bus;

Erik Walthinsen's avatar
Erik Walthinsen committed
469
470
  bin->numchildren = 0;
  bin->children = NULL;
471
  bin->children_cookie = 0;
472
  bin->messages = NULL;
473
474
  bin->provided_clock = NULL;
  bin->clock_dirty = FALSE;
475

476
  /* Set up a bus for listening to child elements */
477
  bus = gst_bus_new ();
478
  bin->child_bus = bus;
479
480
  GST_DEBUG_OBJECT (bin, "using bus %" GST_PTR_FORMAT " to listen to children",
      bus);
481
  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin);
482

483
484
  bin->priv = g_new0 (GstBinPrivate, 1);
  bin->priv->asynchandling = FALSE;
Erik Walthinsen's avatar
Erik Walthinsen committed
485
486
}

487
488
489
490
static void
gst_bin_dispose (GObject * object)
{
  GstBin *bin = GST_BIN (object);
491
492
493
  GstBus **child_bus_p = &bin->child_bus;
  GstClock **provided_clock_p = &bin->provided_clock;
  GstElement **clock_provider_p = &bin->clock_provider;
494
495
496
497

  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");

  bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
498

499
500
501
  gst_object_replace ((GstObject **) child_bus_p, NULL);
  gst_object_replace ((GstObject **) provided_clock_p, NULL);
  gst_object_replace ((GstObject **) clock_provider_p, NULL);
502
503
504
505
506
507
508
509
510

  while (bin->children) {
    gst_bin_remove (bin, GST_ELEMENT_CAST (bin->children->data));
  }
  if (G_UNLIKELY (bin->children != NULL)) {
    g_critical ("could not remove elements from bin %s",
        GST_STR_NULL (GST_OBJECT_NAME (object)));
  }

511
512
513
  if (bin->priv) {
    g_free (bin->priv);
    bin->priv = NULL;
514
515
  }

516
517
518
  G_OBJECT_CLASS (parent_class)->dispose (object);
}

Erik Walthinsen's avatar
Erik Walthinsen committed
519
520
/**
 * gst_bin_new:
521
 * @name: the name of the new bin
Erik Walthinsen's avatar
Erik Walthinsen committed
522
 *
523
 * Creates a new bin with the given name.
Erik Walthinsen's avatar
Erik Walthinsen committed
524
 *
525
 * Returns: a new #GstBin
Erik Walthinsen's avatar
Erik Walthinsen committed
526
 */
527
528
GstElement *
gst_bin_new (const gchar * name)
529
{
530
  return gst_element_factory_make ("bin", name);
Erik Walthinsen's avatar
Erik Walthinsen committed
531
532
}

533
534
535
536
537
538
539
540
541
542
543
static void
gst_bin_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstBin *gstbin;

  gstbin = GST_BIN (object);

  switch (prop_id) {
    case PROP_ASYNC_HANDLING:
      GST_OBJECT_LOCK (gstbin);
544
      gstbin->priv->asynchandling = g_value_get_boolean (value);
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
      GST_OBJECT_UNLOCK (gstbin);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_bin_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstBin *gstbin;

  gstbin = GST_BIN (object);

  switch (prop_id) {
    case PROP_ASYNC_HANDLING:
      GST_OBJECT_LOCK (gstbin);
564
      g_value_set_boolean (value, gstbin->priv->asynchandling);
565
566
567
568
569
570
571
572
      GST_OBJECT_UNLOCK (gstbin);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Wim Taymans's avatar
Wim Taymans committed
573
574
/* set the index on all elements in this bin
 *
575
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
576
577
578
579
 */
#ifndef GST_DISABLE_INDEX
static void
gst_bin_set_index_func (GstElement * element, GstIndex * index)
Wim Taymans's avatar
Wim Taymans committed
580
{
Wim Taymans's avatar
Wim Taymans committed
581
582
  GstBin *bin;
  GList *children;
Wim Taymans's avatar
Wim Taymans committed
583

Wim Taymans's avatar
Wim Taymans committed
584
  bin = GST_BIN (element);
Wim Taymans's avatar
Wim Taymans committed
585

586
  GST_OBJECT_LOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
587
588
589
590
591
  for (children = bin->children; children; children = g_list_next (children)) {
    GstElement *child = GST_ELEMENT (children->data);

    gst_element_set_index (child, index);
  }
592
  GST_OBJECT_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
593
}
Wim Taymans's avatar
Wim Taymans committed
594
#endif
Wim Taymans's avatar
Wim Taymans committed
595

Wim Taymans's avatar
Wim Taymans committed
596
/* set the clock on all elements in this bin
Wim Taymans's avatar
Wim Taymans committed
597
 *
598
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
599
 */
600
static gboolean
Wim Taymans's avatar
Wim Taymans committed
601
gst_bin_set_clock_func (GstElement * element, GstClock * clock)
602
{
Wim Taymans's avatar
Wim Taymans committed
603
604
  GList *children;
  GstBin *bin;
605
  gboolean res = TRUE;
606

Wim Taymans's avatar
Wim Taymans committed
607
  bin = GST_BIN (element);
608

609
  GST_OBJECT_LOCK (bin);
610
611
612
  if (element->clock != clock) {
    for (children = bin->children; children; children = g_list_next (children)) {
      GstElement *child = GST_ELEMENT (children->data);
613

614
      res &= gst_element_set_clock (child, clock);
615
    }
Wim Taymans's avatar
Wim Taymans committed
616
  }
617
  GST_OBJECT_UNLOCK (bin);
618
619

  return res;
620
621
}

Wim Taymans's avatar
Wim Taymans committed
622
/* get the clock for this bin by asking all of the children in this bin
623
624
 *
 * The ref of the returned clock in increased so unref after usage.
Wim Taymans's avatar
Wim Taymans committed
625
 *
626
 * We loop the elements in state order and pick the last clock we can
627
628
 * get. This makes sure we get a clock from the source.
 *
629
 * MT safe
630
 */
Wim Taymans's avatar
Wim Taymans committed
631
static GstClock *
632
gst_bin_provide_clock_func (GstElement * element)
633
{
Wim Taymans's avatar
Wim Taymans committed
634
  GstClock *result = NULL;
635
  GstElement *provider = NULL;
636
  GstBin *bin;
637
638
  GstIterator *it;
  gpointer val;
639
640
  GstClock **provided_clock_p;
  GstElement **clock_provider_p;
641

642
643
  bin = GST_BIN (element);

644
  GST_OBJECT_LOCK (bin);
645
646
647
  if (!bin->clock_dirty)
    goto not_dirty;

648
  GST_DEBUG_OBJECT (bin, "finding new clock");
Wim Taymans's avatar
Wim Taymans committed
649

650
651
652
653
654
655
656
  it = gst_bin_sort_iterator_new (bin);

  while (it->next (it, &val) == GST_ITERATOR_OK) {
    GstElement *child = GST_ELEMENT_CAST (val);
    GstClock *clock;

    clock = gst_element_provide_clock (child);
657
658
659
660
    if (clock) {
      GST_DEBUG_OBJECT (bin, "found candidate clock %p by element %s",
          clock, GST_ELEMENT_NAME (child));
      if (result) {
661
        gst_object_unref (result);
662
663
        gst_object_unref (provider);
      }
664
      result = clock;
665
666
667
      provider = child;
    } else {
      gst_object_unref (child);
668
    }
669
  }
670
671
672
673
674

  provided_clock_p = &bin->provided_clock;
  clock_provider_p = &bin->clock_provider;
  gst_object_replace ((GstObject **) provided_clock_p, (GstObject *) result);
  gst_object_replace ((GstObject **) clock_provider_p, (GstObject *) provider);
675
  bin->clock_dirty = FALSE;
676
677
678
679
680
681
  GST_DEBUG_OBJECT (bin,
      "provided new clock %" GST_PTR_FORMAT " by provider %" GST_PTR_FORMAT,
      result, provider);
  /* Provider is not being returned to caller, just the result */
  if (provider)
    gst_object_unref (provider);
682
  GST_OBJECT_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
683

684
685
  gst_iterator_free (it);

Wim Taymans's avatar
Wim Taymans committed
686
  return result;
687
688
689
690
691
692

not_dirty:
  {
    if ((result = bin->provided_clock))
      gst_object_ref (result);
    GST_DEBUG_OBJECT (bin, "returning old clock %p", result);
693
    GST_OBJECT_UNLOCK (bin);
694
695
696

    return result;
  }
Wim Taymans's avatar
Wim Taymans committed
697
698
}

699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
/*
 * functions for manipulating cached messages
 */
typedef struct
{
  GstObject *src;
  GstMessageType types;
} MessageFind;

/* check if a message is of given src and type */
static gint
message_check (GstMessage * message, MessageFind * target)
{
  gboolean eq = TRUE;

  if (target->src)
    eq &= GST_MESSAGE_SRC (message) == target->src;
  if (target->types)
    eq &= (GST_MESSAGE_TYPE (message) & target->types) != 0;

  return (eq ? 0 : 1);
}

722
723
724
725
726
727
728
729
730
731
732
733
static GList *
find_message (GstBin * bin, GstObject * src, GstMessageType types)
{
  GList *result;
  MessageFind find;

  find.src = src;
  find.types = types;

  result = g_list_find_custom (bin->messages, &find,
      (GCompareFunc) message_check);

734
735
736
737
738
739
740
741
  if (result) {
    GST_DEBUG_OBJECT (bin, "we found a message %p from %s mathing types %08x",
        result->data, GST_OBJECT_NAME (GST_MESSAGE_CAST (result->data)->src),
        types);
  } else {
    GST_DEBUG_OBJECT (bin, "no message found matching types %08x", types);
  }

742
743
744
  return result;
}

745
/* with LOCK, returns TRUE if message had a valid SRC, takes ref on
746
 * the message.
747
748
749
750
 *
 * A message that is cached and has the same SRC and type is replaced
 * by the given message.
 */
751
752
753
754
755
756
static gboolean
bin_replace_message (GstBin * bin, GstMessage * message, GstMessageType types)
{
  GList *previous;
  GstObject *src;
  gboolean res = TRUE;
757
758
  const gchar *name;

759
  name = GST_MESSAGE_TYPE_NAME (message);
760
761
762

  if ((src = GST_MESSAGE_SRC (message))) {
    /* first find the previous message posted by this element */
763
    if ((previous = find_message (bin, src, types))) {
764
765
766
767
768
      /* if we found a previous message, replace it */
      gst_message_unref (previous->data);
      previous->data = message;

      GST_DEBUG_OBJECT (bin, "replace old message %s from %s",
769
          name, GST_ELEMENT_NAME (src));
770
771
772
773
    } else {
      /* keep new message */
      bin->messages = g_list_prepend (bin->messages, message);

774
775
      GST_DEBUG_OBJECT (bin, "got new message %p, %s from %s",
          message, name, GST_ELEMENT_NAME (src));
776
777
    }
  } else {
778
    GST_DEBUG_OBJECT (bin, "got message %s from (NULL), not processing", name);
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
    res = FALSE;
    gst_message_unref (message);
  }
  return res;
}

/* with LOCK. Remove all messages of given types */
static void
bin_remove_messages (GstBin * bin, GstObject * src, GstMessageType types)
{
  MessageFind find;
  GList *walk, *next;

  find.src = src;
  find.types = types;

  for (walk = bin->messages; walk; walk = next) {
    GstMessage *message = (GstMessage *) walk->data;

    next = g_list_next (walk);

    if (message_check (message, &find) == 0) {
      GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
802
          "deleting message %p of types 0x%08x", message, types);
803
804
805
806
      bin->messages = g_list_delete_link (bin->messages, walk);
      gst_message_unref (message);
    } else {
      GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
807
808
          "not deleting message %p of type 0x%08x", message,
          GST_MESSAGE_TYPE (message));
809
810
811
812
813
    }
  }
}


814
/* Check if the bin is EOS. We do this by scanning all sinks and
815
 * checking if they posted an EOS message.
816
817
 *
 * call with bin LOCK */
818
819
820
static gboolean
is_eos (GstBin * bin)
{
821
822
  gboolean result;
  GList *walk;
823

824
825
826
  result = TRUE;
  for (walk = bin->children; walk; walk = g_list_next (walk)) {
    GstElement *element;
827

828
829
    element = GST_ELEMENT_CAST (walk->data);
    if (bin_element_is_sink (element, bin) == 0) {
830
      /* check if element posted EOS */
831
      if (find_message (bin, GST_OBJECT_CAST (element), GST_MESSAGE_EOS)) {
Wim Taymans's avatar
Wim Taymans committed
832
        GST_DEBUG ("sink '%s' posted EOS", GST_ELEMENT_NAME (element));
833
      } else {
Wim Taymans's avatar
Wim Taymans committed
834
835
        GST_DEBUG ("sink '%s' did not post EOS yet",
            GST_ELEMENT_NAME (element));
836
        result = FALSE;
837
838
839
840
841
842
843
        break;
      }
    }
  }
  return result;
}

844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
static void
unlink_pads (GstPad * pad)
{
  GstPad *peer;

  if ((peer = gst_pad_get_peer (pad))) {
    if (gst_pad_get_direction (pad) == GST_PAD_SRC)
      gst_pad_unlink (pad, peer);
    else
      gst_pad_unlink (peer, pad);
    gst_object_unref (peer);
  }
  gst_object_unref (pad);
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
859
/* vmethod that adds an element to a bin
860
 *
861
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
862
 */
863
static gboolean
864
gst_bin_add_func (GstBin * bin, GstElement * element)
865
{
866
  gchar *elem_name;
867
  GstIterator *it;
868
  gboolean is_sink;
Wim Taymans's avatar
Wim Taymans committed
869
  GstMessage *clock_message = NULL, *async_message = NULL;
870
871
872
  GstStateChangeReturn ret;

  GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
873

874
875
876
  /* we obviously can't add ourself to ourself */
  if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
    goto adding_itself;
877

878
  /* get the element name to make sure it is unique in this bin. */
879
  GST_OBJECT_LOCK (element);
880
  elem_name = g_strdup (GST_ELEMENT_NAME (element));
881
  is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
882
  GST_OBJECT_UNLOCK (element);
883

884
  GST_OBJECT_LOCK (bin);
885

886
887
  /* then check to see if the element's name is already taken in the bin,
   * we can safely take the lock here. This check is probably bogus because
888
889
890
   * you can safely change the element name after this check and before setting
   * the object parent. The window is very small though... */
  if (G_UNLIKELY (!gst_object_check_uniqueness (bin->children, elem_name)))
891
    goto duplicate_name;
892

893
  /* set the element's parent and add the element to the bin's list of children */
Wim Taymans's avatar
Wim Taymans committed
894
895
  if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (element),
              GST_OBJECT_CAST (bin))))
896
    goto had_parent;
897

898
  /* if we add a sink we become a sink */
899
  if (is_sink) {
900
901
    GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was sink",
        elem_name);
902
    GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SINK);
903
  }
904
  if (gst_element_provides_clock (element)) {
905
    GST_DEBUG_OBJECT (bin, "element \"%s\" can provide a clock", elem_name);
906
    clock_message =
Wim Taymans's avatar
Wim Taymans committed
907
        gst_message_new_clock_provide (GST_OBJECT_CAST (element), NULL, TRUE);
908
  }
909

910
  bin->children = g_list_prepend (bin->children, element);
Erik Walthinsen's avatar
Erik Walthinsen committed
911
  bin->numchildren++;
912
  bin->children_cookie++;
Erik Walthinsen's avatar
Erik Walthinsen committed
913

914
915
916
917
918
919
  ret = GST_STATE_RETURN (bin);

  /* no need to update the state if we are in error */
  if (ret == GST_STATE_CHANGE_FAILURE)
    goto no_state_recalc;

Wim Taymans's avatar
Wim Taymans committed
920
921
922
923
924
925
926
927
928
929
  /* distribute the bus */
  gst_element_set_bus (element, bin->child_bus);

  /* propagate the current base_time and clock */
  gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time);
  /* it's possible that the element did not accept the clock but
   * that is not important right now. When the pipeline goes to PLAYING,
   * a new clock will be selected */
  gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));