gstbin.c 51.9 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
 */
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
/**
 * SECTION:gstbin
 * @short_description: Base class for elements that contain other elements
 *
 * GstBin is the simplest of the container elements, allowing elements to
 * become children of itself.  Pads from the child elements can be ghosted to
 * the bin, making the bin itself look transparently like any other element,
 * allowing for deep nesting of predefined sub-pipelines.
 *
 * A new GstBin is created with gst_bin_new(). Use a #GstPipeline instead if you
 * want to create a toplevel bin because a normal bin doesn't have a scheduler
 * of its own.
 * 
 * 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.
 * 
 * The list of elements in a bin can be retrieved with gst_bin_get_list().
 * 
 * After the bin has been set to the PLAYING state (with gst_element_set_state()), 
 * gst_bin_iterate() is used to process the elements in the bin.
 * 
 * The "element_added" signal is fired whenever a new element is added to the
 * bin. Likewise the "element_removed" signal is fired whenever an element is
 * removed from the bin.
 *
 * gst_bin_destroy() is used to destroy the bin. 
 *
 * To control the selection of the clock in a bin, you can use the following
 * methods:
 * gst_bin_auto_clock() to let the bin select a clock automatically,
 * gst_bin_get_clock() to get the current clock of the bin and
 * gst_bin_use_clock() to specify a clock explicitly.
 * Note that the default behaviour is to automatically select a clock from one
 * of the clock providers in the bin.
 */
Erik Walthinsen's avatar
Erik Walthinsen committed
65

66
#include "gst_private.h"
67

68
#include "gstevent.h"
69
#include "gstbin.h"
70
#include "gstmarshal.h"
71
#include "gstxml.h"
72
#include "gstinfo.h"
73
#include "gsterror.h"
74

Wim Taymans's avatar
Wim Taymans committed
75
#include "gstindex.h"
76
#include "gstutils.h"
77
#include "gstchildproxy.h"
78

79 80 81 82 83 84 85 86 87
GST_DEBUG_CATEGORY_STATIC (bin_debug);
#define GST_CAT_DEFAULT bin_debug
#define GST_LOG_BIN_CONTENTS(bin, text) GST_LOG_OBJECT ((bin), \
	text ": %d elements: %u PLAYING, %u PAUSED, %u READY, %u NULL, own state: %s", \
	(bin)->numchildren, (guint) (bin)->child_states[3], \
	(guint) (bin)->child_states[2], (bin)->child_states[1], \
	(bin)->child_states[0], gst_element_state_get_name (GST_STATE (bin)))


88 89 90
static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin",
    "Generic/Bin",
    "Simple container object",
91
    "Erik Walthinsen <omega@cse.ogi.edu>," "Wim Taymans <wim@fluendo.com>");
Erik Walthinsen's avatar
Erik Walthinsen committed
92

93
GType _gst_bin_type = 0;
Erik Walthinsen's avatar
Erik Walthinsen committed
94

95
static void gst_bin_dispose (GObject * object);
Erik Walthinsen's avatar
Erik Walthinsen committed
96

97
static GstElementStateReturn gst_bin_change_state (GstElement * element);
Wim Taymans's avatar
Wim Taymans committed
98 99
static GstElementStateReturn gst_bin_get_state (GstElement * element,
    GstElementState * state, GstElementState * pending, GTimeVal * timeout);
Erik Walthinsen's avatar
Erik Walthinsen committed
100

Wim Taymans's avatar
Wim Taymans committed
101 102 103
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
104
#ifndef GST_DISABLE_INDEX
Wim Taymans's avatar
Wim Taymans committed
105
static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
Wim Taymans's avatar
Wim Taymans committed
106
#endif
Wim Taymans's avatar
Wim Taymans committed
107 108
static GstClock *gst_bin_get_clock_func (GstElement * element);
static void gst_bin_set_clock_func (GstElement * element, GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
109

Wim Taymans's avatar
Wim Taymans committed
110
static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
111 112
static GstBusSyncReply bin_bus_handler (GstBus * bus,
    GstMessage * message, GstBin * bin);
113
static gboolean gst_bin_query (GstElement * element, GstQuery * query);
Erik Walthinsen's avatar
Erik Walthinsen committed
114

115
#ifndef GST_DISABLE_LOADSAVE
116 117
static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent);
static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self);
118
#endif
Erik Walthinsen's avatar
Erik Walthinsen committed
119

