gstbin.c 107 KB
Newer Older
1
/* GStreamer
2
 *
3
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
4
 *                    2004 Wim Taymans <wim.taymans@gmail.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
 *
Stefan Kost's avatar
Stefan Kost committed
54
55
56
 * The #GstBin::element-added signal is fired whenever a new element is added to
 * the bin. Likewise the #GstBin::element-removed signal is fired whenever an
 * element is removed from the bin.
57
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58
59
 * <refsect2><title>Notes</title>
 * <para>
Wim Taymans's avatar
Wim Taymans committed
60
 * A #GstBin internally intercepts every #GstMessage posted by its children and
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61
62
63
64
65
66
67
68
69
70
71
 * 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.
72
 *     The messages are used to decide when all elements have completed playback
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
73
74
75
76
 *     of their segment.</para></listitem>
 *   </varlistentry>
 *   <varlistentry>
 *     <term>GST_MESSAGE_SEGMENT_DONE</term>
Wim Taymans's avatar
Wim Taymans committed
77
 *     <listitem><para> Is posted by #GstBin when all elements that posted
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
78
79
80
 *     a SEGMENT_START have posted a SEGMENT_DONE.</para></listitem>
 *   </varlistentry>
 *   <varlistentry>
Wim Taymans's avatar
Wim Taymans committed
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
 *     <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
106
107
 *     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
108
109
110
111
112
113
114
 *     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
115
116
117
118
 *     <term>OTHERS</term>
 *     <listitem><para> posted upwards.</para></listitem>
 *   </varlistentry>
 * </variablelist>
119
 *
Wim Taymans's avatar
Wim Taymans committed
120
 *
Wim Taymans's avatar
Wim Taymans committed
121
 * A #GstBin implements the following default behaviour for answering to a
122
 * #GstQuery:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
123
124
125
 * <variablelist>
 *   <varlistentry>
 *     <term>GST_QUERY_DURATION</term>
Wim Taymans's avatar
Wim Taymans committed
126
127
 *     <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
128
129
 *     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
130
 *     values is returned. If the bin is a toplevel bin the value is cached.
Wim Taymans's avatar
Wim Taymans committed
131
132
 *     If no sinks are available in the bin, the query fails.
 *     </para></listitem>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133
134
 *   </varlistentry>
 *   <varlistentry>
135
136
137
138
139
140
141
 *     <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
142
143
144
145
146
147
 *     <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
148
149
 *
 * A #GstBin will by default forward any event sent to it to all sink elements.
Stefan Kost's avatar
Stefan Kost committed
150
151
 * 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
152
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
153
154
 * </para>
 * </refsect2>
155
 *
Stefan Kost's avatar
Stefan Kost committed
156
 * Last reviewed on 2006-04-28 (0.10.6)
157
 */
Erik Walthinsen's avatar
Erik Walthinsen committed
158

159
#include "gst_private.h"
160

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

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

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

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

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

187
188
189
190
/* 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)

191
192
193
#define GST_BIN_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BIN, GstBinPrivate))

194
195
196
struct _GstBinPrivate
{
  gboolean asynchandling;
197
198
199
200
201
202
  /* if we get an ASYNC_DONE message from ourselves, this means that the
   * subclass will simulate ASYNC behaviour without having ASYNC children. When
   * such an ASYNC_DONE message is posted while we are doing a state change, we
   * have to process the message after finishing the state change even when no
   * child returned GST_STATE_CHANGE_ASYNC. */
  gboolean pending_async_done;
203
204

  guint32 structure_cookie;
205
206
};

207
208
209
210
211
212
213
typedef struct
{
  GstBin *bin;
  guint32 cookie;
  GstState pending;
} BinContinueData;

214
static void gst_bin_dispose (GObject * object);
Erik Walthinsen's avatar
Erik Walthinsen committed
215

216
217
218
219
220
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);

221
static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
222
    GstStateChange transition);
223
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
224
    GstState * state, GstState * pending, GstClockTime timeout);
225
static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
226
    gboolean flag_pending);
