gstbin.c 75 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
107
108
109
110
111
112
113
114
115
 *     <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
 *     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 
 *     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
Wim Taymans's avatar
Wim Taymans committed
131
132
133
 *     values is returned. If the bin is a toplevel bin the value is cached. 
 *     If no sinks are available in the bin, the query fails.
 *     </para></listitem>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
135
136
137
138
139
140
141
 *   </varlistentry>
 *   <varlistentry>
 *     <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
142
143
 *
 * A #GstBin will by default forward any event sent to it to all sink elements.
Stefan Kost's avatar
Stefan Kost committed
144
145
 * 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
146
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147
148
 * </para>
 * </refsect2>
149
 *
Stefan Kost's avatar
Stefan Kost committed
150
 * Last reviewed on 2006-04-28 (0.10.6)
151
 */
Erik Walthinsen's avatar
Erik Walthinsen committed
152

153
#include "gst_private.h"
154

155
#include "gstevent.h"
156
#include "gstbin.h"
157
#include "gstmarshal.h"
158
#include "gstxml.h"
159
#include "gstinfo.h"
160
#include "gsterror.h"
161

Wim Taymans's avatar
Wim Taymans committed
162
#include "gstindex.h"
163
#include "gstindexfactory.h"
164
#include "gstutils.h"
165
#include "gstchildproxy.h"
166

167
168
169
170
171
/* enable for DURATION caching. 
 * FIXME currently too many elements don't update
 * their duration when it changes so we return inaccurate values.
 * #define DURATION_CACHING */

172
173
174
GST_DEBUG_CATEGORY_STATIC (bin_debug);
#define GST_CAT_DEFAULT bin_debug

Stefan Kost's avatar
Stefan Kost committed
175
176
static const GstElementDetails gst_bin_details =
GST_ELEMENT_DETAILS ("Generic bin",
177
178
    "Generic/Bin",
    "Simple container object",
179
    "Erik Walthinsen <omega@cse.ogi.edu>," "Wim Taymans <wim@fluendo.com>");
Erik Walthinsen's avatar
Erik Walthinsen committed
180

181
static void gst_bin_dispose (GObject * object);
Erik Walthinsen's avatar
Erik Walthinsen committed
182

183
static void gst_bin_recalc_state (GstBin * bin, gboolean force);
184
static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
185
    GstStateChange transition);
186
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
187
    GstState * state, GstState * pending, GstClockTime timeout);
Erik Walthinsen's avatar
Erik Walthinsen committed
188

Wim Taymans's avatar
Wim Taymans committed
189
190
191
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
192
#ifndef GST_DISABLE_INDEX
Wim Taymans's avatar
Wim Taymans committed
193
static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
Wim Taymans's avatar
Wim Taymans committed
194
#endif
195
static GstClock *gst_bin_provide_clock_func (GstElement * element);
196
static gboolean gst_bin_set_clock_func (GstElement * element, GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
197

198
static void gst_bin_handle_message_func (GstBin * bin, GstMessage * message);
Wim Taymans's avatar
Wim Taymans committed
199
static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
200
201
static GstBusSyncReply bin_bus_handler (GstBus * bus,
    GstMessage * message, GstBin * bin);
202
static gboolean gst_bin_query (GstElement * element, GstQuery * query);
Erik Walthinsen's avatar
Erik Walthinsen committed
203

204
#ifndef GST_DISABLE_LOADSAVE
205
206
static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent);
static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self);
207
#endif
Erik Walthinsen's avatar
Erik Walthinsen committed
208

209
210
static void bin_remove_messages (GstBin * bin, GstObject * src,
    GstMessageType types);
211
static void gst_bin_recalc_func (GstBin * child, gpointer data);
212
static gint bin_element_is_sink (GstElement * child, GstBin * bin);
213
static gint bin_element_is_src (GstElement * child, GstBin * bin);
214

215
216
static GstIterator *gst_bin_sort_iterator_new (GstBin * bin);

