gstghostpad.c 29.6 KB
Newer Older
1 2 3 4
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
 *                    2005 Andy Wingo <wingo@pobox.com>
5
 *		      2006 Edward Hervey <bilboed@bilboed.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * gstghostpad.c: Proxy pads
 *
 * 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.
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
24

Stefan Kost's avatar
Stefan Kost committed
25 26 27 28 29
/**
 * SECTION:gstghostpad
 * @short_description: Pseudo link pads
 * @see_also: #GstPad
 *
Stefan Kost's avatar
Stefan Kost committed
30 31 32 33 34
 * GhostPads are useful when organizing pipelines with #GstBin like elements.
 * The idea here is to create hierarchical element graphs. The bin element
 * contains a sub-graph. Now one would like to treat the bin-element like other
 * #GstElements. This is where GhostPads come into play. A GhostPad acts as a
 * proxy for another pad. Thus the bin can have sink and source ghost-pads that
35
 * are associated with sink and source pads of the child elements.
Stefan Kost's avatar
Stefan Kost committed
36 37
 *
 * If the target pad is known at creation time, gst_ghost_pad_new() is the
38
 * function to use to get a ghost-pad. Otherwise one can use gst_ghost_pad_new_no_target()
Stefan Kost's avatar
Stefan Kost committed
39
 * to create the ghost-pad and use gst_ghost_pad_set_target() to establish the
40
 * association later on.
Stefan Kost's avatar
Stefan Kost committed
41
 *
Edward Hervey's avatar
Edward Hervey committed
42 43
 * Note that GhostPads add overhead to the data processing of a pipeline.
 *
Stefan Kost's avatar
Stefan Kost committed
44
 * Last reviewed on 2005-11-18 (0.9.5)
Stefan Kost's avatar
Stefan Kost committed
45
 */
46 47

#include "gst_private.h"
48
#include "gstinfo.h"
49 50 51

#include "gstghostpad.h"

52 53
#define GST_CAT_DEFAULT GST_CAT_PADS

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54 55 56 57 58
#define GST_TYPE_PROXY_PAD              (gst_proxy_pad_get_type ())
#define GST_IS_PROXY_PAD(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PROXY_PAD))
#define GST_IS_PROXY_PAD_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PROXY_PAD))
#define GST_PROXY_PAD(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PROXY_PAD, GstProxyPad))
#define GST_PROXY_PAD_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PROXY_PAD, GstProxyPadClass))
Edward Hervey's avatar
Edward Hervey committed
59 60
#define GST_PROXY_PAD_CAST(obj)         ((GstProxyPad *)obj)

61 62
#define GST_PROXY_PAD_TARGET(pad)       (GST_PROXY_PAD_CAST (pad)->target)
#define GST_PROXY_PAD_INTERNAL(pad)     (GST_PROXY_PAD_CAST (pad)->internal)
63 64 65 66

typedef struct _GstProxyPad GstProxyPad;
typedef struct _GstProxyPadClass GstProxyPadClass;

67
#define GST_PROXY_GET_LOCK(pad) (GST_PROXY_PAD_CAST (pad)->proxy_lock)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68 69
#define GST_PROXY_LOCK(pad)     (g_mutex_lock (GST_PROXY_GET_LOCK (pad)))
#define GST_PROXY_UNLOCK(pad)   (g_mutex_unlock (GST_PROXY_GET_LOCK (pad)))
70 71 72 73 74

struct _GstProxyPad
{
  GstPad pad;

75 76
  /* with PROXY_LOCK */
  GMutex *proxy_lock;
77
  GstPad *target;
78
  GstPad *internal;
79 80 81 82 83 84 85 86 87 88 89 90 91
};

struct _GstProxyPadClass
{
  GstPadClass parent_class;

  /*< private > */
  gpointer _gst_reserved[1];
};


G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);

92
static GstPad *gst_proxy_pad_get_target (GstPad * pad);
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

static void gst_proxy_pad_dispose (GObject * object);
static void gst_proxy_pad_finalize (GObject * object);

#ifndef GST_DISABLE_LOADSAVE
static xmlNodePtr gst_proxy_pad_save_thyself (GstObject * object,
    xmlNodePtr parent);
#endif


static void
gst_proxy_pad_class_init (GstProxyPadClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;

  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_proxy_pad_dispose);
  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_proxy_pad_finalize);

#ifndef GST_DISABLE_LOADSAVE
  {
    GstObjectClass *gstobject_class = (GstObjectClass *) klass;

    gstobject_class->save_thyself =
        GST_DEBUG_FUNCPTR (gst_proxy_pad_save_thyself);
  }
#endif
}