227
static void bin_handle_async_start (GstBin * bin, gboolean new_base_time);
228
static void bin_push_state_continue (BinContinueData * data);
Erik Walthinsen's avatar
Erik Walthinsen committed
229

Wim Taymans's avatar
Wim Taymans committed
230
231
232
233
static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);

static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
234
static GstClock *gst_bin_provide_clock_func (GstElement * element);
235
static gboolean gst_bin_set_clock_func (GstElement * element, GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
236

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

243
244
static gboolean gst_bin_do_latency_func (GstBin * bin);

245
#ifndef GST_DISABLE_LOADSAVE
246
247
static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent);
static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self);
248
#endif
Erik Walthinsen's avatar
Erik Walthinsen committed
249

250
251
static void bin_remove_messages (GstBin * bin, GstObject * src,
    GstMessageType types);
252
static void gst_bin_continue_func (BinContinueData * data);
253
static gint bin_element_is_sink (GstElement * child, GstBin * bin);
254
static gint bin_element_is_src (GstElement * child, GstBin * bin);
255

256
257
static GstIterator *gst_bin_sort_iterator_new (GstBin * bin);

258
/* Bin signals and properties */
259
260
enum
{
Wim Taymans's avatar
Wim Taymans committed
261
262
  ELEMENT_ADDED,
  ELEMENT_REMOVED,
263
  DO_LATENCY,
Erik Walthinsen's avatar
Erik Walthinsen committed
264
265
266
  LAST_SIGNAL
};

267
268
#define DEFAULT_ASYNC_HANDLING	FALSE

269
270
enum
{
271
272
  PROP_0,
  PROP_ASYNC_HANDLING
273
      /* FILL ME */
Erik Walthinsen's avatar
Erik Walthinsen committed
274
275
};

276
static void gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data);
Erik Walthinsen's avatar
Erik Walthinsen committed
277
278
279

static guint gst_bin_signals[LAST_SIGNAL] = { 0 };

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#define _do_init(type) \
{ \
  const gchar *compat; \
  static const GInterfaceInfo iface_info = { \
    gst_bin_child_proxy_init, \
    NULL, \
    NULL}; \
  \
  g_type_add_interface_static (type, GST_TYPE_CHILD_PROXY, &iface_info); \
  \
  GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD, \
      "debugging info for the 'bin' container element"); \
  \
  /* 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
301
302
}

303
304
GST_BOILERPLATE_FULL (GstBin, gst_bin, GstElement, GST_TYPE_ELEMENT, _do_init);

305
306
307
308
309
310
311
312
313
314
315
316
static void
gst_bin_base_init (gpointer g_class)
{
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);

  gst_element_class_set_details_simple (gstelement_class, "Generic bin",
      "Generic/Bin",
      "Simple container object",
      "Erik Walthinsen <omega@cse.ogi.edu>,"
      "Wim Taymans <wim.taymans@gmail.com>");
}

317
318
319
320
static GstObject *
gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
    guint index)
{
321
322
323
324
325
  GstObject *res;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

326
  GST_OBJECT_LOCK (bin);
327
328
  if ((res = g_list_nth_data (bin->children, index)))
    gst_object_ref (res);
329
  GST_OBJECT_UNLOCK (bin);
330
331

  return res;
332
333
334
335
336
}

guint
gst_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
{
337
338
339
340
341
  guint num;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

342
  GST_OBJECT_LOCK (bin);
343
  num = bin->numchildren;
344
  GST_OBJECT_UNLOCK (bin);
345
346

  return num;
347
348
349
350
351
352
353
354
355
356
357
}

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;
}

358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
static gboolean
_gst_boolean_accumulator (GSignalInvocationHint * ihint,
    GValue * return_accu, const GValue * handler_return, gpointer dummy)
{
  gboolean myboolean;

  myboolean = g_value_get_boolean (handler_return);
  if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
    g_value_set_boolean (return_accu, myboolean);

  GST_DEBUG ("invocation %d, %d", ihint->run_type, myboolean);

  /* stop emission */
  return FALSE;
}

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

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