217
/* Bin signals and properties */
218
219
enum
{
Wim Taymans's avatar
Wim Taymans committed
220
221
  ELEMENT_ADDED,
  ELEMENT_REMOVED,
Erik Walthinsen's avatar
Erik Walthinsen committed
222
223
224
  LAST_SIGNAL
};

225
226
enum
{
227
  PROP_0
228
      /* FILL ME */
Erik Walthinsen's avatar
Erik Walthinsen committed
229
230
};

231
232
233
static void gst_bin_base_init (gpointer g_class);
static void gst_bin_class_init (GstBinClass * klass);
static void gst_bin_init (GstBin * bin);
234
static void gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data);
Erik Walthinsen's avatar
Erik Walthinsen committed
235
236
237
238

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

239
240
241
242
243
/**
 * gst_bin_get_type:
 *
 * Returns: the type of #GstBin
 */
244
GType
245
gst_bin_get_type (void)
246
{
247
248
  static GType gst_bin_type = 0;

249
  if (G_UNLIKELY (gst_bin_type == 0)) {
250
    static const GTypeInfo bin_info = {
251
      sizeof (GstBinClass),
252
      gst_bin_base_init,
253
      NULL,
254
      (GClassInitFunc) gst_bin_class_init,
255
256
      NULL,
      NULL,
257
      sizeof (GstBin),
258
      0,
259
      (GInstanceInitFunc) gst_bin_init,
260
      NULL
Erik Walthinsen's avatar
Erik Walthinsen committed
261
    };
262
263
264
265
266
    static const GInterfaceInfo child_proxy_info = {
      gst_bin_child_proxy_init,
      NULL,
      NULL
    };
267

268
    gst_bin_type =
269
        g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
270

271
    g_type_add_interface_static (gst_bin_type, GST_TYPE_CHILD_PROXY,
272
273
        &child_proxy_info);

274
275
    GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD,
        "debugging info for the 'bin' container element");
Erik Walthinsen's avatar
Erik Walthinsen committed
276
  }
277
  return gst_bin_type;
Erik Walthinsen's avatar
Erik Walthinsen committed
278
279
}

280
281
282
283
static void
gst_bin_base_init (gpointer g_class)
{
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
284

285
286
287
  gst_element_class_set_details (gstelement_class, &gst_bin_details);
}

288
289
290
291
static GstObject *
gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
    guint index)
{
292
293
294
295
296
  GstObject *res;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

297
  GST_OBJECT_LOCK (bin);
298
299
  if ((res = g_list_nth_data (bin->children, index)))
    gst_object_ref (res);
300
  GST_OBJECT_UNLOCK (bin);
301
302

  return res;
303
304
305
306
307
}

guint
gst_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
{
308
309
310
311
312
  guint num;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

313
  GST_OBJECT_LOCK (bin);
314
  num = bin->numchildren;
315
  GST_OBJECT_UNLOCK (bin);
316
317

  return num;
318
319
320
321
322
323
324
325
326
327
328
}

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
329
static void
330
gst_bin_class_init (GstBinClass * klass)
331
{
332
  GObjectClass *gobject_class;
333
  GstObjectClass *gstobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
334
  GstElementClass *gstelement_class;
335
  GError *err;
Erik Walthinsen's avatar
Erik Walthinsen committed
336

337
338
339
  gobject_class = (GObjectClass *) klass;
  gstobject_class = (GstObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Erik Walthinsen's avatar
Erik Walthinsen committed
340

341
  parent_class = g_type_class_peek_parent (klass);
Erik Walthinsen's avatar
Erik Walthinsen committed
342

343
344
  /**
   * GstBin::element-added:
345
346
   * @bin: the #GstBin
   * @element: the #GstElement that was added to the bin
347
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
348
   * Will be emitted after the element was added to the bin.
349
   */
Wim Taymans's avatar
Wim Taymans committed
350
  gst_bin_signals[ELEMENT_ADDED] =
351
352
353
      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);
354
355
  /**
   * GstBin::element-removed:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
356
   * @bin: the #GstBin
357
   * @element: the #GstElement that was removed from the bin
358
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
359
   * Will be emitted after the element was removed from the bin.
360
   */
Wim Taymans's avatar
Wim Taymans committed
361
  gst_bin_signals[ELEMENT_REMOVED] =
362
363
364
      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);