120 121
static gint bin_element_is_sink (GstElement * child, GstBin * bin);

Erik Walthinsen's avatar
Erik Walthinsen committed
122
/* Bin signals and args */
123 124
enum
{
Wim Taymans's avatar
Wim Taymans committed
125 126
  ELEMENT_ADDED,
  ELEMENT_REMOVED,
Erik Walthinsen's avatar
Erik Walthinsen committed
127 128 129
  LAST_SIGNAL
};

130 131
enum
{
132
  ARG_0
133
      /* FILL ME */
Erik Walthinsen's avatar
Erik Walthinsen committed
134 135
};

136 137 138
static void gst_bin_base_init (gpointer g_class);
static void gst_bin_class_init (GstBinClass * klass);
static void gst_bin_init (GstBin * bin);
139
static void gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data);
Erik Walthinsen's avatar
Erik Walthinsen committed
140 141 142 143

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

144 145 146 147 148
/**
 * gst_bin_get_type:
 *
 * Returns: the type of #GstBin
 */
149
GType
150
gst_bin_get_type (void)
151
{
152
  if (!_gst_bin_type) {
153
    static const GTypeInfo bin_info = {
154
      sizeof (GstBinClass),
155
      gst_bin_base_init,
156
      NULL,
157
      (GClassInitFunc) gst_bin_class_init,
158 159
      NULL,
      NULL,
160
      sizeof (GstBin),
161
      0,
162
      (GInstanceInitFunc) gst_bin_init,
163
      NULL
Erik Walthinsen's avatar
Erik Walthinsen committed
164
    };
165 166 167 168 169
    static const GInterfaceInfo child_proxy_info = {
      gst_bin_child_proxy_init,
      NULL,
      NULL
    };
170

171
    _gst_bin_type =
172
        g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
173

174 175 176
    g_type_add_interface_static (_gst_bin_type, GST_TYPE_CHILD_PROXY,
        &child_proxy_info);

177 178
    GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD,
        "debugging info for the 'bin' container element");
Erik Walthinsen's avatar
Erik Walthinsen committed
179
  }
180
  return _gst_bin_type;
Erik Walthinsen's avatar
Erik Walthinsen committed
181 182
}

183 184 185 186
static void
gst_bin_base_init (gpointer g_class)
{
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
187

188 189 190
  gst_element_class_set_details (gstelement_class, &gst_bin_details);
}

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
static GstObject *
gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
    guint index)
{
  return g_list_nth_data (GST_BIN (child_proxy)->children, index);
}

guint
gst_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
{
  return GST_BIN (child_proxy)->numchildren;
}

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
213
static void
214
gst_bin_class_init (GstBinClass * klass)
215
{
216
  GObjectClass *gobject_class;
217
  GstObjectClass *gstobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
218 219
  GstElementClass *gstelement_class;

220 221 222
  gobject_class = (GObjectClass *) klass;
  gstobject_class = (GstObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Erik Walthinsen's avatar
Erik Walthinsen committed
223

224
  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
Erik Walthinsen's avatar
Erik Walthinsen committed
225

226 227 228 229 230 231 232
  /**
   * GstBin::element-added:
   * @bin: the object which emitted the signal.
   * @element: the element that was added to the bin
   *
   * Will be emitted if a new element was removed/added to this bin.
   */
Wim Taymans's avatar
Wim Taymans committed
233
  gst_bin_signals[ELEMENT_ADDED] =
234 235 236
      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);
237 238 239 240 241 242 243
  /**
   * GstBin::element-removed:
   * @bin: the object which emitted the signal.
   * @element: the element that was removed from the bin
   *
   * Will be emitted if an element was removed from this bin.
   */
Wim Taymans's avatar
Wim Taymans committed
244
  gst_bin_signals[ELEMENT_REMOVED] =
245 246 247
      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);