386
387
  g_type_class_add_private (klass, sizeof (GstBinPrivate));

388
389
390
391
392
393
394
395
  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
396
   * of its children on its own.
397
398
   *
   * Since: 0.10.13
399
   */
400
401
402
  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",
403
          DEFAULT_ASYNC_HANDLING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
404

405
406
  /**
   * GstBin::element-added:
407
408
   * @bin: the #GstBin
   * @element: the #GstElement that was added to the bin
409
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
410
   * Will be emitted after the element was added to the bin.
411
   */
Wim Taymans's avatar
Wim Taymans committed
412
  gst_bin_signals[ELEMENT_ADDED] =
413
414
415
      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);
416
417
  /**
   * GstBin::element-removed:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
418
   * @bin: the #GstBin
419
   * @element: the #GstElement that was removed from the bin
420
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
421
   * Will be emitted after the element was removed from the bin.
422
   */
Wim Taymans's avatar
Wim Taymans committed
423
  gst_bin_signals[ELEMENT_REMOVED] =
424
425
426
      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);
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
  /**
   * GstBin::do-latency:
   * @bin: the #GstBin
   *
   * Will be emitted when the bin needs to perform latency calculations. This
   * signal is only emited for toplevel bins or when async-handling is
   * enabled.
   *
   * Only one signal handler is invoked. If no signals are connected, the
   * default handler is invoked, which will query and distribute the lowest
   * possible latency to all sinks.
   *
   * Connect to this signal if the default latency calculations are not
   * sufficient, like when you need different latencies for different sinks in
   * the same pipeline.
   *
   * Since: 0.10.22
   */
  gst_bin_signals[DO_LATENCY] =
      g_signal_new ("do-latency", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstBinClass, do_latency),
      _gst_boolean_accumulator, NULL, gst_marshal_BOOLEAN__VOID,
      G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
450

451
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose);
452

453
#ifndef GST_DISABLE_LOADSAVE
454
455
456
  gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
  gstobject_class->restore_thyself =
      GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
457
#endif
458

459
460
461
  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
462
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
463
464
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_bin_provide_clock_func);
Wim Taymans's avatar
Wim Taymans committed
465
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
466

Wim Taymans's avatar
Wim Taymans committed
467
  gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
468
  gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query);
Wim Taymans's avatar
Wim Taymans committed
469

470
471
  klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
  klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
472
  klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
473

474
475
  klass->do_latency = GST_DEBUG_FUNCPTR (gst_bin_do_latency_func);

476
477
478
  GST_DEBUG ("creating bin thread pool");
  err = NULL;
  klass->pool =
479
      g_thread_pool_new ((GFunc) gst_bin_continue_func, NULL, -1, FALSE, &err);
480
481
482
  if (err != NULL) {
    g_critical ("could alloc threadpool %s", err->message);
  }
483
484
}

485
static void
486
gst_bin_init (GstBin * bin, GstBinClass * klass)
487
{
488
489
  GstBus *bus;

Erik Walthinsen's avatar
Erik Walthinsen committed
490
491
  bin->numchildren = 0;
  bin->children = NULL;
492
  bin->children_cookie = 0;
493
  bin->messages = NULL;
494
495
  bin->provided_clock = NULL;
  bin->clock_dirty = FALSE;
496

497
  /* Set up a bus for listening to child elements */
498
  bus = gst_bus_new ();
499
  bin->child_bus = bus;
500
501
  GST_DEBUG_OBJECT (bin, "using bus %" GST_PTR_FORMAT " to listen to children",
      bus);
502
  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin);
503

504
  bin->priv = GST_BIN_GET_PRIVATE (bin);
505
  bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING;
506
  bin->priv->structure_cookie = 0;
Erik Walthinsen's avatar
Erik Walthinsen committed
507
508
}