365

366
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose);
367

368
#ifndef GST_DISABLE_LOADSAVE
369
370
371
  gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
  gstobject_class->restore_thyself =
      GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
372
#endif
373

374
375
376
  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
377
#ifndef GST_DISABLE_INDEX
Wim Taymans's avatar
Wim Taymans committed
378
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
Wim Taymans's avatar
Wim Taymans committed
379
#endif
380
381
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_bin_provide_clock_func);
Wim Taymans's avatar
Wim Taymans committed
382
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
383

Wim Taymans's avatar
Wim Taymans committed
384
  gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
385
  gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query);
Wim Taymans's avatar
Wim Taymans committed
386

387
388
  klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
  klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
389
  klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
390
391
392
393

  GST_DEBUG ("creating bin thread pool");
  err = NULL;
  klass->pool =
394
      g_thread_pool_new ((GFunc) gst_bin_recalc_func, NULL, -1, FALSE, &err);
395
396
397
  if (err != NULL) {
    g_critical ("could alloc threadpool %s", err->message);
  }
398
399
}

400
static void
401
gst_bin_init (GstBin * bin)
402
{
403
404
  GstBus *bus;

Erik Walthinsen's avatar
Erik Walthinsen committed
405
406
  bin->numchildren = 0;
  bin->children = NULL;
407
  bin->children_cookie = 0;
408
  bin->messages = NULL;
409
410
  bin->polling = FALSE;
  bin->state_dirty = FALSE;
411
412
  bin->provided_clock = NULL;
  bin->clock_dirty = FALSE;
413

414
  /* Set up a bus for listening to child elements */
415
  bus = gst_bus_new ();
416
  bin->child_bus = bus;
417
418
  GST_DEBUG_OBJECT (bin, "using bus %" GST_PTR_FORMAT " to listen to children",
      bus);
419
  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin);
Erik Walthinsen's avatar
Erik Walthinsen committed
420
421
}

