gstbin.c 128 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
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
20 21
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, 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>
81
 *     <term>GST_MESSAGE_DURATION_CHANGED</term>
Wim Taymans's avatar
Wim Taymans committed
82 83 84 85 86
 *     <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.
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
 */
Erik Walthinsen's avatar
Erik Walthinsen committed
157

158
#include "gst_private.h"
159

160
#include "gstevent.h"
161
#include "gstbin.h"
162
#include "gstinfo.h"
163
#include "gsterror.h"
164

165
#include "gstutils.h"
166
#include "gstchildproxy.h"
167

168 169 170
GST_DEBUG_CATEGORY_STATIC (bin_debug);
#define GST_CAT_DEFAULT bin_debug

171 172 173 174
/* 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)

175 176 177
#define GST_BIN_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BIN, GstBinPrivate))

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

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

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

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

  gboolean posted_eos;
199
  gboolean posted_playing;
200 201

  GList *contexts;
202 203
};

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

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

213 214 215 216 217
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);

218
static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
219
    GstStateChange transition);
220
static gboolean gst_bin_post_message (GstElement * element, GstMessage * msg);
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,
224
    gboolean flag_pending, GstClockTime running_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);
245
static void gst_bin_set_context (GstElement * element, GstContext * context);
Erik Walthinsen's avatar
Erik Walthinsen committed
246

247 248
static gboolean gst_bin_do_latency_func (GstBin * bin);

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

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

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

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

269 270
enum
{
271
  PROP_0,
Wim Taymans's avatar
Wim Taymans committed
272 273 274
  PROP_ASYNC_HANDLING,
  PROP_MESSAGE_FORWARD,
  PROP_LAST
Erik Walthinsen's avatar
Erik Walthinsen committed
275 276
};

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

static guint gst_bin_signals[LAST_SIGNAL] = { 0 };

281
#define _do_init \
282 283 284 285 286 287
{ \
  static const GInterfaceInfo iface_info = { \
    gst_bin_child_proxy_init, \
    NULL, \
    NULL}; \
  \
288
  g_type_add_interface_static (g_define_type_id, GST_TYPE_CHILD_PROXY, &iface_info); \
289 290 291 292
  \
  GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD, \
      "debugging info for the 'bin' container element"); \
  \
Erik Walthinsen's avatar
Erik Walthinsen committed
293 294
}

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

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

  bin = GST_BIN_CAST (child_proxy);

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

312
  return (GObject *) res;
313 314
}

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

  bin = GST_BIN_CAST (child_proxy);

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

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

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

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

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

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

367 368
  gobject_class->set_property = gst_bin_set_property;
  gobject_class->get_property = gst_bin_get_property;
369 370

  /**
371
   * GstBin:async-handling:
372
   *
373
   * If set to %TRUE, the bin will handle asynchronous state changes.
374
   * This should be used only if the bin subclass is modifying the state
375
   * of its children on its own.
376
   */
377 378 379
  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",
380
          DEFAULT_ASYNC_HANDLING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
381

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

Wim Taymans's avatar
Wim Taymans committed
426
  /**
427
   * GstBin:message-forward:
Wim Taymans's avatar
Wim Taymans committed
428 429 430 431 432
   *
   * 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.
   *
433
   * The messages are converted to an ELEMENT message with the bin as the
Wim Taymans's avatar
Wim Taymans committed
434 435 436 437 438 439 440 441 442
   * 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.
   */
  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));

443
  gobject_class->dispose = gst_bin_dispose;
444

445
  gst_element_class_set_static_metadata (gstelement_class, "Generic bin",
446 447 448 449 450
      "Generic/Bin",
      "Simple container object",
      "Erik Walthinsen <omega@cse.ogi.edu>,"
      "Wim Taymans <wim.taymans@gmail.com>");

451 452
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_bin_change_state_func);
453
  gstelement_class->post_message = GST_DEBUG_FUNCPTR (gst_bin_post_message);
454
  gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func);
455
#if 0
Wim Taymans's avatar
Wim Taymans committed
456
  gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_bin_get_index_func);
Wim Taymans's avatar
Wim Taymans committed
457
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
458
#endif
459 460
  gstelement_class->provide_clock =
      GST_DEBUG_FUNCPTR (gst_bin_provide_clock_func);
Wim Taymans's avatar
Wim Taymans committed
461
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
462

Wim Taymans's avatar
Wim Taymans committed
463
  gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
464
  gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query);
465
  gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_bin_set_context);
Wim Taymans's avatar
Wim Taymans committed
466

467 468
  klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
  klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
469
  klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
470

471 472
  klass->do_latency = GST_DEBUG_FUNCPTR (gst_bin_do_latency_func);

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

482
static void
483
gst_bin_init (GstBin * bin)
484
{
485 486
  GstBus *bus;

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

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

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

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

  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");

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

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

533 534
  g_list_free_full (bin->priv->contexts, (GDestroyNotify) gst_context_unref);

535 536 537
  G_OBJECT_CLASS (parent_class)->dispose (object);
}

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

552 553 554 555 556 557
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
558
  gstbin = GST_BIN_CAST (object);
559 560 561 562

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

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

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

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

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

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

Wim Taymans's avatar
Wim Taymans committed
635 636 637 638 639 640 641 642 643 644 645 646
  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);

647 648
  it = gst_bin_iterate_elements (bin);

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

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

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

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

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

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

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

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

        res &= gst_element_set_clock (child, clock);

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

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

735
  return res;
736 737
}

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

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

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

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

767
  it = gst_bin_sort_iterator_new (bin);
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
  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);
        }
789

790 791
        g_value_reset (&val);
        break;
792
      }
793 794 795 796 797 798 799
      case GST_ITERATOR_RESYNC:
        gst_iterator_resync (it);
        break;
      default:
      case GST_ITERATOR_DONE:
        done = TRUE;
        break;
800
    }
801
  }
802 803 804 805 806 807 808 809 810 811 812 813 814
  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;
  }
815 816 817 818 819

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

  return result;
830 831 832 833 834 835

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

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

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

  return (eq ? 0 : 1);
}

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

878
  if (result) {
879
    GST_DEBUG_OBJECT (bin, "we found a message %p from %s matching types %08x",
880 881 882 883
        result->data, GST_OBJECT_NAME (GST_MESSAGE_CAST (result->data)->src),
        types);
  } else {
    GST_DEBUG_OBJECT (bin, "no message found matching types %08x", types);
884 885 886 887 888
#ifndef GST_DISABLE_GST_DEBUG
    {
      guint i;

      for (i = 0; i < 32; i++)
889 890
        if (types & (1U << i))
          GST_DEBUG_OBJECT (bin, "  %s", gst_message_type_get_name (1U << i));
891 892
    }
#endif
893 894
  }

895