509
510
511
512
static void
gst_bin_dispose (GObject * object)
{
  GstBin *bin = GST_BIN (object);
513
514
515
  GstBus **child_bus_p = &bin->child_bus;
  GstClock **provided_clock_p = &bin->provided_clock;
  GstElement **clock_provider_p = &bin->clock_provider;
516
517
518

  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");

519
  GST_OBJECT_LOCK (object);
520
521
522
  gst_object_replace ((GstObject **) child_bus_p, NULL);
  gst_object_replace ((GstObject **) provided_clock_p, NULL);
  gst_object_replace ((GstObject **) clock_provider_p, NULL);
523
524
  bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
  GST_OBJECT_UNLOCK (object);
525
526
527
528
529
530
531
532
533
534
535
536

  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)));
  }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

Erik Walthinsen's avatar
Erik Walthinsen committed
537
538
/**
 * gst_bin_new:
539
 * @name: the name of the new bin
Erik Walthinsen's avatar
Erik Walthinsen committed
540
 *
541
 * Creates a new bin with the given name.
Erik Walthinsen's avatar
Erik Walthinsen committed
542
 *
543
 * Returns: a new #GstBin
Erik Walthinsen's avatar
Erik Walthinsen committed
544
 */
545
546
GstElement *
gst_bin_new (const gchar * name)
547
{
548
  return gst_element_factory_make ("bin", name);
Erik Walthinsen's avatar
Erik Walthinsen committed
549
550
}

551
552
553
554
555
556
557
558
559
560
561
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);
562
      gstbin->priv->asynchandling = g_value_get_boolean (value);
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
      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);
582
      g_value_set_boolean (value, gstbin->priv->asynchandling);
583
584
585
586
587
588
589
590
      GST_OBJECT_UNLOCK (gstbin);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Wim Taymans's avatar
Wim Taymans committed
591
592
/* set the index on all elements in this bin
 *
593
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
594
595
596
 */
static void
gst_bin_set_index_func (GstElement * element, GstIndex * index)
Wim Taymans's avatar
Wim Taymans committed
597
{
Wim Taymans's avatar
Wim Taymans committed
598
  GstBin *bin;
599
600
  gboolean done;
  GstIterator *it;
Wim Taymans's avatar
Wim Taymans committed
601

Wim Taymans's avatar
Wim Taymans committed
602
  bin = GST_BIN (element);
Wim Taymans's avatar
Wim Taymans committed
603

604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
  it = gst_bin_iterate_elements (bin);

  done = FALSE;
  while (!done) {
    gpointer data;

    switch (gst_iterator_next (it, &data)) {
      case GST_ITERATOR_OK:
      {
        GstElement *child = GST_ELEMENT_CAST (data);

        GST_DEBUG_OBJECT (bin, "setting index on %s", GST_ELEMENT_NAME (child));
        gst_element_set_index (child, index);

        gst_object_unref (child);
        break;
      }
      case GST_ITERATOR_RESYNC:
        GST_DEBUG_OBJECT (bin, "iterator doing resync");
        gst_iterator_resync (it);
        break;
      default:
      case GST_ITERATOR_DONE:
        GST_DEBUG_OBJECT (bin, "iterator done");
        done = TRUE;
        break;
    }
Wim Taymans's avatar
Wim Taymans committed
631
  }
632
  gst_iterator_free (it);
Wim Taymans's avatar
Wim Taymans committed
633
634
}

Wim Taymans's avatar
Wim Taymans committed
635
/* set the clock on all elements in this bin
Wim Taymans's avatar
Wim Taymans committed
636
 *
637
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
638
 */
