tests/check/: use the new macro
[platform/upstream/gstreamer.git] / gst / gstghostpad.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2005 Andy Wingo <wingo@pobox.com>
5  *
6  * gstghostpad.c: Proxy pads
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /**
25  * SECTION:gstghostpad
26  * @short_description: Pseudo link pads
27  * @see_also: #GstPad
28  *
29  * GhostPads are useful when organizing pipelines with #GstBin like elements.
30  * The idea here is to create hierarchical element graphs. The bin element
31  * contains a sub-graph. Now one would like to treat the bin-element like other
32  * #GstElements. This is where GhostPads come into play. A GhostPad acts as a
33  * proxy for another pad. Thus the bin can have sink and source ghost-pads that
34  * are associated with sink and source pads of the child elements.
35  *
36  * If the target pad is known at creation time, gst_ghost_pad_new() is the
37  * function to use to get a ghost-pad. Otherwise one can use gst_ghost_pad_new_no_target()
38  * to create the ghost-pad and use gst_ghost_pad_set_target() to establish the
39  * association later on.
40  *
41  * Last reviewed on 2005-11-18 (0.9.5)
42  */
43
44 #include "gst_private.h"
45
46 #include "gstghostpad.h"
47
48 #define GST_TYPE_PROXY_PAD              (gst_proxy_pad_get_type ())
49 #define GST_IS_PROXY_PAD(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PROXY_PAD))
50 #define GST_IS_PROXY_PAD_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PROXY_PAD))
51 #define GST_PROXY_PAD(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PROXY_PAD, GstProxyPad))
52 #define GST_PROXY_PAD_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PROXY_PAD, GstProxyPadClass))
53 #define GST_PROXY_PAD_TARGET(pad)       (GST_PROXY_PAD (pad)->target)
54
55
56 typedef struct _GstProxyPad GstProxyPad;
57 typedef struct _GstProxyPadClass GstProxyPadClass;
58
59 #define GST_PROXY_GET_LOCK(pad) (GST_PROXY_PAD (pad)->proxy_lock)
60 #define GST_PROXY_LOCK(pad)     (g_mutex_lock (GST_PROXY_GET_LOCK (pad)))
61 #define GST_PROXY_UNLOCK(pad)   (g_mutex_unlock (GST_PROXY_GET_LOCK (pad)))
62
63 struct _GstProxyPad
64 {
65   GstPad pad;
66
67   /* with PROXY_LOCK */
68   GMutex *proxy_lock;
69   GstPad *target;
70
71   /*< private > */
72   gpointer _gst_reserved[1];
73 };
74
75 struct _GstProxyPadClass
76 {
77   GstPadClass parent_class;
78
79   /*< private > */
80   gpointer _gst_reserved[1];
81 };
82
83
84 G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);
85
86 static GstPad *gst_proxy_pad_get_target (GstPad * pad);
87
88 static void gst_proxy_pad_dispose (GObject * object);
89 static void gst_proxy_pad_finalize (GObject * object);
90
91 #ifndef GST_DISABLE_LOADSAVE
92 static xmlNodePtr gst_proxy_pad_save_thyself (GstObject * object,
93     xmlNodePtr parent);
94 #endif
95
96
97 static void
98 gst_proxy_pad_class_init (GstProxyPadClass * klass)
99 {
100   GObjectClass *gobject_class = (GObjectClass *) klass;
101
102   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_proxy_pad_dispose);
103   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_proxy_pad_finalize);
104
105 #ifndef GST_DISABLE_LOADSAVE
106   {
107     GstObjectClass *gstobject_class = (GstObjectClass *) klass;
108
109     gstobject_class->save_thyself =
110         GST_DEBUG_FUNCPTR (gst_proxy_pad_save_thyself);
111   }
112 #endif
113 }
114
115 const GstQueryType *
116 gst_proxy_pad_do_query_type (GstPad * pad)
117 {
118   GstPad *target = gst_proxy_pad_get_target (pad);
119   const GstQueryType *res;
120
121   g_return_val_if_fail (target != NULL, NULL);
122
123   res = gst_pad_get_query_types (target);
124   gst_object_unref (target);
125
126   return res;
127 }
128
129 static gboolean
130 gst_proxy_pad_do_event (GstPad * pad, GstEvent * event)
131 {
132   gboolean res;
133   GstPad *target = gst_proxy_pad_get_target (pad);
134
135   g_return_val_if_fail (target != NULL, FALSE);
136
137   res = gst_pad_send_event (target, event);
138   gst_object_unref (target);
139
140   return res;
141 }
142
143 static gboolean
144 gst_proxy_pad_do_query (GstPad * pad, GstQuery * query)
145 {
146   gboolean res;
147   GstPad *target = gst_proxy_pad_get_target (pad);
148
149   g_return_val_if_fail (target != NULL, FALSE);
150
151   res = gst_pad_query (target, query);
152   gst_object_unref (target);
153
154   return res;
155 }
156
157 static GList *
158 gst_proxy_pad_do_internal_link (GstPad * pad)
159 {
160   GstPad *target = gst_proxy_pad_get_target (pad);
161   GList *res;
162
163   g_return_val_if_fail (target != NULL, NULL);
164
165   res = gst_pad_get_internal_links (target);
166   gst_object_unref (target);
167
168   return res;
169 }
170
171 static GstFlowReturn
172 gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size,
173     GstCaps * caps, GstBuffer ** buf)
174 {
175   GstFlowReturn result;
176   GstPad *target = gst_proxy_pad_get_target (pad);
177   GstPad *peer;
178
179   g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED);
180
181   peer = gst_pad_get_peer (target);
182   if (peer) {
183     GST_DEBUG ("buffer alloc on %s:%s", GST_DEBUG_PAD_NAME (target));
184
185     result = gst_pad_alloc_buffer (peer, offset, size, caps, buf);
186
187     gst_object_unref (peer);
188   } else {
189     result = GST_FLOW_NOT_LINKED;
190   }
191
192   gst_object_unref (target);
193
194   return result;
195 }
196
197 static GstFlowReturn
198 gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
199 {
200   GstPad *target = gst_proxy_pad_get_target (pad);
201   GstFlowReturn res;
202
203   g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED);
204
205   res = gst_pad_chain (target, buffer);
206   gst_object_unref (target);
207
208   return res;
209 }
210
211 static GstFlowReturn
212 gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
213     GstBuffer ** buffer)
214 {
215   GstPad *target = gst_proxy_pad_get_target (pad);
216   GstFlowReturn res;
217
218   g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED);
219
220   res = gst_pad_get_range (target, offset, size, buffer);
221   gst_object_unref (target);
222
223   return res;
224 }
225
226 static gboolean
227 gst_proxy_pad_do_checkgetrange (GstPad * pad)
228 {
229   gboolean result;
230   GstPad *target = gst_proxy_pad_get_target (pad);
231   GstPad *peer;
232
233   g_return_val_if_fail (target != NULL, FALSE);
234
235   peer = gst_pad_get_peer (target);
236   if (peer) {
237     result = gst_pad_check_pull_range (peer);
238     gst_object_unref (peer);
239   } else {
240     result = FALSE;
241   }
242   gst_object_unref (target);
243
244   return result;
245 }
246
247 static GstCaps *
248 gst_proxy_pad_do_getcaps (GstPad * pad)
249 {
250   GstPad *target = gst_proxy_pad_get_target (pad);
251   GstCaps *res;
252
253   g_return_val_if_fail (target != NULL, NULL);
254
255   res = gst_pad_get_caps (target);
256   gst_object_unref (target);
257
258   return res;
259 }
260
261 static gboolean
262 gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
263 {
264   GstPad *target = gst_proxy_pad_get_target (pad);
265   gboolean res;
266
267   g_return_val_if_fail (target != NULL, FALSE);
268
269   res = gst_pad_accept_caps (target, caps);
270   gst_object_unref (target);
271
272   return res;
273 }
274
275 static void
276 gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
277 {
278   GstPad *target = gst_proxy_pad_get_target (pad);
279
280   g_return_if_fail (target != NULL);
281
282   gst_pad_fixate_caps (target, caps);
283   gst_object_unref (target);
284 }
285
286 static gboolean
287 gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
288 {
289   GstPad *target = gst_proxy_pad_get_target (pad);
290   gboolean res;
291
292   g_return_val_if_fail (target != NULL, FALSE);
293
294   res = gst_pad_set_caps (target, caps);
295   gst_object_unref (target);
296
297   return res;
298 }
299
300 static gboolean
301 gst_proxy_pad_set_target_unlocked (GstPad * pad, GstPad * target)
302 {
303   GstPad *oldtarget;
304
305   GST_DEBUG ("set target %s:%s on %s:%s",
306       GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (pad));
307
308   /* clear old target */
309   if ((oldtarget = GST_PROXY_PAD_TARGET (pad))) {
310     gst_object_unref (oldtarget);
311     GST_PROXY_PAD_TARGET (pad) = NULL;
312   }
313
314   if (target) {
315     /* set and ref new target if any */
316     GST_PROXY_PAD_TARGET (pad) = gst_object_ref (target);
317
318     gst_pad_set_query_type_function (pad,
319         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query_type));
320     gst_pad_set_event_function (pad,
321         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_event));
322     gst_pad_set_query_function (pad,
323         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query));
324     gst_pad_set_internal_link_function (pad,
325         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_internal_link));
326     gst_pad_set_getcaps_function (pad,
327         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getcaps));
328     gst_pad_set_acceptcaps_function (pad,
329         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_acceptcaps));
330     gst_pad_set_fixatecaps_function (pad,
331         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_fixatecaps));
332     gst_pad_set_setcaps_function (pad,
333         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_setcaps));
334
335     if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
336       gst_pad_set_bufferalloc_function (pad,
337           GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc));
338       gst_pad_set_chain_function (pad,
339           GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain));
340     } else {
341       gst_pad_set_getrange_function (pad,
342           GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange));
343       gst_pad_set_checkgetrange_function (pad,
344           GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange));
345     }
346   }
347   return TRUE;
348 }
349
350 static gboolean
351 gst_proxy_pad_set_target (GstPad * pad, GstPad * target)
352 {
353   gboolean result;
354
355   GST_PROXY_LOCK (pad);
356   result = gst_proxy_pad_set_target_unlocked (pad, target);
357   GST_PROXY_UNLOCK (pad);
358
359   return result;
360 }
361
362 static GstPad *
363 gst_proxy_pad_get_target (GstPad * pad)
364 {
365   GstPad *target;
366
367   GST_PROXY_LOCK (pad);
368   target = GST_PROXY_PAD_TARGET (pad);
369   if (target)
370     gst_object_ref (target);
371   GST_PROXY_UNLOCK (pad);
372
373   return target;
374 }
375
376 static void
377 gst_proxy_pad_init (GstProxyPad * pad)
378 {
379   pad->proxy_lock = g_mutex_new ();
380 }
381
382 static void
383 gst_proxy_pad_dispose (GObject * object)
384 {
385   GstPad *pad = GST_PAD (object);
386   GstPad **target_p;
387
388   GST_PROXY_LOCK (pad);
389   target_p = &GST_PROXY_PAD_TARGET (pad);
390   gst_object_replace ((GstObject **) target_p, NULL);
391   GST_PROXY_UNLOCK (pad);
392
393   G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
394 }
395
396 static void
397 gst_proxy_pad_finalize (GObject * object)
398 {
399   GstProxyPad *pad = GST_PROXY_PAD (object);
400
401   g_mutex_free (pad->proxy_lock);
402   pad->proxy_lock = NULL;
403
404   G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
405 }
406
407 #ifndef GST_DISABLE_LOADSAVE
408 /**
409  * gst_proxy_pad_save_thyself:
410  * @pad: a ghost #GstPad to save.
411  * @parent: the parent #xmlNodePtr to save the description in.
412  *
413  * Saves the ghost pad into an xml representation.
414  *
415  * Returns: the #xmlNodePtr representation of the pad.
416  */
417 static xmlNodePtr
418 gst_proxy_pad_save_thyself (GstObject * object, xmlNodePtr parent)
419 {
420   xmlNodePtr self;
421
422   g_return_val_if_fail (GST_IS_PROXY_PAD (object), NULL);
423
424   self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
425   xmlNewChild (self, NULL, (xmlChar *) "name",
426       (xmlChar *) GST_OBJECT_NAME (object));
427   xmlNewChild (self, NULL, (xmlChar *) "parent",
428       (xmlChar *) GST_OBJECT_NAME (GST_OBJECT_PARENT (object)));
429
430   /* FIXME FIXME FIXME! */
431
432   return self;
433 }
434 #endif /* GST_DISABLE_LOADSAVE */
435
436
437 /***********************************************************************
438  * Ghost pads, implemented as a pair of proxy pads (sort of)
439  */
440
441
442 struct _GstGhostPad
443 {
444   GstProxyPad pad;
445
446   /* with PROXY_LOCK */
447   GstPad *internal;
448   gulong notify_id;
449
450   /*< private > */
451   gpointer _gst_reserved[GST_PADDING];
452 };
453
454 struct _GstGhostPadClass
455 {
456   GstProxyPadClass parent_class;
457
458   /*< private > */
459   gpointer _gst_reserved[GST_PADDING];
460 };
461
462
463 G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
464
465 static gboolean gst_ghost_pad_set_internal (GstGhostPad * pad,
466     GstPad * internal);
467
468 static void gst_ghost_pad_dispose (GObject * object);
469
470 /* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we
471  * use for GST_PTR_FORMAT. */
472 static void
473 gst_critical (const gchar * format, ...)
474 {
475   va_list args;
476
477   va_start (args, format);
478   g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
479   va_end (args);
480 }
481
482 static GstPad *
483 gst_ghost_pad_get_internal (GstPad * pad)
484 {
485   GstPad *internal;
486
487   GST_PROXY_LOCK (pad);
488   internal = GST_GHOST_PAD (pad)->internal;
489   if (internal)
490     gst_object_ref (internal);
491   GST_PROXY_UNLOCK (pad);
492
493   return internal;
494 }
495
496 static void
497 gst_ghost_pad_class_init (GstGhostPadClass * klass)
498 {
499   GObjectClass *gobject_class = (GObjectClass *) klass;
500
501   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
502 }
503
504 /* see gstghostpad design docs */
505 static gboolean
506 gst_ghost_pad_internal_do_activate_push (GstPad * pad, gboolean active)
507 {
508   gboolean ret;
509
510   if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
511     GstPad *parent = GST_PAD (gst_object_get_parent (GST_OBJECT (pad)));
512
513     if (parent) {
514       g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE);
515
516       ret = gst_pad_activate_push (parent, active);
517
518       gst_object_unref (parent);
519     } else {
520       ret = FALSE;
521     }
522   } else {
523     GstPad *peer = gst_pad_get_peer (pad);
524
525     if (peer) {
526       ret = gst_pad_activate_push (peer, active);
527       gst_object_unref (peer);
528     } else {
529       ret = FALSE;
530     }
531   }
532
533   return ret;
534 }
535
536 static gboolean
537 gst_ghost_pad_internal_do_activate_pull (GstPad * pad, gboolean active)
538 {
539   gboolean ret;
540
541   if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
542     GstPad *peer = gst_pad_get_peer (pad);
543
544     if (peer) {
545       ret = gst_pad_activate_pull (peer, active);
546       gst_object_unref (peer);
547     } else {
548       ret = FALSE;
549     }
550   } else {
551     GstPad *parent = GST_PAD (gst_object_get_parent (GST_OBJECT (pad)));
552
553     if (parent) {
554       g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE);
555
556       ret = gst_pad_activate_pull (parent, active);
557
558       gst_object_unref (parent);
559     } else {
560       ret = FALSE;
561     }
562   }
563
564   return ret;
565 }
566
567 static gboolean
568 gst_ghost_pad_do_activate_push (GstPad * pad, gboolean active)
569 {
570   gboolean ret;
571
572   if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
573     GstPad *internal = gst_ghost_pad_get_internal (pad);
574
575     if (internal) {
576       ret = gst_pad_activate_push (internal, active);
577       gst_object_unref (internal);
578     } else {
579       ret = TRUE;
580     }
581   } else {
582     ret = TRUE;
583   }
584
585   return ret;
586 }
587
588 static gboolean
589 gst_ghost_pad_do_activate_pull (GstPad * pad, gboolean active)
590 {
591   gboolean ret;
592
593   if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
594     GstPad *peer = gst_pad_get_peer (pad);
595
596     if (peer) {
597       ret = gst_pad_activate_pull (peer, active);
598       gst_object_unref (peer);
599     } else {
600       ret = FALSE;
601     }
602   } else {
603     GstPad *internal = gst_ghost_pad_get_internal (pad);
604
605     if (internal) {
606       ret = gst_pad_activate_pull (internal, active);
607       gst_object_unref (internal);
608     } else {
609       ret = FALSE;
610     }
611   }
612
613   return ret;
614 }
615
616 static GstPadLinkReturn
617 gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
618 {
619   GstPad *internal, *target;
620   GstPadLinkReturn ret;
621
622   target = gst_proxy_pad_get_target (pad);
623
624   g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED);
625
626   /* proxy the peer into the bin */
627   internal = g_object_new (GST_TYPE_PROXY_PAD,
628       "name", NULL,
629       "direction", GST_PAD_DIRECTION (peer),
630       "template", GST_PAD_PAD_TEMPLATE (peer), NULL);
631
632   gst_proxy_pad_set_target (internal, peer);
633   gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), internal);
634
635   if (GST_PAD_IS_SRC (internal))
636     ret = gst_pad_link (internal, target);
637   else
638     ret = gst_pad_link (target, internal);
639
640   /* if we are a source pad, we should call the peer link function
641    * if the peer has one */
642   if (GST_PAD_IS_SRC (pad)) {
643     if (GST_PAD_LINKFUNC (peer) && ret == GST_PAD_LINK_OK)
644       ret = GST_PAD_LINKFUNC (peer) (peer, pad);
645   }
646
647   gst_object_unref (target);
648
649   if (ret == GST_PAD_LINK_OK)
650     gst_pad_set_active (internal, GST_PAD_ACTIVATE_MODE (pad));
651   else
652     gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), NULL);
653
654   return ret;
655 }
656
657 static void
658 gst_ghost_pad_do_unlink (GstPad * pad)
659 {
660   GstPad *target = gst_proxy_pad_get_target (pad);
661
662   g_return_if_fail (target != NULL);
663
664   GST_DEBUG_OBJECT (pad, "unlinking ghostpad");
665
666   if (target->unlinkfunc)
667     target->unlinkfunc (target);
668
669   gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), NULL);
670
671   gst_object_unref (target);
672 }
673
674 static void
675 on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
676 {
677   GstCaps *caps;
678
679   g_object_get (internal, "caps", &caps, NULL);
680
681   GST_OBJECT_LOCK (pad);
682   gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
683   GST_OBJECT_UNLOCK (pad);
684
685   g_object_notify (G_OBJECT (pad), "caps");
686   if (caps)
687     gst_caps_unref (caps);
688 }
689
690 static gboolean
691 gst_ghost_pad_set_internal (GstGhostPad * pad, GstPad * internal)
692 {
693   GST_PROXY_LOCK (pad);
694   /* first remove old internal pad */
695   if (pad->internal) {
696     GstPad *intpeer;
697
698     gst_pad_set_activatepull_function (pad->internal, NULL);
699     gst_pad_set_activatepush_function (pad->internal, NULL);
700
701     g_signal_handler_disconnect (pad->internal, pad->notify_id);
702
703     intpeer = gst_pad_get_peer (pad->internal);
704     if (intpeer) {
705       if (GST_PAD_IS_SRC (pad->internal))
706         gst_pad_unlink (pad->internal, intpeer);
707       else
708         gst_pad_unlink (intpeer, pad->internal);
709       gst_object_unref (intpeer);
710     }
711     /* should dispose it */
712     gst_object_unparent (GST_OBJECT_CAST (pad->internal));
713   }
714
715   /* then set new internal pad */
716   if (internal) {
717     if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
718             GST_OBJECT_CAST (pad)))
719       goto could_not_set;
720
721     /* could be more general here, iterating over all writable properties...
722      * taking the short road for now tho */
723     pad->notify_id = g_signal_connect (internal, "notify::caps",
724         G_CALLBACK (on_int_notify), pad);
725     on_int_notify (internal, NULL, pad);
726     gst_pad_set_activatepull_function (GST_PAD (internal),
727         GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_pull));
728     gst_pad_set_activatepush_function (GST_PAD (internal),
729         GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_push));
730     /* a ref was taken by set_parent */
731   }
732   pad->internal = internal;
733
734   GST_PROXY_UNLOCK (pad);
735
736   return TRUE;
737
738   /* ERRORS */
739 could_not_set:
740   {
741     gst_critical ("Could not set internal pad %" GST_PTR_FORMAT, internal);
742     GST_PROXY_UNLOCK (pad);
743     return FALSE;
744   }
745 }
746
747 static void
748 gst_ghost_pad_init (GstGhostPad * pad)
749 {
750   gst_pad_set_activatepull_function (GST_PAD (pad),
751       GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_pull));
752   gst_pad_set_activatepush_function (GST_PAD (pad),
753       GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_push));
754 }
755
756 static void
757 gst_ghost_pad_dispose (GObject * object)
758 {
759   gst_ghost_pad_set_internal (GST_GHOST_PAD (object), NULL);
760
761   G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
762 }
763
764 /**
765  * gst_ghost_pad_new_no_target:
766  * @name: the name of the new pad, or NULL to assign a default name.
767  * @dir: the direction of the ghostpad
768  *
769  * Create a new ghostpad without a target with the given direction.
770  * A target can be set on the ghostpad later with the
771  * gst_ghost_pad_set_target() function.
772  *
773  * The created ghostpad will not have a padtemplate.
774  *
775  * Returns: a new #GstPad, or NULL in case of an error.
776  */
777 GstPad *
778 gst_ghost_pad_new_no_target (const gchar * name, GstPadDirection dir)
779 {
780   GstPad *ret;
781
782   ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name, "direction", dir, NULL);
783
784   gst_pad_set_activatepush_function (ret,
785       GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_push));
786   gst_pad_set_link_function (ret, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_link));
787   gst_pad_set_unlink_function (ret,
788       GST_DEBUG_FUNCPTR (gst_ghost_pad_do_unlink));
789
790   return ret;
791 }
792
793 /**
794  * gst_ghost_pad_new:
795  * @name: the name of the new pad, or NULL to assign a default name.
796  * @target: the pad to ghost.
797  *
798  * Create a new ghostpad with @target as the target. The direction and
799  * padtemplate will be taken from the target pad.
800  *
801  * Will ref the target.
802  *
803  * Returns: a new #GstPad, or NULL in case of an error.
804  */
805 GstPad *
806 gst_ghost_pad_new (const gchar * name, GstPad * target)
807 {
808   GstPad *ret;
809
810   g_return_val_if_fail (GST_IS_PAD (target), NULL);
811   g_return_val_if_fail (!gst_pad_is_linked (target), NULL);
812
813   if ((ret = gst_ghost_pad_new_no_target (name, GST_PAD_DIRECTION (target)))) {
814     g_object_set (G_OBJECT (ret),
815         "template", GST_PAD_PAD_TEMPLATE (target), NULL);
816     gst_ghost_pad_set_target (GST_GHOST_PAD (ret), target);
817   }
818   return ret;
819 }
820
821 /**
822  * gst_ghost_pad_get_target:
823  * @gpad: the #GstGhostpad
824  *
825  * Get the target pad of #gpad. Unref target pad after usage.
826  *
827  * Returns: the target #GstPad, can be NULL if the ghostpad
828  * has no target set. Unref target pad after usage.
829  */
830 GstPad *
831 gst_ghost_pad_get_target (GstGhostPad * gpad)
832 {
833   g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), NULL);
834
835   return gst_proxy_pad_get_target (GST_PAD_CAST (gpad));
836 }
837
838 /**
839  * gst_ghost_pad_set_target:
840  * @gpad: the #GstGhostpad
841  * @newtarget: the new pad target
842  *
843  * Set the new target of the ghostpad @gpad. Any existing target
844  * is unlinked and links to the new target are established.
845  *
846  * Returns: TRUE if the new target could be set, FALSE otherwise.
847  */
848 gboolean
849 gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget)
850 {
851   GstPad *internal;
852   GstPad *oldtarget;
853   gboolean result;
854
855   g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE);
856
857   GST_PROXY_LOCK (gpad);
858   internal = gpad->internal;
859
860   GST_DEBUG ("set target %s:%s on %s:%s",
861       GST_DEBUG_PAD_NAME (newtarget), GST_DEBUG_PAD_NAME (gpad));
862
863   /* clear old target */
864   if ((oldtarget = GST_PROXY_PAD_TARGET (gpad))) {
865     /* if we have an internal pad, unlink */
866     if (internal) {
867       if (GST_PAD_IS_SRC (internal))
868         gst_pad_unlink (internal, oldtarget);
869       else
870         gst_pad_unlink (oldtarget, internal);
871     }
872   }
873
874   result = gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), newtarget);
875
876   if (result && newtarget) {
877     /* and link to internal pad if we have one */
878     if (internal) {
879       if (GST_PAD_IS_SRC (internal))
880         result = gst_pad_link (internal, newtarget);
881       else
882         result = gst_pad_link (newtarget, internal);
883     }
884   }
885   GST_PROXY_UNLOCK (gpad);
886
887   return result;
888 }