gstbin.c 117 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
 *     <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
Wim Taymans's avatar
Wim Taymans committed
87
88
 *     query. Note that these messages can be posted before the bin is
 *     prerolled, in which case the duration query might fail.
Wim Taymans's avatar
Wim Taymans committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 *     </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 "gstinfo.h"
165
#include "gsterror.h"
166

167
#include "gstutils.h"
168
#include "gstchildproxy.h"
169

170
171
172
GST_DEBUG_CATEGORY_STATIC (bin_debug);
#define GST_CAT_DEFAULT bin_debug

173
174
175
176
/* 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)

177
178
179
#define GST_BIN_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BIN, GstBinPrivate))

180
181
182
struct _GstBinPrivate
{
  gboolean asynchandling;
183
184
185
186
187
188
  /* 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;
189
190

  guint32 structure_cookie;
Wim Taymans's avatar
Wim Taymans committed
191

192
#if 0
Wim Taymans's avatar
Wim Taymans committed
193
194
  /* cached index */
  GstIndex *index;
195
196
#endif

Wim Taymans's avatar
Wim Taymans committed
197
198
  /* forward messages from our children */
  gboolean message_forward;
199
200

  gboolean posted_eos;
201
202
};

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
220
static void gst_bin_state_changed (GstElement * element, GstState oldstate,
    GstState newstate, GstState pending);
221
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
222
    GstState * state, GstState * pending, GstClockTime timeout);
223
static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
Wim Taymans's avatar
Wim Taymans committed
224
    gboolean flag_pending, gboolean reset_time);
225
static void bin_handle_async_start (GstBin * bin);
226
static void bin_push_state_continue (BinContinueData * data);
227
static void bin_do_eos (GstBin * bin);
Erik Walthinsen's avatar
Erik Walthinsen committed
228

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

232
#if 0
Wim Taymans's avatar
Wim Taymans committed
233
static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
Wim Taymans's avatar
Wim Taymans committed
234
static GstIndex *gst_bin_get_index_func (GstElement * element);
235
#endif
Wim Taymans's avatar
Wim Taymans committed
236

237
static GstClock *gst_bin_provide_clock_func (GstElement * element);
238
static gboolean gst_bin_set_clock_func (GstElement * element, GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
239

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

246
247
static gboolean gst_bin_do_latency_func (GstBin * bin);

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

254
255
static GstIterator *gst_bin_sort_iterator_new (GstBin * bin);

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

265
#define DEFAULT_ASYNC_HANDLING	FALSE
Wim Taymans's avatar
Wim Taymans committed
266
#define DEFAULT_MESSAGE_FORWARD	FALSE
267

268
269
enum
{
270
  PROP_0,
Wim Taymans's avatar
Wim Taymans committed
271
272
273
  PROP_ASYNC_HANDLING,
  PROP_MESSAGE_FORWARD,
  PROP_LAST
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
#define _do_init \
281
282
283
284
285
286
{ \
  static const GInterfaceInfo iface_info = { \
    gst_bin_child_proxy_init, \
    NULL, \
    NULL}; \
  \
287
  g_type_add_interface_static (g_define_type_id, GST_TYPE_CHILD_PROXY, &iface_info); \
288
289
290
291
  \
  GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD, \
      "debugging info for the 'bin' container element"); \
  \
Erik Walthinsen's avatar
Erik Walthinsen committed
292
293
}

294
295
#define gst_bin_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstBin, gst_bin, GST_TYPE_ELEMENT, _do_init);
296

297
298
299
300
static GstObject *
gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
    guint index)
{
301
302
303
304
305
  GstObject *res;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

306
  GST_OBJECT_LOCK (bin);
307
308
  if ((res = g_list_nth_data (bin->children, index)))
    gst_object_ref (res);
309
  GST_OBJECT_UNLOCK (bin);
310
311

  return res;
312
313
}