639
static gboolean
Wim Taymans's avatar
Wim Taymans committed
640
gst_bin_set_clock_func (GstElement * element, GstClock * clock)
641
{
Wim Taymans's avatar
Wim Taymans committed
642
  GstBin *bin;
643
644
  gboolean done;
  GstIterator *it;
645
  gboolean res = TRUE;
646

Wim Taymans's avatar
Wim Taymans committed
647
  bin = GST_BIN (element);
648

649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
  it = gst_bin_iterate_elements (bin);

  done = FALSE;
  while (!done) {
    gpointer data;

    switch (gst_iterator_next (it, &data)) {
      case GST_ITERATOR_OK:
      {
        GstElement *child = GST_ELEMENT_CAST (data);

        res &= gst_element_set_clock (child, clock);

        gst_object_unref (child);
        break;
      }
      case GST_ITERATOR_RESYNC:
        GST_DEBUG_OBJECT (bin, "iterator doing resync");
        gst_iterator_resync (it);
        res = TRUE;
        break;
      default:
      case GST_ITERATOR_DONE:
        GST_DEBUG_OBJECT (bin, "iterator done");
        done = TRUE;
        break;
675
    }
Wim Taymans's avatar
Wim Taymans committed
676
  }
677
  gst_iterator_free (it);
678
679

  return res;
680
681
}

Wim Taymans's avatar
Wim Taymans committed
682
/* get the clock for this bin by asking all of the children in this bin
683
684
 *
 * The ref of the returned clock in increased so unref after usage.
Wim Taymans's avatar
Wim Taymans committed
685
 *
686
 * We loop the elements in state order and pick the last clock we can
687
688
 * get. This makes sure we get a clock from the source.
 *
689
 * MT safe
690
 */
Wim Taymans's avatar
Wim Taymans committed
691
static GstClock *
692
gst_bin_provide_clock_func (GstElement * element)
693
{
Wim Taymans's avatar
Wim Taymans committed
694
  GstClock *result = NULL;
695
  GstElement *provider = NULL;
696
  GstBin *bin;
697
698
  GstIterator *it;
  gpointer val;
699
700
  GstClock **provided_clock_p;
  GstElement **clock_provider_p;
701

702
703
  bin = GST_BIN (element);

704
  GST_OBJECT_LOCK (bin);
705
706
707
  if (!bin->clock_dirty)
    goto not_dirty;

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

710
711
712
713
714
715
716
  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);
717
718
719
720
    if (clock) {
      GST_DEBUG_OBJECT (bin, "found candidate clock %p by element %s",
          clock, GST_ELEMENT_NAME (child));
      if (result) {
721
        gst_object_unref (result);
722
723
        gst_object_unref (provider);
      }
724
      result = clock;
725
726
727
      provider = child;
    } else {
      gst_object_unref (child);
728
    }
729
  }
730
731
732
733
734

  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);
735
  bin->clock_dirty = FALSE;
736
737
738
739
740
741
  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);
742
  GST_OBJECT_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
743

744
745
  gst_iterator_free (it);

Wim Taymans's avatar
Wim Taymans committed
746
  return result;
747
748
749
750
751
752

not_dirty:
  {
    if ((result = bin->provided_clock))
      gst_object_ref (result);
    GST_DEBUG_OBJECT (bin, "returning old clock %p", result);
753
    GST_OBJECT_UNLOCK (bin);
754
755
756

    return result;
  }
Wim Taymans's avatar
Wim Taymans committed
757
758
}

759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
/*
 * 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;
778
  GST_LOG ("looking at message %p: %d", message, eq);
779
780
781
782

  return (eq ? 0 : 1);
}

783
784
785
786
787
788
789
790
791
792
793
794
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);

795
  if (result) {
796
    GST_DEBUG_OBJECT (bin, "we found a message %p from %s matching types %08x",
797
798
799
800
        result->data, GST_OBJECT_NAME (GST_MESSAGE_CAST (result->data)->src),
        types);
  } else {
    GST_DEBUG_OBJECT (bin, "no message found matching types %08x", types);
801
802
803
804
805
806
807
808
809
#ifndef GST_DISABLE_GST_DEBUG
    {
      guint i;

      for (i = 0; i < 32; i++)
        if (types & (1 << i))
          GST_DEBUG_OBJECT (bin, "  %s", gst_message_type_get_name (1 << i));
    }
#endif
810
811
  }

812
813
814
  return result;
}

815
/* with LOCK, returns TRUE if message had a valid SRC, takes ownership of
816
 * the message.
817
818
819
820
 *
 * A message that is cached and has the same SRC and type is replaced
 * by the given message.
 */