const GstQueryType *
gst_proxy_pad_do_query_type (GstPad * pad)
{
124
  GstPad *target = gst_proxy_pad_get_target (pad);
125
  const GstQueryType *res = NULL;
126

127 128 129 130
  if (target) {
    res = gst_pad_get_query_types (target);
    gst_object_unref (target);
  }
131
  return res;
132 133 134 135 136
}

static gboolean
gst_proxy_pad_do_event (GstPad * pad, GstEvent * event)
{
137
  gboolean res = FALSE;
138
  GstPad *internal = GST_PROXY_PAD_INTERNAL (pad);
139

140
  res = gst_pad_push_event (internal, event);
141 142

  return res;
143 144 145 146 147
}

static gboolean
gst_proxy_pad_do_query (GstPad * pad, GstQuery * query)
{
148
  gboolean res = FALSE;
149
  GstPad *target = gst_proxy_pad_get_target (pad);
150

151 152 153 154
  if (target) {
    res = gst_pad_query (target, query);
    gst_object_unref (target);
  }
155 156

  return res;
157 158 159 160 161
}

static GList *
gst_proxy_pad_do_internal_link (GstPad * pad)
{
162
  GList *res = NULL;
163
  GstPad *target = gst_proxy_pad_get_target (pad);
164

165 166 167 168
  if (target) {
    res = gst_pad_get_internal_links (target);
    gst_object_unref (target);
  }
169 170

  return res;
171 172 173 174 175 176
}

static GstFlowReturn
gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size,
    GstCaps * caps, GstBuffer ** buf)
{
177
  GstFlowReturn result;
178
  GstPad *internal = GST_PROXY_PAD_INTERNAL (pad);
179

180
  result = gst_pad_alloc_buffer (internal, offset, size, caps, buf);
181

182
  return result;
183 184 185 186 187
}

static GstFlowReturn
gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
{
188
  GstFlowReturn res;
189
  GstPad *internal = GST_PROXY_PAD_INTERNAL (pad);
190

191
  res = gst_pad_push (internal, buffer);
192 193

  return res;
194 195 196 197 198 199
}

static GstFlowReturn
gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
    GstBuffer ** buffer)
{
200
  GstFlowReturn res;
201
  GstPad *internal = GST_PROXY_PAD_INTERNAL (pad);
202

203
  res = gst_pad_pull_range (internal, offset, size, buffer);
204 205

  return res;
206 207 208 209 210
}

static gboolean
gst_proxy_pad_do_checkgetrange (GstPad * pad)
{
211
  gboolean result;
212
  GstPad *internal = GST_PROXY_PAD_INTERNAL (pad);
213

214
  result = gst_pad_check_pull_range (internal);
215

216
  return result;
217 218 219 220 221
}

static GstCaps *
gst_proxy_pad_do_getcaps (GstPad * pad)
{
222 223
  GstPad *target = gst_proxy_pad_get_target (pad);
  GstCaps *res;
224
  GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
225

226
  if (target) {
Edward Hervey's avatar
Edward Hervey committed
227
    /* if we have a real target, proxy the call */
228 229
    res = gst_pad_get_caps (target);
    gst_object_unref (target);
Edward Hervey's avatar
Edward Hervey committed
230

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    GST_DEBUG_OBJECT (pad, "get caps of target: %" GST_PTR_FORMAT, res);

    /* filter against the template */
    if (templ && res) {
      GstCaps *filt, *tmp;

      filt = GST_PAD_TEMPLATE_CAPS (templ);
      if (filt) {
        tmp = gst_caps_intersect (filt, res);
        gst_caps_unref (res);
        res = tmp;
        GST_DEBUG_OBJECT (pad,
            "filtered against template gives %" GST_PTR_FORMAT, res);
      }
    }
  } else {
    /* else, if we have a template, use its caps. */
Edward Hervey's avatar
Edward Hervey committed
248 249 250 251 252 253 254 255 256 257 258
    if (templ) {
      res = GST_PAD_TEMPLATE_CAPS (templ);
      GST_DEBUG_OBJECT (pad,
          "using pad template %p with caps %p %" GST_PTR_FORMAT, templ, res,
          res);
      res = gst_caps_ref (res);
      goto done;
    }

    /* last resort, any caps */
    GST_DEBUG_OBJECT (pad, "pad has no template, returning ANY");
259 260
    res = gst_caps_new_any ();
  }
Edward Hervey's avatar
Edward Hervey committed
261 262

done:
263
  return res;
264 265 266 267 268
}

static gboolean
gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
{
269
  GstPad *target = gst_proxy_pad_get_target (pad);
270
  gboolean res;
271

272 273 274
  if (target) {
    res = gst_pad_accept_caps (target, caps);
    gst_object_unref (target);
275 276 277 278
  } else {
    /* We don't have a target, we return TRUE and we assume that any future
     * target will be able to deal with any configured caps. */
    res = TRUE;
279
  }
280 281

  return res;
282 283
}