248

249
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose);
250

251
#ifndef GST_DISABLE_LOADSAVE
252 253 254
  gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_bin_save_thyself);
  gstobject_class->restore_thyself =
      GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
255
#endif
256

257
  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state);
Wim Taymans's avatar
Wim Taymans committed
258
  gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state);
Wim Taymans's avatar
Wim Taymans committed
259
#ifndef GST_DISABLE_INDEX
Wim Taymans's avatar
Wim Taymans committed
260
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
Wim Taymans's avatar
Wim Taymans committed
261
#endif
Wim Taymans's avatar
Wim Taymans committed
262 263
  gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_bin_get_clock_func);
  gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
264

Wim Taymans's avatar
Wim Taymans committed
265
  gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
266
  gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query);
Wim Taymans's avatar
Wim Taymans committed
267

268 269
  klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
  klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
270 271
}

272
static void
273
gst_bin_init (GstBin * bin)
274
{
275 276
  GstBus *bus;

Erik Walthinsen's avatar
Erik Walthinsen committed
277 278
  bin->numchildren = 0;
  bin->children = NULL;
279
  bin->children_cookie = 0;
280 281
  bin->eosed = NULL;

282
  /* Set up a bus for listening to child elements,
283 284 285 286 287 288 289 290 291
   * and one for sending messages up the hierarchy */
  bus = g_object_new (gst_bus_get_type (), NULL);
  bin->child_bus = bus;
  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin);

  bus = g_object_new (gst_bus_get_type (), NULL);
  gst_element_set_bus (GST_ELEMENT (bin), bus);
  /* set_bus refs the bus via gst_object_replace, we drop our ref */
  gst_object_unref (bus);
Erik Walthinsen's avatar
Erik Walthinsen committed
292 293 294 295 296 297 298 299 300 301
}

/**
 * gst_bin_new:
 * @name: name of new bin
 *
 * Create a new bin with given name.
 *
 * Returns: new bin
 */
302 303
GstElement *
gst_bin_new (const gchar * name)
304
{
305
  return gst_element_factory_make ("bin", name);
Erik Walthinsen's avatar
Erik Walthinsen committed
306 307
}

Wim Taymans's avatar
Wim Taymans committed
308 309
/* set the index on all elements in this bin
 *
310
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
311 312 313 314
 */
#ifndef GST_DISABLE_INDEX
static void
gst_bin_set_index_func (GstElement * element, GstIndex * index)
Wim Taymans's avatar
Wim Taymans committed
315
{
Wim Taymans's avatar
Wim Taymans committed
316 317
  GstBin *bin;
  GList *children;
Wim Taymans's avatar
Wim Taymans committed
318

Wim Taymans's avatar
Wim Taymans committed
319
  bin = GST_BIN (element);
Wim Taymans's avatar
Wim Taymans committed
320

Wim Taymans's avatar
Wim Taymans committed
321 322 323 324 325 326 327
  GST_LOCK (bin);
  for (children = bin->children; children; children = g_list_next (children)) {
    GstElement *child = GST_ELEMENT (children->data);

    gst_element_set_index (child, index);
  }
  GST_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
328
}
Wim Taymans's avatar
Wim Taymans committed
329
#endif
Wim Taymans's avatar
Wim Taymans committed
330

Wim Taymans's avatar
Wim Taymans committed
331
/* set the clock on all elements in this bin
Wim Taymans's avatar
Wim Taymans committed
332
 *
333
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
334
 */
Wim Taymans's avatar
Wim Taymans committed
335 336
static void
gst_bin_set_clock_func (GstElement * element, GstClock * clock)
337
{
Wim Taymans's avatar
Wim Taymans committed
338 339
  GList *children;
  GstBin *bin;
340

Wim Taymans's avatar
Wim Taymans committed
341
  bin = GST_BIN (element);
342

Wim Taymans's avatar
Wim Taymans committed
343 344 345
  GST_LOCK (bin);
  for (children = bin->children; children; children = g_list_next (children)) {
    GstElement *child = GST_ELEMENT (children->data);
346

Wim Taymans's avatar
Wim Taymans committed
347 348 349
    gst_element_set_clock (child, clock);
  }
  GST_UNLOCK (bin);
350 351
}