821
822
823
824
825
826
static gboolean
bin_replace_message (GstBin * bin, GstMessage * message, GstMessageType types)
{
  GList *previous;
  GstObject *src;
  gboolean res = TRUE;
827
828
  const gchar *name;

829
  name = GST_MESSAGE_TYPE_NAME (message);
830
831
832

  if ((src = GST_MESSAGE_SRC (message))) {
    /* first find the previous message posted by this element */
833
    if ((previous = find_message (bin, src, types))) {
834
835
836
837
838
      /* 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",
839
          name, GST_ELEMENT_NAME (src));
840
841
842
843
    } else {
      /* keep new message */
      bin->messages = g_list_prepend (bin->messages, message);

844
845
      GST_DEBUG_OBJECT (bin, "got new message %p, %s from %s",
          message, name, GST_ELEMENT_NAME (src));
846
847
    }
  } else {
848
    GST_DEBUG_OBJECT (bin, "got message %s from (NULL), not processing", name);
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
    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),
872
          "deleting message %p of types 0x%08x", message, types);
873
874
875
876
      bin->messages = g_list_delete_link (bin->messages, walk);
      gst_message_unref (message);
    } else {
      GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
877
878
          "not deleting message %p of type 0x%08x", message,
          GST_MESSAGE_TYPE (message));
879
880
881
882
883
    }
  }
}


884
/* Check if the bin is EOS. We do this by scanning all sinks and
885
 * checking if they posted an EOS message.
886
887
 *
 * call with bin LOCK */
888
889
890
static gboolean
is_eos (GstBin * bin)
{
891
892
  gboolean result;
  GList *walk;
893

894
895
896
  result = TRUE;
  for (walk = bin->children; walk; walk = g_list_next (walk)) {
    GstElement *element;
897

898
899
    element = GST_ELEMENT_CAST (walk->data);
    if (bin_element_is_sink (element, bin) == 0) {
900
      /* check if element posted EOS */
901
      if (find_message (bin, GST_OBJECT_CAST (element), GST_MESSAGE_EOS)) {
Wim Taymans's avatar
Wim Taymans committed
902
        GST_DEBUG ("sink '%s' posted EOS", GST_ELEMENT_NAME (element));
903
      } else {
Wim Taymans's avatar
Wim Taymans committed
904
905
        GST_DEBUG ("sink '%s' did not post EOS yet",
            GST_ELEMENT_NAME (element));
906
        result = FALSE;
907
908
909
910
911
912
913
        break;
      }
    }
  }
  return result;
}

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
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
929
/* vmethod that adds an element to a bin
930
 *
931
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
932
 */
933
static gboolean
934
gst_bin_add_func (GstBin * bin, GstElement * element)
935
{
936
  gchar *elem_name;
937
  GstIterator *it;
938
  gboolean is_sink;
Wim Taymans's avatar
Wim Taymans committed
939
  GstMessage *clock_message = NULL, *async_message = NULL;
940
941
942
  GstStateChangeReturn ret;

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

944
945
946
  /* we obviously can't add ourself to ourself */
  if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
    goto adding_itself;
947

948
  /* get the element name to make sure it is unique in this bin. */
949
  GST_OBJECT_LOCK (element);
950
  elem_name = g_strdup (GST_ELEMENT_NAME (element));
951
  is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
952
  GST_OBJECT_UNLOCK (element);
953

954
  GST_OBJECT_LOCK (bin);
955

956
957
  /* 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
958
959
960
   * 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)))
961
    goto duplicate_name;
962

963
  /* set the element's parent and add the element to the bin's list of children */
Wim Taymans's avatar
Wim Taymans committed
964
965
  if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (element),
              GST_OBJECT_CAST (bin))))
966
    goto had_parent;
967

968
  /* if we add a sink we become a sink */
969
  if (is_sink) {
970
971
    GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was sink",
        elem_name);
972
    GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SINK);
973
  }
974
  if (gst_element_provides_clock (element)) {
975
    GST_DEBUG_OBJECT (bin, "element \"%s\" can provide a clock", elem_name);
976
    clock_message =
Wim Taymans's avatar
Wim Taymans committed
977
        gst_message_new_clock_provide (GST_OBJECT_CAST (element), NULL, TRUE);
978
  }