284
static void
285 286
gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
{
287
  GstPad *target = gst_proxy_pad_get_target (pad);
288

289 290 291 292
  if (target) {
    gst_pad_fixate_caps (target, caps);
    gst_object_unref (target);
  }
293 294 295 296 297
}

static gboolean
gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
{
298 299
  GstPad *target = gst_proxy_pad_get_target (pad);
  gboolean res;
300

301 302 303 304
  if (target) {
    res = gst_pad_set_caps (target, caps);
    gst_object_unref (target);
  } else {
Edward Hervey's avatar
Edward Hervey committed
305 306 307
    /* We don't have any target, but we shouldn't return FALSE since this
     * would stop the actual push of a buffer (which might trigger a pad block
     * or probe, or properly return GST_FLOW_NOT_LINKED.
308 309 310
     */
    res = TRUE;
  }
311
  return res;
312 313
}

314
static gboolean
315
gst_proxy_pad_set_target_unlocked (GstPad * pad, GstPad * target)
316
{
317 318
  GstPad *oldtarget;

319 320
  if (target) {
    GST_LOG_OBJECT (pad, "setting target %s:%s", GST_DEBUG_PAD_NAME (target));
Edward Hervey's avatar
Edward Hervey committed
321 322 323

    if (G_UNLIKELY (GST_PAD_DIRECTION (pad) != GST_PAD_DIRECTION (target)))
      goto wrong_direction;
324 325
  } else
    GST_LOG_OBJECT (pad, "clearing target");
326

327
  /* clear old target */
328
  if ((oldtarget = GST_PROXY_PAD_TARGET (pad))) {
329
    gst_object_unref (oldtarget);
330
  }
331 332
  /* set and ref new target if any */
  if (target)
333
    GST_PROXY_PAD_TARGET (pad) = gst_object_ref (target);
334 335
  else
    GST_PROXY_PAD_TARGET (pad) = NULL;
336

337
  return TRUE;
Edward Hervey's avatar
Edward Hervey committed
338

339
  /* ERRORS */
Edward Hervey's avatar
Edward Hervey committed
340 341 342 343 344 345
wrong_direction:
  {
    GST_ERROR_OBJECT (pad,
        "target pad doesn't have the same direction as ourself");
    return FALSE;
  }
346 347 348 349 350 351 352 353 354
}

static gboolean
gst_proxy_pad_set_target (GstPad * pad, GstPad * target)
{
  gboolean result;

  GST_PROXY_LOCK (pad);
  result = gst_proxy_pad_set_target_unlocked (pad, target);
355 356
  GST_PROXY_UNLOCK (pad);

357
  return result;
358 359
}

360 361 362 363 364 365 366
static GstPad *
gst_proxy_pad_get_target (GstPad * pad)
{
  GstPad *target;

  GST_PROXY_LOCK (pad);
  target = GST_PROXY_PAD_TARGET (pad);
367 368
  if (target)
    gst_object_ref (target);
369 370 371
  GST_PROXY_UNLOCK (pad);

  return target;
372 373 374 375 376 377
}

static void
gst_proxy_pad_dispose (GObject * object)
{
  GstPad *pad = GST_PAD (object);
378
  GstPad **target_p;
379

380
  GST_PROXY_LOCK (pad);
381
  /* remove and unref the target */
382 383
  target_p = &GST_PROXY_PAD_TARGET (pad);
  gst_object_replace ((GstObject **) target_p, NULL);
Edward Hervey's avatar
Edward Hervey committed
384 385
  /* The internal is only cleared by GstGhostPad::dispose, since it is the 
   * parent of non-ghost GstProxyPad and owns the refcount on the internal.
386
   */
387
  GST_PROXY_UNLOCK (pad);
388 389 390 391 392 393 394 395 396

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

static void
gst_proxy_pad_finalize (GObject * object)
{
  GstProxyPad *pad = GST_PROXY_PAD (object);

397 398
  g_mutex_free (pad->proxy_lock);
  pad->proxy_lock = NULL;
399 400 401 402

  G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
}

403 404 405 406 407 408 409 410 411 412 413 414 415
static void
gst_proxy_pad_init (GstProxyPad * ppad)
{
  GstPad *pad = (GstPad *) ppad;

  ppad->proxy_lock = g_mutex_new ();

  gst_pad_set_query_type_function (pad,
      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query_type));
  gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_event));
  gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query));
  gst_pad_set_internal_link_function (pad,
      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_internal_link));
416