Wim Taymans's avatar
Wim Taymans committed
352
/* get the clock for this bin by asking all of the children in this bin
Wim Taymans's avatar
Wim Taymans committed
353
 *
354
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
355
 */
Wim Taymans's avatar
Wim Taymans committed
356 357
static GstClock *
gst_bin_get_clock_func (GstElement * element)
358
{
Wim Taymans's avatar
Wim Taymans committed
359
  GstClock *result = NULL;
360 361
  GstBin *bin;
  GList *children;
362

363 364 365 366 367
  bin = GST_BIN (element);

  GST_LOCK (bin);
  for (children = bin->children; children; children = g_list_next (children)) {
    GstElement *child = GST_ELEMENT (children->data);
Wim Taymans's avatar
Wim Taymans committed
368

Wim Taymans's avatar
Wim Taymans committed
369 370 371
    result = gst_element_get_clock (child);
    if (result)
      break;
372 373
  }
  GST_UNLOCK (bin);
Wim Taymans's avatar
Wim Taymans committed
374 375

  return result;
Wim Taymans's avatar
Wim Taymans committed
376 377
}

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
static gboolean
is_eos (GstBin * bin)
{
  GstIterator *sinks;
  gboolean result = TRUE;
  gboolean done = FALSE;

  sinks = gst_bin_iterate_sinks (bin);
  while (!done) {
    gpointer data;

    switch (gst_iterator_next (sinks, &data)) {
      case GST_ITERATOR_OK:
      {
        GstElement *element = GST_ELEMENT (data);
        GList *eosed;
        gchar *name;

        name = gst_element_get_name (element);
        eosed = g_list_find (bin->eosed, element);
        if (!eosed) {
          GST_DEBUG ("element %s did not post EOS yet", name);
          result = FALSE;
          done = TRUE;
        } else {
          GST_DEBUG ("element %s posted EOS", name);
        }
        g_free (name);
        gst_object_unref (element);
        break;
      }
      case GST_ITERATOR_RESYNC:
        result = TRUE;
        gst_iterator_resync (sinks);
        break;
      case GST_ITERATOR_DONE:
        done = TRUE;
        break;
      default:
        g_assert_not_reached ();
        break;
    }
  }
  gst_iterator_free (sinks);
  return result;
}

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
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);
}

440 441
/* add an element to this bin
 *
442
 * MT safe
Wim Taymans's avatar
Wim Taymans committed
443
 */
444
static gboolean
445
gst_bin_add_func (GstBin * bin, GstElement * element)
446
{
447
  gchar *elem_name;
448
  GstIterator *it;
449

450 451 452
  /* we obviously can't add ourself to ourself */
  if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
    goto adding_itself;
453

454
  /* get the element name to make sure it is unique in this bin. */
455 456 457
  GST_LOCK (element);
  elem_name = g_strdup (GST_ELEMENT_NAME (element));
  GST_UNLOCK (element);
458

459
  GST_LOCK (bin);
460

461 462
  /* 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
463 464 465
   * 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)))
466
    goto duplicate_name;
467

468
  /* set the element's parent and add the element to the bin's list of children */
Wim Taymans's avatar
Wim Taymans committed
469 470
  if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (element),
              GST_OBJECT_CAST (bin))))
471
    goto had_parent;
472

473
  /* if we add a sink we become a sink */
474 475 476
  if (GST_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK))
    GST_FLAG_SET (bin, GST_ELEMENT_IS_SINK);

477
  bin->children = g_list_prepend (bin->children, element);
Erik Walthinsen's avatar
Erik Walthinsen committed
478
  bin->numchildren++;
479
  bin->children_cookie++;
Erik Walthinsen's avatar
Erik Walthinsen committed
480

481
  /* distribute the bus */