422
423
424
425
static void
gst_bin_dispose (GObject * object)
{
  GstBin *bin = GST_BIN (object);
426
427
428
  GstBus **child_bus_p = &bin->child_bus;
  GstClock **provided_clock_p = &bin->provided_clock;
  GstElement **clock_provider_p = &bin->clock_provider;
429
430
431
432

  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");

  bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
433

434
435
436
  gst_object_replace ((GstObject **) child_bus_p, NULL);
  gst_object_replace ((GstObject **) provided_clock_p, NULL);
  gst_object_replace ((GstObject **) clock_provider_p, NULL);
437
438
439
440
441
442
443
444
445
446
447
448

  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
449
450
/**
 * gst_bin_new:
451
 * @name: the name of the new bin
Erik Walthinsen's avatar
Erik Walthinsen committed
452
 *
453
 * Creates a new bin with the given name.
Erik Walthinsen's avatar
Erik Walthinsen committed
454
 *
455
 * Returns: a new #GstBin
Erik Walthinsen's avatar
Erik Walthinsen committed
456
 */
457
458
GstElement *
gst_bin_new (const gchar * name)
459
{
460
  return gst_element_factory_make ("bin", name);
Erik Walthinsen's avatar
Erik Walthinsen committed
461
462
}

Wim Taymans's avatar
Wim Taymans committed
463
464
/* set the index on all elements in this bin
 *
465
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
466
467
468
469
 */
#ifndef GST_DISABLE_INDEX
static void
gst_bin_set_index_func (GstElement * element, GstIndex * index)
Wim Taymans's avatar
Wim Taymans committed
470
{
Wim Taymans's avatar
Wim Taymans committed
471
472
  GstBin *bin;
  GList *children;
Wim Taymans's avatar
Wim Taymans committed
473

Wim Taymans's avatar
Wim Taymans committed
474
  bin = GST_BIN (element);
Wim Taymans's avatar
Wim Taymans committed
475

476
  GST_OBJECT_LOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
477
478
479
480
481
  for (children = bin->children; children; children = g_list_next (children)) {
    GstElement *child = GST_ELEMENT (children->data);

    gst_element_set_index (child, index);
  }
482
  GST_OBJECT_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
483
}
Wim Taymans's avatar
Wim Taymans committed
484
#endif
Wim Taymans's avatar
Wim Taymans committed
485

Wim Taymans's avatar
Wim Taymans committed
486
/* set the clock on all elements in this bin
Wim Taymans's avatar
Wim Taymans committed
487
 *
488
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
489
 */
490
static gboolean
Wim Taymans's avatar
Wim Taymans committed
491
gst_bin_set_clock_func (GstElement * element, GstClock * clock)
492
{
Wim Taymans's avatar
Wim Taymans committed
493
494
  GList *children;
  GstBin *bin;
495
  gboolean res = TRUE;
496

Wim Taymans's avatar
Wim Taymans committed
497
  bin = GST_BIN (element);
498

499
  GST_OBJECT_LOCK (bin);
500
501
502
  if (element->clock != clock) {
    for (children = bin->children; children; children = g_list_next (children)) {
      GstElement *child = GST_ELEMENT (children->data);
503

504
      res &= gst_element_set_clock (child, clock);
505
    }
Wim Taymans's avatar
Wim Taymans committed
506
  }
507
  GST_OBJECT_UNLOCK (bin);
508
509

  return res;
510
511
}

Wim Taymans's avatar
Wim Taymans committed
512
/* get the clock for this bin by asking all of the children in this bin
513
514
 *
 * The ref of the returned clock in increased so unref after usage.
Wim Taymans's avatar
Wim Taymans committed
515
 *
516
517
518
 * We loop the elements in state order and pick the last clock we can 
 * get. This makes sure we get a clock from the source.
 *
519
 * MT safe
520
 */
Wim Taymans's avatar
Wim Taymans committed
521
static GstClock *
522
gst_bin_provide_clock_func (GstElement * element)
523
{
Wim Taymans's avatar
Wim Taymans committed
524
  GstClock *result = NULL;
525
  GstElement *provider = NULL;
526
  GstBin *bin;
527
528
  GstIterator *it;
  gpointer val;
529
530
  GstClock **provided_clock_p;
  GstElement **clock_provider_p;
531

532
533
  bin = GST_BIN (element);

534
  GST_OBJECT_LOCK (bin);
535
536
537
  if (!bin->clock_dirty)
    goto not_dirty;

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

540
541
542
543
544
545
546
  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);
547
548
549
550
    if (clock) {
      GST_DEBUG_OBJECT (bin, "found candidate clock %p by element %s",
          clock, GST_ELEMENT_NAME (child));
      if (result) {
551
        gst_object_unref (result);
552
553
        gst_object_unref (provider);
      }
554
      result = clock;
555
556
557
      provider = child;
    } else {
      gst_object_unref (child);
558
    }
559
  }
560
561
562
563
564

  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);
565
  bin->clock_dirty = FALSE;
566
567
568
569
570
571
  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);
572
  GST_OBJECT_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
573

574
575
  gst_iterator_free (it);

Wim Taymans's avatar
Wim Taymans committed
576
  return result;
577
578
579
580
581
582

not_dirty:
  {
    if ((result = bin->provided_clock))
      gst_object_ref (result);
    GST_DEBUG_OBJECT (bin, "returning old clock %p", result);
583
    GST_OBJECT_UNLOCK (bin);
584
585
586

    return result;
  }
Wim Taymans's avatar
Wim Taymans committed
587
588
}

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
/*
 * 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);
}

/* with LOCK, returns TRUE if message had a valid SRC, takes ref on
613
614
615
616
617
 * the message. 
 *
 * A message that is cached and has the same SRC and type is replaced
 * by the given message.
 */