314
static guint
315
316
gst_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
{
317
318
319
320
321
  guint num;
  GstBin *bin;

  bin = GST_BIN_CAST (child_proxy);

322
  GST_OBJECT_LOCK (bin);
323
  num = bin->numchildren;
324
  GST_OBJECT_UNLOCK (bin);
325
326

  return num;
327
328
329
330
331
332
333
334
335
336
337
}

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

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
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
354
static void
355
gst_bin_class_init (GstBinClass * klass)
356
{
357
  GObjectClass *gobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
358
  GstElementClass *gstelement_class;
359
  GError *err;
Erik Walthinsen's avatar
Erik Walthinsen committed
360

361
362
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Erik Walthinsen's avatar
Erik Walthinsen committed
363

364
365
  g_type_class_add_private (klass, sizeof (GstBinPrivate));

366
367
  gobject_class->set_property = gst_bin_set_property;
  gobject_class->get_property = gst_bin_get_property;
368
369
370
371
372
373

  /**
   * 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
374
   * of its children on its own.
375
376
   *
   * Since: 0.10.13
377
   */
378
379
380
  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",
381
          DEFAULT_ASYNC_HANDLING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
382

383
384
  /**
   * GstBin::element-added:
385
386
   * @bin: the #GstBin
   * @element: the #GstElement that was added to the bin
387
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
388
   * Will be emitted after the element was added to the bin.
389
   */
Wim Taymans's avatar
Wim Taymans committed
390
  gst_bin_signals[ELEMENT_ADDED] =
391
392
      g_signal_new ("element-added", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_added), NULL,
393
      NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
394
395
  /**
   * GstBin::element-removed:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
396
   * @bin: the #GstBin
397
   * @element: the #GstElement that was removed from the bin
398
   *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
399
   * Will be emitted after the element was removed from the bin.
400
   */
Wim Taymans's avatar
Wim Taymans committed
401
  gst_bin_signals[ELEMENT_REMOVED] =
402
403
      g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL,
404
      NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  /**
   * 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),
426
      _gst_boolean_accumulator, NULL, g_cclosure_marshal_generic,
427
      G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
428

Wim Taymans's avatar
Wim Taymans committed
429
430
431
432
433
434
435
  /**
   * GstBin:message-forward
   *
   * Forward all children messages, even those that would normally be filtered by
   * the bin. This can be interesting when one wants to be notified of the EOS
   * state of individual elements, for example.
   *
436
   * The messages are converted to an ELEMENT message with the bin as the
Wim Taymans's avatar
Wim Taymans committed
437
438
439
440
441
442
443
444
445
446
447
   * source. The structure of the message is named 'GstBinForwarded' and contains
   * a field named 'message' of type GST_TYPE_MESSAGE that contains the original
   * forwarded message.
   *
   * Since: 0.10.31
   */
  g_object_class_install_property (gobject_class, PROP_MESSAGE_FORWARD,
      g_param_spec_boolean ("message-forward", "Message Forward",
          "Forwards all children messages",
          DEFAULT_MESSAGE_FORWARD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

448
  gobject_class->dispose = gst_bin_dispose;
449

450
451
452
453
454
455
  gst_element_class_set_metadata (gstelement_class, "Generic bin",
      "Generic/Bin",
      "Simple container object",
      "Erik Walthinsen <omega@cse.ogi.edu>,"
      "Wim Taymans <wim.taymans@gmail.com>");

456
457
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_bin_change_state_func);
458
  gstelement_class->state_changed = GST_DEBUG_FUNCPTR (gst_bin_state_changed);
459
  gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func);
460
#if 0
Wim Taymans's avatar
Wim Taymans committed
461
  gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_bin_get_index_func);
Wim Taymans's avatar
Wim Taymans committed
462
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
463
#endif
464
465
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_bin_provide_clock_func);
Wim Taymans's avatar
Wim Taymans committed
466
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
467

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

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

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

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

486
static void
487
gst_bin_init (GstBin * bin)
488
{
489
490
  GstBus *bus;

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

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

505
  bin->priv = GST_BIN_GET_PRIVATE (bin);
506
  bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING;
507
  bin->priv->structure_cookie = 0;
Wim Taymans's avatar
Wim Taymans committed
508
  bin->priv->message_forward = DEFAULT_MESSAGE_FORWARD;
Erik Walthinsen's avatar
Erik Walthinsen committed
509
510
}