482
  gst_element_set_bus (element, bin->child_bus);
Wim Taymans's avatar
Wim Taymans committed
483

484
  /* propagate the current base time and clock */
Wim Taymans's avatar
Wim Taymans committed
485
  gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time);
486
  gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
487 488

  GST_UNLOCK (bin);
489

490 491 492 493 494
  /* unlink all linked pads */
  it = gst_element_iterate_pads (element);
  gst_iterator_foreach (it, (GFunc) unlink_pads, element);
  gst_iterator_free (it);

495
  GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"",
496 497
      elem_name);
  g_free (elem_name);
Erik Walthinsen's avatar
Erik Walthinsen committed
498

Wim Taymans's avatar
Wim Taymans committed
499
  g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
500 501 502 503 504

  return TRUE;

  /* ERROR handling here */
adding_itself:
505 506 507 508 509 510
  {
    GST_LOCK (bin);
    g_warning ("Cannot add bin %s to itself", GST_ELEMENT_NAME (bin));
    GST_UNLOCK (bin);
    return FALSE;
  }
511
duplicate_name:
512 513 514 515 516 517 518
  {
    g_warning ("Name %s is not unique in bin %s, not adding",
        elem_name, GST_ELEMENT_NAME (bin));
    GST_UNLOCK (bin);
    g_free (elem_name);
    return FALSE;
  }
519
had_parent:
520 521 522 523 524 525
  {
    g_warning ("Element %s already has parent", elem_name);
    GST_UNLOCK (bin);
    g_free (elem_name);
    return FALSE;
  }
Erik Walthinsen's avatar
Erik Walthinsen committed
526 527
}

528

Erik Walthinsen's avatar
Erik Walthinsen committed
529
/**
530 531 532
 * gst_bin_add:
 * @bin: #GstBin to add element to
 * @element: #GstElement to add to bin
Erik Walthinsen's avatar
Erik Walthinsen committed
533
 *
534
 * Adds the given element to the bin.  Sets the element's parent, and thus
535
 * takes ownership of the element. An element can only be added to one bin.
536
 *
537 538 539
 * If the element's pads are linked to other pads, the pads will be unlinked
 * before the element is added to the bin.
 *
540 541
 * MT safe.
 *
542 543
 * Returns: TRUE if the element could be added, FALSE on wrong parameters or
 * the bin does not want to accept the element.
Erik Walthinsen's avatar
Erik Walthinsen committed
544
 */
545
gboolean
546
gst_bin_add (GstBin * bin, GstElement * element)
547
{
548
  GstBinClass *bclass;
549
  gboolean result;
550

551 552
  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
553 554 555

  bclass = GST_BIN_GET_CLASS (bin);

556 557 558 559 560 561 562 563 564 565
  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;

566
  /* ERROR handling */
567
no_function:
568 569 570 571 572
  {
    g_warning ("adding elements to bin %s is not supported",
        GST_ELEMENT_NAME (bin));
    return FALSE;
  }
573 574
}

575 576 577 578 579
/* remove an element from the bin
 *
 * MT safe
 */
static gboolean
580
gst_bin_remove_func (GstBin * bin, GstElement * element)
581
{
582
  gchar *elem_name;
583
  GstIterator *it;
584

585
  GST_LOCK (element);
586 587 588 589 590 591 592
  /* Check if the element is already being removed and immediately
   * return */
  if (G_UNLIKELY (GST_FLAG_IS_SET (element, GST_ELEMENT_UNPARENTING)))
    goto already_removing;

  GST_FLAG_SET (element, GST_ELEMENT_UNPARENTING);
  /* grab element name so we can print it */
593 594
  elem_name = g_strdup (GST_ELEMENT_NAME (element));
  GST_UNLOCK (element);
595

596 597 598 599 600
  /* unlink all linked pads */
  it = gst_element_iterate_pads (element);
  gst_iterator_foreach (it, (GFunc) unlink_pads, element);
  gst_iterator_free (it);

601
  GST_LOCK (bin);
602
  /* the element must be in the bin's list of children */
603 604
  if (G_UNLIKELY (g_list_find (bin->children, element) == NULL))
    goto not_in_bin;
605

606
  /* now remove the element from the list of elements */
607
  bin->children = g_list_remove (bin->children, element);
Erik Walthinsen's avatar
Erik Walthinsen committed
608
  bin->numchildren--;
609
  bin->children_cookie++;
610

611
  /* check if we removed a sink */
612 613 614 615 616 617 618 619 620 621 622
  if (GST_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK)) {
    GList *other_sink;

    /* check if we removed the last sink */
    other_sink = g_list_find_custom (bin->children,
        bin, (GCompareFunc) bin_element_is_sink);
    if (!other_sink) {
      /* yups, we're not a sink anymore */
      GST_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK);
    }
  }