417 418 419 420 421 422 423 424 425 426
  gst_pad_set_getcaps_function (pad,
      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getcaps));
  gst_pad_set_acceptcaps_function (pad,
      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_acceptcaps));
  gst_pad_set_fixatecaps_function (pad,
      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_fixatecaps));
  gst_pad_set_setcaps_function (pad,
      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_setcaps));
}

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
#ifndef GST_DISABLE_LOADSAVE
/**
 * gst_proxy_pad_save_thyself:
 * @pad: a ghost #GstPad to save.
 * @parent: the parent #xmlNodePtr to save the description in.
 *
 * Saves the ghost pad into an xml representation.
 *
 * Returns: the #xmlNodePtr representation of the pad.
 */
static xmlNodePtr
gst_proxy_pad_save_thyself (GstObject * object, xmlNodePtr parent)
{
  xmlNodePtr self;

  g_return_val_if_fail (GST_IS_PROXY_PAD (object), NULL);

  self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
  xmlNewChild (self, NULL, (xmlChar *) "name",
      (xmlChar *) GST_OBJECT_NAME (object));
  xmlNewChild (self, NULL, (xmlChar *) "parent",
      (xmlChar *) GST_OBJECT_NAME (GST_OBJECT_PARENT (object)));

  /* FIXME FIXME FIXME! */

  return self;
}
#endif /* GST_DISABLE_LOADSAVE */


/***********************************************************************
 * Ghost pads, implemented as a pair of proxy pads (sort of)
 */


struct _GstGhostPad
{
  GstProxyPad pad;

466
  /* with PROXY_LOCK */
467
  gulong notify_id;
468 469

  /*< private > */
470
  gpointer _gst_reserved[GST_PADDING];
471 472 473 474 475 476 477
};

struct _GstGhostPadClass
{
  GstProxyPadClass parent_class;

  /*< private > */
478
  gpointer _gst_reserved[GST_PADDING];
479 480 481 482 483 484 485
};


G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);

static void gst_ghost_pad_dispose (GObject * object);

486 487 488 489 490 491 492 493
/*
 * The parent_set and parent_unset are used to make sure that:
 * _ the internal and target are only linked when the GhostPad has a parent,
 * _ the internal and target are unlinked as soon as the GhostPad is removed
 *    from it's parent.
 */
static void
gst_ghost_pad_parent_set (GstObject * object, GstObject * parent)
494
{
495 496 497 498
  GstPad *gpad;
  GstPad *target;
  GstPad *internal;
  GstPadLinkReturn ret;
499
  GstObjectClass *klass;
500 501 502 503

  gpad = GST_PAD (object);

  /* internal is never NULL */
504
  internal = GST_PROXY_PAD_INTERNAL (gpad);
505
  target = gst_proxy_pad_get_target (gpad);
506

507 508
  if (target) {
    if (GST_PAD_IS_SRC (internal))
509
      ret = gst_pad_link (internal, target);
510
    else
511
      ret = gst_pad_link (target, internal);
512

513
    gst_object_unref (target);
514 515 516

    if (ret != GST_PAD_LINK_OK)
      goto link_failed;
517
  }
518

519 520 521 522
  klass = GST_OBJECT_CLASS (gst_ghost_pad_parent_class);

  if (klass->parent_set)
    klass->parent_set (object, parent);
523 524 525 526 527 528 529

  return;

  /* ERRORS */
link_failed:
  {
    /* a warning is all we can do */
530 531 532 533
    GST_WARNING_OBJECT (gpad, "could not link internal ghostpad %s:%s",
        GST_DEBUG_PAD_NAME (target));
    g_warning ("could not link internal ghostpad %s:%s",
        GST_DEBUG_PAD_NAME (target));
534 535
    return;
  }
536 537
}

538 539 540
static void
gst_ghost_pad_parent_unset (GstObject * object, GstObject * parent)
{
541 542 543
  GstPad *gpad;
  GstPad *target;
  GstPad *internal;
544
  GstObjectClass *klass;
545 546

  gpad = GST_PAD (object);
547
  internal = GST_PROXY_PAD_INTERNAL (gpad);
548
  target = gst_proxy_pad_get_target (gpad);
549 550 551 552 553 554

  if (target) {
    if (GST_PAD_IS_SRC (internal))
      gst_pad_unlink (internal, target);
    else
      gst_pad_unlink (target, internal);
555

556 557 558
    gst_object_unref (target);
  }

559 560 561 562
  klass = GST_OBJECT_CLASS (gst_ghost_pad_parent_class);

  if (klass->parent_unset)
    klass->parent_unset (object, parent);
563 564 565
}


566 567 568 569
static void
gst_ghost_pad_class_init (GstGhostPadClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
570
  GstObjectClass *gstobject_class = (GstObjectClass *) klass;
571 572

  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
573 574 575
  gstobject_class->parent_set = GST_DEBUG_FUNCPTR (gst_ghost_pad_parent_set);
  gstobject_class->parent_unset =
      GST_DEBUG_FUNCPTR (gst_ghost_pad_parent_unset);
576 577
}