511
512
513
static void
gst_bin_dispose (GObject * object)
{
Wim Taymans's avatar
Wim Taymans committed
514
  GstBin *bin = GST_BIN_CAST (object);
515
516
517
  GstBus **child_bus_p = &bin->child_bus;
  GstClock **provided_clock_p = &bin->provided_clock;
  GstElement **clock_provider_p = &bin->clock_provider;
518
519
520

  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");

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

  while (bin->children) {
    gst_bin_remove (bin, GST_ELEMENT_CAST (bin->children->data));
  }
  if (G_UNLIKELY (bin->children != NULL)) {
532
    g_critical ("could not remove elements from bin '%s'",
533
534
535
536
537
538
        GST_STR_NULL (GST_OBJECT_NAME (object)));
  }

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

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

553
554
555
556
557
558
static void
gst_bin_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstBin *gstbin;

Wim Taymans's avatar
Wim Taymans committed
559
  gstbin = GST_BIN_CAST (object);
560
561
562
563

  switch (prop_id) {
    case PROP_ASYNC_HANDLING:
      GST_OBJECT_LOCK (gstbin);
564
      gstbin->priv->asynchandling = g_value_get_boolean (value);
565
566
      GST_OBJECT_UNLOCK (gstbin);
      break;
Wim Taymans's avatar
Wim Taymans committed
567
568
569
570
571
    case PROP_MESSAGE_FORWARD:
      GST_OBJECT_LOCK (gstbin);
      gstbin->priv->message_forward = g_value_get_boolean (value);
      GST_OBJECT_UNLOCK (gstbin);
      break;
572
573
574
575
576
577
578
579
580
581
582
583
    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;

Wim Taymans's avatar
Wim Taymans committed
584
  gstbin = GST_BIN_CAST (object);
585
586
587
588

  switch (prop_id) {
    case PROP_ASYNC_HANDLING:
      GST_OBJECT_LOCK (gstbin);
589
      g_value_set_boolean (value, gstbin->priv->asynchandling);
590
591
      GST_OBJECT_UNLOCK (gstbin);
      break;
Wim Taymans's avatar
Wim Taymans committed
592
593
594
595
596
    case PROP_MESSAGE_FORWARD:
      GST_OBJECT_LOCK (gstbin);
      g_value_set_boolean (value, gstbin->priv->message_forward);
      GST_OBJECT_UNLOCK (gstbin);
      break;
597
598
599
600
601
602
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

603
#if 0
Wim Taymans's avatar
Wim Taymans committed
604
605
606
607
608
609
610
/* return the cached index */
static GstIndex *
gst_bin_get_index_func (GstElement * element)
{
  GstBin *bin;
  GstIndex *result;

Wim Taymans's avatar
Wim Taymans committed
611
  bin = GST_BIN_CAST (element);
Wim Taymans's avatar
Wim Taymans committed
612
613
614
615
616
617
618
619
620

  GST_OBJECT_LOCK (bin);
  if ((result = bin->priv->index))
    gst_object_ref (result);
  GST_OBJECT_UNLOCK (bin);

  return result;
}

Wim Taymans's avatar
Wim Taymans committed
621
622
/* set the index on all elements in this bin
 *
623
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
624
625
626
 */
static void
gst_bin_set_index_func (GstElement * element, GstIndex * index)
Wim Taymans's avatar
Wim Taymans committed
627
{
Wim Taymans's avatar
Wim Taymans committed
628
  GstBin *bin;
629
630
  gboolean done;
  GstIterator *it;
Wim Taymans's avatar
Wim Taymans committed
631
  GstIndex *old;
632
  GValue data = { 0, };
Wim Taymans's avatar
Wim Taymans committed
633

Wim Taymans's avatar
Wim Taymans committed
634
  bin = GST_BIN_CAST (element);
Wim Taymans's avatar
Wim Taymans committed
635

Wim Taymans's avatar
Wim Taymans committed
636
637
638
639
640
641
642
643
644
645
646
647
  GST_OBJECT_LOCK (bin);
  old = bin->priv->index;
  if (G_UNLIKELY (old == index))
    goto was_set;
  if (index)
    gst_object_ref (index);
  bin->priv->index = index;
  GST_OBJECT_UNLOCK (bin);

  if (old)
    gst_object_unref (old);

648
649
  it = gst_bin_iterate_elements (bin);

Wim Taymans's avatar
Wim Taymans committed
650
  /* set the index on all elements in the bin */
651
652
653
654
655
  done = FALSE;
  while (!done) {
    switch (gst_iterator_next (it, &data)) {
      case GST_ITERATOR_OK:
      {
656
        GstElement *child = g_value_get_object (&data);
657

658
659
        GST_DEBUG_OBJECT (bin, "setting index on '%s'",
            GST_ELEMENT_NAME (child));
660
661
        gst_element_set_index (child, index);

662
        g_value_reset (&data);
663
664
665
666
667
668
669
670
671
672
673
674
        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
675
  }
676
  g_value_unset (&data);
677
  gst_iterator_free (it);
Wim Taymans's avatar
Wim Taymans committed
678
679
680
681
682
683
684
685
  return;

was_set:
  {
    GST_DEBUG_OBJECT (bin, "index was already set");
    GST_OBJECT_UNLOCK (bin);
    return;
  }
Wim Taymans's avatar
Wim Taymans committed
686
}
687
#endif
Wim Taymans's avatar
Wim Taymans committed
688

Wim Taymans's avatar
Wim Taymans committed
689
/* set the clock on all elements in this bin
Wim Taymans's avatar
Wim Taymans committed
690
 *
691
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
692
 */
693
static gboolean
Wim Taymans's avatar
Wim Taymans committed
694
gst_bin_set_clock_func (GstElement * element, GstClock * clock)
695
{
Wim Taymans's avatar
Wim Taymans committed
696
  GstBin *bin;
697
698
  gboolean done;
  GstIterator *it;
699
  gboolean res = TRUE;
700
  GValue data = { 0, };
701

Wim Taymans's avatar
Wim Taymans committed
702
  bin = GST_BIN_CAST (element);
703

704
705
706
707
708
709
710
  it = gst_bin_iterate_elements (bin);

  done = FALSE;
  while (!done) {
    switch (gst_iterator_next (it, &data)) {
      case GST_ITERATOR_OK:
      {
711
        GstElement *child = g_value_get_object (&data);
712
713
714

        res &= gst_element_set_clock (child, clock);

715
        g_value_reset (&data);
716
717
718
719
720
721
722
723
724
725
726
727
        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;
728
    }
Wim Taymans's avatar
Wim Taymans committed
729
  }
730
  g_value_unset (&data);
731
  gst_iterator_free (it);
732

733
734
735
  if (res)
    res = GST_ELEMENT_CLASS (parent_class)->set_clock (element, clock);

736
  return res;
737
738
}

Wim Taymans's avatar
Wim Taymans committed
739
/* get the clock for this bin by asking all of the children in this bin
740
741
 *
 * The ref of the returned clock in increased so unref after usage.
Wim Taymans's avatar
Wim Taymans committed
742
 *
743
 * We loop the elements in state order and pick the last clock we can
744
745
 * get. This makes sure we get a clock from the source.
 *
746
 * MT safe
747
 */
Wim Taymans's avatar
Wim Taymans committed
748
static GstClock *
749
gst_bin_provide_clock_func (GstElement * element)
750
{
Wim Taymans's avatar
Wim Taymans committed
751
  GstClock *result = NULL;
752
  GstElement *provider = NULL;
753
  GstBin *bin;
754
  GstIterator *it;
755
756
  gboolean done;
  GValue val = { 0, };
757
758
  GstClock **provided_clock_p;
  GstElement **clock_provider_p;
759

Wim Taymans's avatar
Wim Taymans committed
760
  bin = GST_BIN_CAST (element);
761

762
  GST_OBJECT_LOCK (bin);
763
764
765
  if (!bin->clock_dirty)
    goto not_dirty;

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

768
  it = gst_bin_sort_iterator_new (bin);
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
  GST_OBJECT_UNLOCK (bin);

  done = FALSE;
  while (!done) {
    switch (gst_iterator_next (it, &val)) {
      case GST_ITERATOR_OK:
      {
        GstElement *child = g_value_get_object (&val);
        GstClock *clock;

        clock = gst_element_provide_clock (child);
        if (clock) {
          GST_DEBUG_OBJECT (bin, "found candidate clock %p by element %s",
              clock, GST_ELEMENT_NAME (child));
          if (result) {
            gst_object_unref (result);
            gst_object_unref (provider);
          }
          result = clock;
          provider = gst_object_ref (child);
        }
790

791
792
        g_value_reset (&val);
        break;
793
      }
794
795
796
797
798
799
800
      case GST_ITERATOR_RESYNC:
        gst_iterator_resync (it);
        break;
      default:
      case GST_ITERATOR_DONE:
        done = TRUE;
        break;
801
    }
802
  }
803
804
805
806
807
808
809
810
811
812
813
814
815
  g_value_unset (&val);
  gst_iterator_free (it);

  GST_OBJECT_LOCK (bin);
  if (!bin->clock_dirty) {
    if (provider)
      gst_object_unref (provider);
    if (result)
      gst_object_unref (result);
    result = NULL;

    goto not_dirty;
  }
816
817
818
819
820

  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);
821
  bin->clock_dirty = FALSE;
822
823
824
825
826
827
  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);
828
  GST_OBJECT_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
829
830

  return result;
831
832
833
834
835
836

not_dirty:
  {
    if ((result = bin->provided_clock))
      gst_object_ref (result);
    GST_DEBUG_OBJECT (bin, "returning old clock %p", result);
837
    GST_OBJECT_UNLOCK (bin);
838
839
840

    return result;
  }
Wim Taymans's avatar
Wim Taymans committed
841
842
}

843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
/*
 * 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;
862
  GST_LOG ("looking at message %p: %d", message, eq);
863
864
865
866

  return (eq ? 0 : 1);
}

867
868
869
870
871
872
873
874
875
876
877
878
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);

879
  if (result) {
880
    GST_DEBUG_OBJECT (bin, "we found a message %p from %s matching types %08x",
881
882
883
884
        result->data, GST_OBJECT_NAME (GST_MESSAGE_CAST (result->data)->src),
        types);
  } else {
    GST_DEBUG_OBJECT (bin, "no message found matching types %08x", types);
885
886
887
888
889
890
891
892
893
#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
894
895
  }

896
897
898
  return result;
}

899
/* with LOCK, returns TRUE if message had a valid SRC, takes ownership of
900
 * the message.
901
902
903
904
 *
 * A message that is cached and has the same SRC and type is replaced
 * by the given message.
 */
905
906
907
908
909
910
911
912
913
static gboolean
bin_replace_message (GstBin * bin, GstMessage * message, GstMessageType types)
{
  GList *previous;
  GstObject *src;
  gboolean res = TRUE;

  if ((src = GST_MESSAGE_SRC (message))) {
    /* first find the previous message posted by this element */
914
    if ((previous = find_message (bin, src, types))) {
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
915
916
      GstMessage *previous_msg;

917
      /* if we found a previous message, replace it */
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
918
      previous_msg = previous->data;
919
920
      previous->data = message;

Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
921
      GST_DEBUG_OBJECT (bin, "replace old message %s from %s with %s message",
922
923
          GST_MESSAGE_TYPE_NAME (previous_msg), GST_ELEMENT_NAME (src),
          GST_MESSAGE_TYPE_NAME (message));
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
924
925

      gst_message_unref (previous_msg);
926
927
928
929
    } else {
      /* keep new message */
      bin->messages = g_list_prepend (bin->messages, message);

930
      GST_DEBUG_OBJECT (bin, "got new message %p, %s from %s",
931
          message, GST_MESSAGE_TYPE_NAME (message), GST_ELEMENT_NAME (src));
932
933
    }
  } else {
934
935
    GST_DEBUG_OBJECT (bin, "got message %s from (NULL), not processing",
        GST_MESSAGE_TYPE_NAME (message));
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
    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),
959
          "deleting message %p of types 0x%08x", message, types);
960
961
962
963
      bin->messages = g_list_delete_link (bin->messages, walk);
      gst_message_unref (message);
    } else {
      GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
964
965
          "not deleting message %p of type 0x%08x", message,
          GST_MESSAGE_TYPE (message));
966
967
968
969
970
    }
  }
}