623
  GST_UNLOCK (bin);
624

625
  GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"",
626 627 628
      elem_name);
  g_free (elem_name);

Wim Taymans's avatar
Wim Taymans committed
629
  gst_element_set_bus (element, NULL);
630

631 632 633 634 635 636 637 638
  /* unlock any waiters for the state change. It is possible that
   * we are waiting for an ASYNC state change on this element. The
   * element cannot be added to another bin yet as it is not yet
   * unparented. */
  GST_STATE_LOCK (element);
  GST_STATE_BROADCAST (element);
  GST_STATE_UNLOCK (element);

639
  /* we ref here because after the _unparent() the element can be disposed
640
   * and we still need it to reset the UNPARENTING flag and fire a signal. */
641
  gst_object_ref (element);
Wim Taymans's avatar
Wim Taymans committed
642
  gst_object_unparent (GST_OBJECT_CAST (element));
643

644 645 646 647
  GST_LOCK (element);
  GST_FLAG_UNSET (element, GST_ELEMENT_UNPARENTING);
  GST_UNLOCK (element);

Wim Taymans's avatar
Wim Taymans committed
648
  g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element);
649

Wim Taymans's avatar
Wim Taymans committed
650
  /* element is really out of our control now */
651
  gst_object_unref (element);
652 653 654

  return TRUE;

655
  /* ERROR handling */
656
not_in_bin:
657 658 659 660 661 662 663
  {
    g_warning ("Element %s is not in bin %s", elem_name,
        GST_ELEMENT_NAME (bin));
    GST_UNLOCK (bin);
    g_free (elem_name);
    return FALSE;
  }
664 665 666 667 668
already_removing:
  {
    GST_UNLOCK (element);
    return FALSE;
  }
Erik Walthinsen's avatar
Erik Walthinsen committed
669 670
}

671 672 673 674 675
/**
 * gst_bin_remove:
 * @bin: #GstBin to remove element from
 * @element: #GstElement to remove
 *
676 677 678 679 680
 * Remove the element from its associated bin, unparenting it as well.
 * Unparenting the element means that the element will be dereferenced,
 * so if the bin holds the only reference to the element, the element
 * will be freed in the process of removing it from the bin.  If you
 * want the element to still exist after removing, you need to call
681
 * #gst_object_ref before removing it from the bin.
682
 *
683 684 685
 * If the element's pads are linked to other pads, the pads will be unlinked
 * before the element is removed from the bin.
 *
686 687
 * MT safe.
 *
688 689
 * Returns: TRUE if the element could be removed, FALSE on wrong parameters or
 * the bin does not want to remove the element.
690
 */
691
gboolean
692
gst_bin_remove (GstBin * bin, GstElement * element)
693 694
{
  GstBinClass *bclass;
695
  gboolean result;
696

697 698
  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
699 700 701

  bclass = GST_BIN_GET_CLASS (bin);

702 703 704 705 706 707 708 709 710 711
  if (G_UNLIKELY (bclass->remove_element == NULL))
    goto no_function;

  GST_CAT_DEBUG (GST_CAT_PARENTAGE, "removing element %s from bin %s",
      GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));

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

  return result;

712
  /* ERROR handling */
713
no_function:
714 715 716 717 718
  {
    g_warning ("removing elements from bin %s is not supported",
        GST_ELEMENT_NAME (bin));
    return FALSE;
  }