578
/* see gstghostpad design docs */
579
static gboolean
580
gst_ghost_pad_internal_do_activate_push (GstPad * pad, gboolean active)
581
{
582
  gboolean ret;
583
  GstPad *other;
584

585 586
  GST_LOG_OBJECT (pad, "%sactivate push on %s:%s", (active ? "" : "de"),
      GST_DEBUG_PAD_NAME (pad));
587

588 589 590 591
  if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
    other = GST_PROXY_PAD_INTERNAL (pad);
    ret = gst_pad_activate_push (other, active);
  } else if (G_LIKELY ((other = gst_pad_get_peer (pad)))) {
592 593 594 595
    ret = gst_pad_activate_push (other, active);
    gst_object_unref (other);
  } else
    ret = FALSE;
596

597 598
  return ret;
}
599

600 601 602 603
static gboolean
gst_ghost_pad_internal_do_activate_pull (GstPad * pad, gboolean active)
{
  gboolean ret;
604
  GstPad *other;
605

606 607
  GST_LOG_OBJECT (pad, "%sactivate pull on %s:%s", (active ? "" : "de"),
      GST_DEBUG_PAD_NAME (pad));
608

609 610 611 612
  if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
    other = GST_PROXY_PAD_INTERNAL (pad);
    ret = gst_pad_activate_pull (other, active);
  } else if (G_LIKELY ((other = gst_pad_get_peer (pad)))) {
613 614 615 616
    ret = gst_pad_activate_pull (other, active);
    gst_object_unref (other);
  } else
    ret = FALSE;
617 618 619 620

  return ret;
}

621 622 623 624
static gboolean
gst_ghost_pad_do_activate_push (GstPad * pad, gboolean active)
{
  gboolean ret;
625
  GstPad *other;
626

627 628
  GST_LOG_OBJECT (pad, "%sactivate push on %s:%s", (active ? "" : "de"),
      GST_DEBUG_PAD_NAME (pad));
629

630
  if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
631 632 633 634
    other = GST_PROXY_PAD_INTERNAL (pad);
    ret = gst_pad_activate_push (other, active);
  } else
    ret = TRUE;
635

636 637 638 639 640 641 642
  return ret;
}

static gboolean
gst_ghost_pad_do_activate_pull (GstPad * pad, gboolean active)
{
  gboolean ret;
643
  GstPad *other;
644

645 646
  GST_LOG_OBJECT (pad, "%sactivate pull on %s:%s", (active ? "" : "de"),
      GST_DEBUG_PAD_NAME (pad));
647

648 649 650 651
  if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
    other = GST_PROXY_PAD_INTERNAL (pad);
    ret = gst_pad_activate_pull (other, active);
  } else if (G_LIKELY ((other = gst_pad_get_peer (pad)))) {
652 653 654 655
    ret = gst_pad_activate_pull (other, active);
    gst_object_unref (other);
  } else
    ret = FALSE;
656 657 658 659

  return ret;
}

660 661 662
static GstPadLinkReturn
gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
{
663 664
  GstPadLinkReturn ret;
  GstPad *internal;
665

666
  GST_DEBUG_OBJECT (pad, "linking ghostpad");
667

668
  internal = GST_PROXY_PAD_INTERNAL (pad);
669
  if (!gst_proxy_pad_set_target (internal, peer))
670
    goto target_failed;
671

672
  ret = GST_PAD_LINK_OK;
673 674 675
  /* if we are a source pad, we should call the peer link function
   * if the peer has one */
  if (GST_PAD_IS_SRC (pad)) {
676
    if (GST_PAD_LINKFUNC (peer)) {
677
      ret = GST_PAD_LINKFUNC (peer) (peer, pad);
678 679 680
      if (ret != GST_PAD_LINK_OK)
        goto link_failed;
    }
681
  }
682
  return ret;
683 684

  /* ERRORS */
685 686 687 688 689 690
target_failed:
  {
    GST_DEBUG_OBJECT (pad, "setting target failed");
    return GST_PAD_LINK_REFUSED;
  }
link_failed:
691
  {
692 693 694 695
    GST_DEBUG_OBJECT (pad, "linking failed");
    /* clear target again */
    gst_proxy_pad_set_target (internal, NULL);
    return ret;
696
  }
697 698 699 700 701
}

static void
gst_ghost_pad_do_unlink (GstPad * pad)
{
702 703 704 705
  GstPad *target;
  GstPad *internal;

  target = gst_proxy_pad_get_target (pad);
706
  internal = GST_PROXY_PAD_INTERNAL (pad);
707

708 709
  GST_DEBUG_OBJECT (pad, "unlinking ghostpad");

710 711 712
  /* The target of the internal pad is no longer valid */
  gst_proxy_pad_set_target (internal, NULL);

713 714 715
  if (target) {
    if (GST_PAD_UNLINKFUNC (target))
      GST_PAD_UNLINKFUNC (target) (target);
716

717
    gst_object_unref (target);
718
  }
719 720
}