618
619
620
621
622
623
static gboolean
bin_replace_message (GstBin * bin, GstMessage * message, GstMessageType types)
{
  GList *previous;
  GstObject *src;
  gboolean res = TRUE;
624
625
  const gchar *name;

626
  name = GST_MESSAGE_TYPE_NAME (message);
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642

  if ((src = GST_MESSAGE_SRC (message))) {
    MessageFind find;

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

    /* first find the previous message posted by this element */
    previous = g_list_find_custom (bin->messages, &find,
        (GCompareFunc) message_check);
    if (previous) {
      /* 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",
643
          name, GST_ELEMENT_NAME (src));
644
645
646
647
648
    } else {
      /* keep new message */
      bin->messages = g_list_prepend (bin->messages, message);

      GST_DEBUG_OBJECT (bin, "got new message %s from %s",
649
          name, GST_ELEMENT_NAME (src));
650
651
    }
  } else {
652
    GST_DEBUG_OBJECT (bin, "got message %s from (NULL), not processing", name);
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
    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),
          "deleting message of types %d", types);
      bin->messages = g_list_delete_link (bin->messages, walk);
      gst_message_unref (message);
    } else {
      GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
681
          "not deleting message of type %d", GST_MESSAGE_TYPE (message));
682
683
684
685
686
    }
  }
}


687
/* Check if the bin is EOS. We do this by scanning all sinks and
688
 * checking if they posted an EOS message.
689
690
 *
 * call with bin LOCK */
691
692
693
static gboolean
is_eos (GstBin * bin)
{
694
695
  gboolean result;
  GList *walk;
696

697
698
699
  result = TRUE;
  for (walk = bin->children; walk; walk = g_list_next (walk)) {
    GstElement *element;
700

701
702
    element = GST_ELEMENT_CAST (walk->data);
    if (bin_element_is_sink (element, bin) == 0) {
703
704
705
706
707
708
709
710
711
712
      MessageFind find;

      /* check if element posted EOS */
      find.src = GST_OBJECT_CAST (element);
      find.types = GST_MESSAGE_EOS;

      if (g_list_find_custom (bin->messages, &find,
              (GCompareFunc) message_check)) {
        GST_DEBUG ("element posted EOS");
      } else {
713
714
        GST_DEBUG ("element did not post EOS yet");
        result = FALSE;
715
716
717
718
719
720
721
        break;
      }
    }
  }
  return result;
}

722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
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
737
/* vmethod that adds an element to a bin
738
 *
739
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
740
 */
741
static gboolean
742
gst_bin_add_func (GstBin * bin, GstElement * element)
743
{
744
  gchar *elem_name;
745
  GstIterator *it;
746
  gboolean is_sink;
747
  GstMessage *clock_message = NULL;
748

749
750
751
  /* we obviously can't add ourself to ourself */
  if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
    goto adding_itself;
752

753
  /* get the element name to make sure it is unique in this bin. */
754
  GST_OBJECT_LOCK (element);
755
  elem_name = g_strdup (GST_ELEMENT_NAME (element));
756
  is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
757
  GST_OBJECT_UNLOCK (element);
758

759
  GST_OBJECT_LOCK (bin);
760

761
762
  /* 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
763
764
765
   * 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)))
766
    goto duplicate_name;
767

768
  /* set the element's parent and add the element to the bin's list of children */
Wim Taymans's avatar
Wim Taymans committed
769
770
  if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (element),
              GST_OBJECT_CAST (bin))))
771
    goto had_parent;
772

773
  /* if we add a sink we become a sink */
774
  if (is_sink) {
775
776
    GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was sink",
        elem_name);
777
    GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SINK);
778
  }
779
  if (gst_element_provides_clock (element)) {
780
    GST_DEBUG_OBJECT (bin, "element \"%s\" can provide a clock", elem_name);
781
    bin->clock_dirty = TRUE;
782
783
    clock_message =
        gst_message_new_clock_provide (GST_OBJECT_CAST (bin), NULL, TRUE);
784
  }