719 720 721 722 723
}

static GstIteratorItem
iterate_child (GstIterator * it, GstElement * child)
{
724
  gst_object_ref (child);
725
  return GST_ITERATOR_ITEM_PASS;
726 727
}

728
/**
729 730 731
 * gst_bin_iterate_elements:
 * @bin: #Gstbin to iterate the elements of
 *
732 733 734 735 736
 * Get an iterator for the elements in this bin.
 * Each element will have its refcount increased, so unref
 * after use.
 *
 * MT safe.
737 738 739
 *
 * Returns: a #GstIterator of #GstElements. gst_iterator_free after
 * use. returns NULL when passing bad parameters.
740
 */
741 742
GstIterator *
gst_bin_iterate_elements (GstBin * bin)
743
{
744
  GstIterator *result;
745

746 747 748 749
  g_return_val_if_fail (GST_IS_BIN (bin), NULL);

  GST_LOCK (bin);
  /* add ref because the iterator refs the bin. When the iterator
750
   * is freed it will unref the bin again using the provided dispose
751
   * function. */
752
  gst_object_ref (bin);
753 754 755 756 757 758 759
  result = gst_iterator_new_list (GST_GET_LOCK (bin),
      &bin->children_cookie,
      &bin->children,
      bin,
      (GstIteratorItemFunction) iterate_child,
      (GstIteratorDisposeFunction) gst_object_unref);
  GST_UNLOCK (bin);
760

761 762
  return result;
}
763

764 765 766
static GstIteratorItem
iterate_child_recurse (GstIterator * it, GstElement * child)
{
767
  gst_object_ref (child);
768 769
  if (GST_IS_BIN (child)) {
    GstIterator *other = gst_bin_iterate_recurse (GST_BIN (child));
770

771
    gst_iterator_push (it, other);
772
  }
773 774
  return GST_ITERATOR_ITEM_PASS;
}
775

776 777 778 779
/**
 * gst_bin_iterate_recurse:
 * @bin: #Gstbin to iterate the elements of
 *
780 781 782 783 784
 * Get an iterator for the elements in this bin.
 * Each element will have its refcount increased, so unref
 * after use. This iterator recurses into GstBin children.
 *
 * MT safe.
785 786 787 788 789 790 791 792 793 794 795 796 797
 *
 * Returns: a #GstIterator of #GstElements. gst_iterator_free after
 * use. returns NULL when passing bad parameters.
 */
GstIterator *
gst_bin_iterate_recurse (GstBin * bin)
{
  GstIterator *result;

  g_return_val_if_fail (GST_IS_BIN (bin), NULL);

  GST_LOCK (bin);
  /* add ref because the iterator refs the bin. When the iterator
798
   * is freed it will unref the bin again using the provided dispose
799
   * function. */
800
  gst_object_ref (bin);
801 802 803 804 805 806 807 808 809
  result = gst_iterator_new_list (GST_GET_LOCK (bin),
      &bin->children_cookie,
      &bin->children,
      bin,
      (GstIteratorItemFunction) iterate_child_recurse,
      (GstIteratorDisposeFunction) gst_object_unref);
  GST_UNLOCK (bin);

  return result;
810 811
}

812
/* returns 0 when TRUE because this is a GCompareFunc */
813
/* MT safe */
Wim Taymans's avatar
Wim Taymans committed
814 815 816
static gint
bin_element_is_sink (GstElement * child, GstBin * bin)
{
817 818
  gboolean is_sink;

Wim Taymans's avatar
Wim Taymans committed
819
  /* we lock the child here for the remainder of the function to
820
   * get its name safely. */
Wim Taymans's avatar
Wim Taymans committed
821
  GST_LOCK (child);
822 823 824 825 826
  is_sink = GST_FLAG_IS_SET (child, GST_ELEMENT_IS_SINK);

  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
      "child %s %s sink", GST_OBJECT_NAME (child), is_sink ? "is" : "is not");

827
  GST_UNLOCK (child);
828 829 830
  return is_sink ? 0 : 1;
}