721 722 723 724 725 726 727
static void
on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
{
  GstCaps *caps;

  g_object_get (internal, "caps", &caps, NULL);

728
  GST_OBJECT_LOCK (pad);
729
  gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
730
  GST_OBJECT_UNLOCK (pad);
731 732 733 734 735 736

  g_object_notify (G_OBJECT (pad), "caps");
  if (caps)
    gst_caps_unref (caps);
}

737 738 739
static void
gst_ghost_pad_init (GstGhostPad * pad)
{
740
  gst_pad_set_activatepull_function (GST_PAD_CAST (pad),
741
      GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_pull));
742
  gst_pad_set_activatepush_function (GST_PAD_CAST (pad),
743
      GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_push));
744 745 746 747 748
}

static void
gst_ghost_pad_dispose (GObject * object)
{
749
  GstPad *pad;
750 751 752
  GstPad *internal;
  GstPad *intpeer;

753 754 755 756
  pad = GST_PAD (object);

  GST_DEBUG_OBJECT (pad, "dispose");

757 758 759 760 761 762
  GST_PROXY_LOCK (pad);
  internal = GST_PROXY_PAD_INTERNAL (pad);

  gst_pad_set_activatepull_function (internal, NULL);
  gst_pad_set_activatepush_function (internal, NULL);

763
  g_signal_handler_disconnect (internal, GST_GHOST_PAD_CAST (pad)->notify_id);
764 765 766 767 768 769 770

  intpeer = gst_pad_get_peer (internal);
  if (intpeer) {
    if (GST_PAD_IS_SRC (internal))
      gst_pad_unlink (internal, intpeer);
    else
      gst_pad_unlink (intpeer, internal);
771

772 773 774 775
    gst_object_unref (intpeer);
  }

  GST_PROXY_PAD_INTERNAL (internal) = NULL;
776 777 778

  /* disposes of the internal pad, since the ghostpad is the only possible object
   * that has a refcount on the internal pad.
779 780 781 782
   */
  gst_object_unparent (GST_OBJECT_CAST (internal));

  GST_PROXY_UNLOCK (pad);
783 784 785 786

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

Edward Hervey's avatar
Edward Hervey committed
787 788 789
static GstPad *
gst_ghost_pad_new_full (const gchar * name, GstPadDirection dir,
    GstPadTemplate * templ)
790 791
{
  GstPad *ret;
792
  GstPad *internal;
793
  GstPadDirection otherdir;
794

Edward Hervey's avatar
Edward Hervey committed
795
  g_return_val_if_fail (dir != GST_PAD_UNKNOWN, NULL);
796 797

  /* OBJECT CREATION */
Edward Hervey's avatar
Edward Hervey committed
798 799 800 801 802 803 804
  if (templ) {
    ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
        "direction", dir, "template", templ, NULL);
  } else {
    ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
        "direction", dir, NULL);
  }
805

806 807 808 809 810 811 812 813 814 815 816 817 818
  /* Set directional padfunctions for ghostpad */
  if (dir == GST_PAD_SINK) {
    gst_pad_set_bufferalloc_function (ret,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc));
    gst_pad_set_chain_function (ret,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain));
  } else {
    gst_pad_set_getrange_function (ret,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange));
    gst_pad_set_checkgetrange_function (ret,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange));
  }

819
  /* link/unlink functions */
820 821 822
  gst_pad_set_link_function (ret, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_link));
  gst_pad_set_unlink_function (ret,
      GST_DEBUG_FUNCPTR (gst_ghost_pad_do_unlink));
823

824

825
  /* INTERNAL PAD, it always exists and is child of the ghostpad */
826 827 828 829 830 831 832 833 834 835
  otherdir = (dir == GST_PAD_SRC) ? GST_PAD_SINK : GST_PAD_SRC;
  if (templ) {
    internal =
        g_object_new (GST_TYPE_PROXY_PAD, "name", NULL,
        "direction", otherdir, "template", templ, NULL);
  } else {
    internal =
        g_object_new (GST_TYPE_PROXY_PAD, "name", NULL,
        "direction", otherdir, NULL);
  }
836
  GST_PAD_UNSET_FLUSHING (internal);
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852

  /* Set directional padfunctions for internal pad */
  if (dir == GST_PAD_SRC) {
    gst_pad_set_bufferalloc_function (internal,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc));
    gst_pad_set_chain_function (internal,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain));
  } else {
    gst_pad_set_getrange_function (internal,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange));
    gst_pad_set_checkgetrange_function (internal,
        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange));
  }

  GST_PROXY_LOCK (ret);