971
/* Check if the bin is EOS. We do this by scanning all sinks and
972
 * checking if they posted an EOS message.
973
974
 *
 * call with bin LOCK */
975
static gboolean
976
is_eos (GstBin * bin, guint32 * seqnum)
977
{
978
  gboolean result;
979
  gint n_eos = 0;
980
  GList *walk, *msgs;
981

982
983
984
  result = TRUE;
  for (walk = bin->children; walk; walk = g_list_next (walk)) {
    GstElement *element;
985

986
987
    element = GST_ELEMENT_CAST (walk->data);
    if (bin_element_is_sink (element, bin) == 0) {
988
      /* check if element posted EOS */
989
990
      if ((msgs =
              find_message (bin, GST_OBJECT_CAST (element), GST_MESSAGE_EOS))) {
Wim Taymans's avatar
Wim Taymans committed
991
        GST_DEBUG ("sink '%s' posted EOS", GST_ELEMENT_NAME (element));
992
        *seqnum = gst_message_get_seqnum (GST_MESSAGE_CAST (msgs->data));
993
        n_eos++;
994
      } else {
Wim Taymans's avatar
Wim Taymans committed
995
996
        GST_DEBUG ("sink '%s' did not post EOS yet",
            GST_ELEMENT_NAME (element));
997
        result = FALSE;
998
999
1000
1001
        break;
      }
    }
  }
1002
1003
1004
1005
1006
1007
1008
  /* FIXME: Some tests (e.g. elements/capsfilter) use
   * pipelines with a dangling sinkpad but no sink element.
   * These tests assume that no EOS message is ever
   * posted on the bus so let's keep that behaviour.
   * In valid pipelines this doesn't make a difference.
   */
  return result && n_eos > 0;
1009
1010
}

1011
static void
1012
unlink_pads (const GValue * item, gpointer user_data)
1013
{
1014
  GstPad *pad;
1015
1016
  GstPad *peer;

1017
1018
  pad = g_value_get_object (item);

1019
1020
1021
1022
1023
1024
1025
1026
1027
  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);
  }
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1028
/* vmethod that adds an element to a bin
1029
 *
1030
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
1031
 */
1032
static gboolean