831 832 833
/* check if object has the given ancestor somewhere up in
 * the hierarchy
 */
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
static gboolean
has_ancestor (GstObject * object, GstObject * ancestor)
{
  GstObject *parent;
  gboolean result = FALSE;

  if (object == NULL)
    return FALSE;

  if (object == ancestor)
    return TRUE;

  parent = gst_object_get_parent (object);
  result = has_ancestor (parent, ancestor);
  if (parent)
849
    gst_object_unref (parent);
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870

  return result;
}

/* returns 0 when TRUE because this is a GCompareFunc.
 * This function returns elements that have no connected srcpads and
 * are therefore not reachable from a real sink. */
/* MT safe */
static gint
bin_element_is_semi_sink (GstElement * child, GstBin * bin)
{
  int ret = 1;

  /* we lock the child here for the remainder of the function to
   * get its pads and name safely. */
  GST_LOCK (child);

  /* check if this is a sink element, these are the elements
   * without (linked) source pads. */
  if (child->numsrcpads == 0) {
    /* shortcut */
Wim Taymans's avatar
Wim Taymans committed
871
    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
872 873
        "adding child %s as sink", GST_OBJECT_NAME (child));
    ret = 0;
Wim Taymans's avatar
Wim Taymans committed
874
  } else {
875 876 877 878 879 880 881 882
    /* loop over all pads, try to figure out if this element
     * is a semi sink because it has no linked source pads */
    GList *pads;
    gboolean connected_src = FALSE;

    for (pads = child->srcpads; pads; pads = g_list_next (pads)) {
      GstPad *peer;

883
      GST_DEBUG ("looking at pad %p", pads->data);
884 885 886
      if ((peer = gst_pad_get_peer (GST_PAD_CAST (pads->data)))) {
        connected_src =
            has_ancestor (GST_OBJECT_CAST (peer), GST_OBJECT_CAST (bin));
887
        gst_object_unref (peer);
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
        if (connected_src) {
          break;
        }
      }
    }
    if (connected_src) {
      GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
          "not adding child %s as sink: linked source pads",
          GST_OBJECT_NAME (child));
    } else {
      GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
          "adding child %s as sink since it has unlinked source pads in this bin",
          GST_OBJECT_NAME (child));
      ret = 0;
    }
903
  }
904 905 906
  GST_UNLOCK (child);

  return ret;
907 908 909 910 911
}

static gint
sink_iterator_filter (GstElement * child, GstBin * bin)
{
912
  if (bin_element_is_sink (child, bin) == 0) {
913 914 915 916 917 918 919
    /* returns 0 because this is a GCompareFunc */
    return 0;
  } else {
    /* child carries a ref from gst_bin_iterate_elements -- drop if not passing
       through */
    gst_object_unref ((GstObject *) child);
    return 1;
Wim Taymans's avatar
Wim Taymans committed
920 921 922 923 924 925 926 927
  }
}

/**
 * gst_bin_iterate_sinks:
 * @bin: #Gstbin to iterate on
 *
 * Get an iterator for the sink elements in this bin.
928 929
 * Each element will have its refcount increased, so unref
 * after use.
Wim Taymans's avatar
Wim Taymans committed
930
 *
Wim Taymans's avatar
Wim Taymans committed
931 932
 * The sink elements are those without any linked srcpads.
 *
Wim Taymans's avatar
Wim Taymans committed
933
 * MT safe.
934 935
 *
 * Returns: a #GstIterator of #GstElements. gst_iterator_free after use.
Wim Taymans's avatar
Wim Taymans committed
936 937 938 939 940 941 942 943 944 945 946
 */
GstIterator *
gst_bin_iterate_sinks (GstBin * bin)
{
  GstIterator *children;
  GstIterator *result;

  g_return_val_if_fail (GST_IS_BIN (bin), NULL);

  children = gst_bin_iterate_elements (bin);
  result = gst_iterator_filter (children,
947
      (GCompareFunc) sink_iterator_filter, bin);
Wim Taymans's avatar
Wim Taymans committed
948 949 950