785

786
  bin->children = g_list_prepend (bin->children, element);
Erik Walthinsen's avatar
Erik Walthinsen committed
787
  bin->numchildren++;
788
  bin->children_cookie++;
Erik Walthinsen's avatar
Erik Walthinsen committed
789

790
  /* distribute the bus */
791
  gst_element_set_bus (element, bin->child_bus);
Wim Taymans's avatar
Wim Taymans committed
792

793
  /* propagate the current base time and clock */
Wim Taymans's avatar
Wim Taymans committed
794
  gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time);
795
796
797
  /* 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 */
798
  gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
799
  bin->state_dirty = TRUE;
800
  GST_OBJECT_UNLOCK (bin);
801

802
803
804
805
  if (clock_message) {
    gst_element_post_message (GST_ELEMENT_CAST (bin), clock_message);
  }

806
807
808
809
810
  /* unlink all linked pads */
  it = gst_element_iterate_pads (element);
  gst_iterator_foreach (it, (GFunc) unlink_pads, element);
  gst_iterator_free (it);

811
  GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"",
812
813
      elem_name);
  g_free (elem_name);
Erik Walthinsen's avatar
Erik Walthinsen committed
814

Wim Taymans's avatar
Wim Taymans committed
815
  g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
816
817
818
819
820

  return TRUE;

  /* ERROR handling here */
adding_itself:
821
  {
822
    GST_OBJECT_LOCK (bin);
823
    g_warning ("Cannot add bin %s to itself", GST_ELEMENT_NAME (bin));
824
    GST_OBJECT_UNLOCK (bin);
825
826
    return FALSE;
  }
827
duplicate_name:
828
829
830
  {
    g_warning ("Name %s is not unique in bin %s, not adding",
        elem_name, GST_ELEMENT_NAME (bin));
831
    GST_OBJECT_UNLOCK (bin);
832
833
834
    g_free (elem_name);
    return FALSE;
  }
835
had_parent:
836
837
  {
    g_warning ("Element %s already has parent", elem_name);
838
    GST_OBJECT_UNLOCK (bin);
839
840
841
    g_free (elem_name);
    return FALSE;
  }
Erik Walthinsen's avatar
Erik Walthinsen committed
842
843
}

844

Erik Walthinsen's avatar
Erik Walthinsen committed
845
/**
846
 * gst_bin_add:
847
 * @bin: a #GstBin
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
848
 * @element: the #GstElement to add
Erik Walthinsen's avatar
Erik Walthinsen committed
849
 *
850
 * Adds the given element to the bin.  Sets the element's parent, and thus
851
 * takes ownership of the element. An element can only be added to one bin.
852
 *
853
854
855
 * If the element's pads are linked to other pads, the pads will be unlinked
 * before the element is added to the bin.
 *
856
857
 * MT safe.
 *
858
 * Returns: TRUE if the element could be added, FALSE if
859
 * the bin does not want to accept the element.
Erik Walthinsen's avatar
Erik Walthinsen committed
860
 */
861
gboolean
862
gst_bin_add (GstBin * bin, GstElement * element)
863
{
864
  GstBinClass *bclass;
865
  gboolean result;
866

867
868
  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
869
870
871

  bclass = GST_BIN_GET_CLASS (bin);

872
873
874
875
876
877
878
879
880
881
  if (G_UNLIKELY (bclass->add_element == NULL))
    goto no_function;

  GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element %s to bin %s",
      GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));

  result = bclass->add_element (bin, element);

  return result;

882
  /* ERROR handling */
883
no_function:
884
885
886
887
888
  {
    g_warning ("adding elements to bin %s is not supported",
        GST_ELEMENT_NAME (bin));
    return FALSE;
  }
889
890
}

891
892
893
894
895
/* remove an element from the bin
 *
 * MT safe
 */
static gboolean