853
  /* now make the ghostpad a parent of the internal pad */
854
  if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
855 856 857 858 859 860 861 862 863 864
          GST_OBJECT_CAST (ret)))
    goto parent_failed;

  /* The ghostpad is the parent of the internal pad and is the only object that
   * can have a refcount on the internal pad.
   * At this point, the GstGhostPad has a refcount of 1, and the internal pad has
   * a refcount of 1.
   * When the refcount of the GstGhostPad drops to 0, the ghostpad will dispose
   * it's refcount on the internal pad in the dispose method by un-parenting it.
   * This is why we don't take extra refcounts in the assignments below
865 866
   */
  GST_PROXY_PAD_INTERNAL (ret) = internal;
867
  GST_PROXY_PAD_INTERNAL (internal) = ret;
868 869 870

  /* could be more general here, iterating over all writable properties...
   * taking the short road for now tho */
871 872 873
  GST_GHOST_PAD_CAST (ret)->notify_id =
      g_signal_connect (internal, "notify::caps", G_CALLBACK (on_int_notify),
      ret);
874 875

  /* call function to init values of the pad caps */
876 877
  on_int_notify (internal, NULL, GST_GHOST_PAD_CAST (ret));

878 879
  /* special activation functions for the internal pad */
  gst_pad_set_activatepull_function (internal,
880
      GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_pull));
881
  gst_pad_set_activatepush_function (internal,
882 883 884 885
      GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_push));

  GST_PROXY_UNLOCK (ret);

886
  return ret;
887 888 889 890

  /* ERRORS */
parent_failed:
  {
891 892 893 894
    GST_WARNING_OBJECT (ret, "Could not set internal pad %s:%s",
        GST_DEBUG_PAD_NAME (internal));
    g_critical ("Could not set internal pad %s:%s",
        GST_DEBUG_PAD_NAME (internal));
895 896
    GST_PROXY_UNLOCK (ret);
    gst_object_unref (ret);
897
    gst_object_unref (internal);
898 899
    return NULL;
  }
900 901
}

Edward Hervey's avatar
Edward Hervey committed
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
/**
 * gst_ghost_pad_new_no_target:
 * @name: the name of the new pad, or NULL to assign a default name.
 * @dir: the direction of the ghostpad
 *
 * Create a new ghostpad without a target with the given direction.
 * A target can be set on the ghostpad later with the
 * gst_ghost_pad_set_target() function.
 *
 * The created ghostpad will not have a padtemplate.
 *
 * Returns: a new #GstPad, or NULL in case of an error.
 */
GstPad *
gst_ghost_pad_new_no_target (const gchar * name, GstPadDirection dir)
{
918 919
  GstPad *ret;

Edward Hervey's avatar
Edward Hervey committed
920 921
  g_return_val_if_fail (dir != GST_PAD_UNKNOWN, NULL);

922 923 924
  GST_LOG ("name:%s, direction:%d", GST_STR_NULL (name), dir);

  ret = gst_ghost_pad_new_full (name, dir, NULL);
Edward Hervey's avatar
Edward Hervey committed
925

926
  return ret;
Edward Hervey's avatar
Edward Hervey committed
927 928
}

929 930 931 932 933
/**
 * gst_ghost_pad_new:
 * @name: the name of the new pad, or NULL to assign a default name.
 * @target: the pad to ghost.
 *
934
 * Create a new ghostpad with @target as the target. The direction will be taken
935
 * from the target pad. @target must be unlinked.
936
 *
937 938 939 940 941 942 943 944 945 946
 * Will ref the target.
 *
 * Returns: a new #GstPad, or NULL in case of an error.
 */
GstPad *
gst_ghost_pad_new (const gchar * name, GstPad * target)
{
  GstPad *ret;

  g_return_val_if_fail (GST_IS_PAD (target), NULL);
947
  g_return_val_if_fail (!gst_pad_is_linked (target), NULL);
948

949 950 951
  GST_LOG ("name:%s, target:%s:%s", GST_STR_NULL (name),
      GST_DEBUG_PAD_NAME (target));

Edward Hervey's avatar
Edward Hervey committed
952 953 954 955 956 957 958 959 960
  if ((ret = gst_ghost_pad_new_no_target (name, GST_PAD_DIRECTION (target))))
    if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ret), target))
      goto set_target_failed;

  return ret;

  /* ERRORS */
set_target_failed:
  {
961 962
    GST_WARNING_OBJECT (ret, "failed to set target %s:%s",
        GST_DEBUG_PAD_NAME (target));
Edward Hervey's avatar
Edward Hervey committed
963 964
    gst_object_unref (ret);
    return NULL;
965
  }
Edward Hervey's avatar
Edward Hervey committed
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
}