979

980
  bin->children = g_list_prepend (bin->children, element);
Erik Walthinsen's avatar
Erik Walthinsen committed
981
  bin->numchildren++;
982
  bin->children_cookie++;
983
  bin->priv->structure_cookie++;
Erik Walthinsen's avatar
Erik Walthinsen committed
984

Wim Taymans's avatar
Wim Taymans committed
985
986
987
988
989
990
991
992
993
994
  /* 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));

995
996
997
998
999
  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;

1000
1001
1002
1003
1004
1005
1006
1007
  /* update the bin state, the new element could have been an ASYNC or
   * NO_PREROLL element */
  ret = GST_STATE_RETURN (element);
  GST_DEBUG_OBJECT (bin, "added %s element",
      gst_element_state_change_return_get_name (ret));

  switch (ret) {
    case GST_STATE_CHANGE_ASYNC:
Wim Taymans's avatar
Wim Taymans committed
1008
1009
1010
1011
1012
    {
      /* create message to track this aync element when it posts an async-done
       * message */
      async_message =
          gst_message_new_async_start (GST_OBJECT_CAST (element), FALSE);
1013
      break;
Wim Taymans's avatar
Wim Taymans committed
1014
    }
1015
    case GST_STATE_CHANGE_NO_PREROLL:
Wim Taymans's avatar
Wim Taymans committed
1016
      /* ignore all async elements we might have and commit our state */
1017
      bin_handle_async_done (bin, ret, FALSE);
1018
1019
1020
1021
1022
1023
1024
1025
      break;
    case GST_STATE_CHANGE_FAILURE:
      break;
    default:
      break;
  }

no_state_recalc:
1026
  GST_OBJECT_UNLOCK (bin);
1027

Wim Taymans's avatar
Wim Taymans committed
1028
1029
  /* post the messages on the bus of the element so that the bin can handle
   * them */
1030
  if (clock_message)
Wim Taymans's avatar
Wim Taymans committed
1031
1032
1033
1034
    gst_element_post_message (GST_ELEMENT_CAST (element), clock_message);

  if (async_message)
    gst_element_post_message (GST_ELEMENT_CAST (element), async_message);
1035

1036
1037
1038
1039
1040
  /* unlink all linked pads */
  it = gst_element_iterate_pads (element);
  gst_iterator_foreach (it, (GFunc) unlink_pads, element);
  gst_iterator_free (it);

1041
  GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"",
1042
1043
      elem_name);
  g_free (elem_name);
Erik Walthinsen's avatar
Erik Walthinsen committed
1044

Wim Taymans's avatar
Wim Taymans committed
1045
  g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
1046
1047
1048
1049
1050

  return TRUE;

  /* ERROR handling here */
adding_itself:
1051
  {
1052
    GST_OBJECT_LOCK (bin);
1053
    g_warning ("Cannot add bin %s to itself", GST_ELEMENT_NAME (bin));
1054
    GST_OBJECT_UNLOCK (bin);
1055
1056
    return FALSE;
  }
1057
duplicate_name:
1058
1059
1060
  {
    g_warning ("Name %s is not unique in bin %s, not adding",
        elem_name, GST_ELEMENT_NAME (bin));
1061
    GST_OBJECT_UNLOCK (bin);
1062
1063
1064
    g_free (elem_name);
    return FALSE;
  }
1065
had_parent:
1066
1067
  {
    g_warning ("Element %s already has parent", elem_name);
1068
    GST_OBJECT_UNLOCK (bin);
1069
1070
1071
    g_free (elem_name);
    return FALSE;
  }
Erik Walthinsen's avatar
Erik Walthinsen committed
1072
1073
1074
}

/**
1075
 * gst_bin_add:
1076
 * @bin: a #GstBin
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1077
 * @element: the #GstElement to add
Erik Walthinsen's avatar
Erik Walthinsen committed
1078
 *