/**
 * gst_ghost_pad_new_from_template:
 * @name: the name of the new pad, or NULL to assign a default name.
 * @target: the pad to ghost.
 * @templ: the #GstPadTemplate to use on the ghostpad.
 *
 * Create a new ghostpad with @target as the target. The direction will be taken
 * from the target pad. The template used on the ghostpad will be @template.
 *
 * Will ref the target.
 *
 * Returns: a new #GstPad, or NULL in case of an error.
 *
 * Since: 0.10.10
 */

GstPad *
gst_ghost_pad_new_from_template (const gchar * name, GstPad * target,
    GstPadTemplate * templ)
{
  GstPad *ret;

  g_return_val_if_fail (GST_IS_PAD (target), NULL);
  g_return_val_if_fail (!gst_pad_is_linked (target), NULL);
  g_return_val_if_fail (templ != NULL, NULL);
993 994 995 996 997
  g_return_val_if_fail (GST_PAD_TEMPLATE_DIRECTION (templ) ==
      GST_PAD_DIRECTION (target), NULL);

  GST_LOG ("name:%s, target:%s:%s, templ:%p", GST_STR_NULL (name),
      GST_DEBUG_PAD_NAME (target), templ);
Edward Hervey's avatar
Edward Hervey committed
998 999 1000 1001 1002 1003 1004 1005 1006 1007

  if ((ret = gst_ghost_pad_new_full (name, GST_PAD_DIRECTION (target), templ)))
    if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ret), target))
      goto set_target_failed;

  return ret;

  /* ERRORS */
set_target_failed:
  {
1008 1009
    GST_WARNING_OBJECT (ret, "failed to set target %s:%s",
        GST_DEBUG_PAD_NAME (target));
Edward Hervey's avatar
Edward Hervey committed
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
    gst_object_unref (ret);
    return NULL;
  }
}

/**
 * gst_ghost_pad_new_no_target_from_template:
 * @name: the name of the new pad, or NULL to assign a default name.
 * @templ: the #GstPadTemplate to create the ghostpad from.
 *
 * Create a new ghostpad based on @templ, without setting a target. The
1021
 * direction will be taken from the @templ.
Edward Hervey's avatar
Edward Hervey committed
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
 *
 * Returns: a new #GstPad, or NULL in case of an error.
 *
 * Since: 0.10.10
 */
GstPad *
gst_ghost_pad_new_no_target_from_template (const gchar * name,
    GstPadTemplate * templ)
{
  GstPad *ret;

  g_return_val_if_fail (templ != NULL, NULL);

  ret =
      gst_ghost_pad_new_full (name, GST_PAD_TEMPLATE_DIRECTION (templ), templ);

1038 1039
  return ret;
}
1040

1041 1042 1043 1044
/**
 * gst_ghost_pad_get_target:
 * @gpad: the #GstGhostpad
 *
1045
 * Get the target pad of #gpad. Unref target pad after usage.
1046 1047
 *
 * Returns: the target #GstPad, can be NULL if the ghostpad
1048
 * has no target set. Unref target pad after usage.
1049 1050 1051 1052
 */
GstPad *
gst_ghost_pad_get_target (GstGhostPad * gpad)
{
1053 1054
  GstPad *ret;

1055
  g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), NULL);
1056

1057 1058 1059 1060 1061
  ret = gst_proxy_pad_get_target (GST_PAD_CAST (gpad));

  GST_DEBUG_OBJECT (gpad, "get target %s:%s", GST_DEBUG_PAD_NAME (ret));

  return ret;
1062 1063 1064 1065 1066 1067 1068 1069
}

/**
 * gst_ghost_pad_set_target:
 * @gpad: the #GstGhostpad
 * @newtarget: the new pad target
 *
 * Set the new target of the ghostpad @gpad. Any existing target
Stefan Kost's avatar
Stefan Kost committed
1070
 * is unlinked and links to the new target are established.
1071 1072 1073 1074 1075 1076
 *
 * Returns: TRUE if the new target could be set, FALSE otherwise.
 */
gboolean
gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget)
{
1077 1078
  GstPad *internal;
  GstPad *oldtarget;
1079
  GstObject *parent;
1080
  gboolean result;
1081
  GstPadLinkReturn lret;
1082

1083 1084
  g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE);

1085
  GST_PROXY_LOCK (gpad);
1086
  internal = GST_PROXY_PAD_INTERNAL (gpad);
1087

1088
  GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget));
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103

  /* clear old target */
  if ((oldtarget = GST_PROXY_PAD_TARGET (gpad))) {
    /* if we have an internal pad, unlink */
    if (internal) {
      if (GST_PAD_IS_SRC (internal))
        gst_pad_unlink (internal, oldtarget);
      else
        gst_pad_unlink (oldtarget, internal);
    }
  }

  result = gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), newtarget);

  if (result && newtarget) {