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>
6 * gstghostpad.c: Proxy pads
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.
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.
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.
25 * @short_description: Pseudo link pads
30 #include "gst_private.h"
32 #include "gstghostpad.h"
34 #define GST_TYPE_PROXY_PAD (gst_proxy_pad_get_type ())
35 #define GST_IS_PROXY_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PROXY_PAD))
36 #define GST_IS_PROXY_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PROXY_PAD))
37 #define GST_PROXY_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PROXY_PAD, GstProxyPad))
38 #define GST_PROXY_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PROXY_PAD, GstProxyPadClass))
39 #define GST_PROXY_PAD_TARGET(pad) (GST_PROXY_PAD (pad)->target)
42 typedef struct _GstProxyPad GstProxyPad;
43 typedef struct _GstProxyPadClass GstProxyPadClass;
45 #define GST_PROXY_GET_LOCK(pad) (GST_PROXY_PAD (pad)->proxy_lock)
46 #define GST_PROXY_LOCK(pad) (g_mutex_lock (GST_PROXY_GET_LOCK (pad)))
47 #define GST_PROXY_UNLOCK(pad) (g_mutex_unlock (GST_PROXY_GET_LOCK (pad)))
58 gpointer _gst_reserved[1];
61 struct _GstProxyPadClass
63 GstPadClass parent_class;
66 gpointer _gst_reserved[1];
70 G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);
72 static GstPad *gst_proxy_pad_get_target (GstPad * pad);
74 static void gst_proxy_pad_dispose (GObject * object);
75 static void gst_proxy_pad_finalize (GObject * object);
77 #ifndef GST_DISABLE_LOADSAVE
78 static xmlNodePtr gst_proxy_pad_save_thyself (GstObject * object,
84 gst_proxy_pad_class_init (GstProxyPadClass * klass)
86 GObjectClass *gobject_class = (GObjectClass *) klass;
88 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_proxy_pad_dispose);
89 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_proxy_pad_finalize);
91 #ifndef GST_DISABLE_LOADSAVE
93 GstObjectClass *gstobject_class = (GstObjectClass *) klass;
95 gstobject_class->save_thyself =
96 GST_DEBUG_FUNCPTR (gst_proxy_pad_save_thyself);
102 gst_proxy_pad_do_query_type (GstPad * pad)
104 GstPad *target = gst_proxy_pad_get_target (pad);
105 const GstQueryType *res;
107 g_return_val_if_fail (target != NULL, NULL);
109 res = gst_pad_get_query_types (target);
110 gst_object_unref (target);
116 gst_proxy_pad_do_event (GstPad * pad, GstEvent * event)
119 GstPad *target = gst_proxy_pad_get_target (pad);
121 g_return_val_if_fail (target != NULL, FALSE);
123 res = gst_pad_send_event (target, event);
124 gst_object_unref (target);
130 gst_proxy_pad_do_query (GstPad * pad, GstQuery * query)
133 GstPad *target = gst_proxy_pad_get_target (pad);
135 g_return_val_if_fail (target != NULL, FALSE);
137 res = gst_pad_query (target, query);
138 gst_object_unref (target);
144 gst_proxy_pad_do_internal_link (GstPad * pad)
146 GstPad *target = gst_proxy_pad_get_target (pad);
149 g_return_val_if_fail (target != NULL, NULL);
151 res = gst_pad_get_internal_links (target);
152 gst_object_unref (target);
158 gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size,
159 GstCaps * caps, GstBuffer ** buf)
161 GstFlowReturn result;
162 GstPad *target = gst_proxy_pad_get_target (pad);
165 g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED);
167 peer = gst_pad_get_peer (target);
169 GST_DEBUG ("buffer alloc on %s:%s", GST_DEBUG_PAD_NAME (target));
171 result = gst_pad_alloc_buffer (peer, offset, size, caps, buf);
173 gst_object_unref (peer);
175 result = GST_FLOW_NOT_LINKED;
178 gst_object_unref (target);
184 gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
186 GstPad *target = gst_proxy_pad_get_target (pad);
189 g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED);
191 res = gst_pad_chain (target, buffer);
192 gst_object_unref (target);
198 gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
201 GstPad *target = gst_proxy_pad_get_target (pad);
204 g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED);
206 res = gst_pad_get_range (target, offset, size, buffer);
207 gst_object_unref (target);
213 gst_proxy_pad_do_checkgetrange (GstPad * pad)
216 GstPad *target = gst_proxy_pad_get_target (pad);
219 g_return_val_if_fail (target != NULL, FALSE);
221 peer = gst_pad_get_peer (target);
223 result = gst_pad_check_pull_range (peer);
224 gst_object_unref (peer);
228 gst_object_unref (target);
234 gst_proxy_pad_do_getcaps (GstPad * pad)
236 GstPad *target = gst_proxy_pad_get_target (pad);
239 g_return_val_if_fail (target != NULL, NULL);
241 res = gst_pad_get_caps (target);
242 gst_object_unref (target);
248 gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
250 GstPad *target = gst_proxy_pad_get_target (pad);
253 g_return_val_if_fail (target != NULL, FALSE);
255 res = gst_pad_accept_caps (target, caps);
256 gst_object_unref (target);
262 gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
264 GstPad *target = gst_proxy_pad_get_target (pad);
266 g_return_if_fail (target != NULL);
268 gst_pad_fixate_caps (target, caps);
269 gst_object_unref (target);
273 gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
275 GstPad *target = gst_proxy_pad_get_target (pad);
278 g_return_val_if_fail (target != NULL, FALSE);
280 res = gst_pad_set_caps (target, caps);
281 gst_object_unref (target);
286 #define SETFUNC(member, kind) \
287 if (target->member) \
288 gst_pad_set_##kind##_function (pad, gst_proxy_pad_do_##kind)
291 gst_proxy_pad_set_target_unlocked (GstPad * pad, GstPad * target)
295 GST_DEBUG ("set target %s:%s on %s:%s",
296 GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (pad));
298 /* clear old target */
299 if ((oldtarget = GST_PROXY_PAD_TARGET (pad))) {
300 gst_object_unref (oldtarget);
301 GST_PROXY_PAD_TARGET (pad) = NULL;
305 /* set and ref new target if any */
306 GST_PROXY_PAD_TARGET (pad) = gst_object_ref (target);
308 /* really, all these should have default implementations so I can set them
309 * in the _init() instead of here */
310 SETFUNC (querytypefunc, query_type);
311 SETFUNC (eventfunc, event);
312 SETFUNC (queryfunc, query);
313 SETFUNC (intlinkfunc, internal_link);
314 SETFUNC (getcapsfunc, getcaps);
315 SETFUNC (acceptcapsfunc, acceptcaps);
316 SETFUNC (fixatecapsfunc, fixatecaps);
317 SETFUNC (setcapsfunc, setcaps);
319 if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
320 SETFUNC (bufferallocfunc, bufferalloc);
321 SETFUNC (chainfunc, chain);
323 SETFUNC (getrangefunc, getrange);
324 SETFUNC (checkgetrangefunc, checkgetrange);
331 gst_proxy_pad_set_target (GstPad * pad, GstPad * target)
335 GST_PROXY_LOCK (pad);
336 result = gst_proxy_pad_set_target_unlocked (pad, target);
337 GST_PROXY_UNLOCK (pad);
343 gst_proxy_pad_get_target (GstPad * pad)
347 GST_PROXY_LOCK (pad);
348 target = GST_PROXY_PAD_TARGET (pad);
349 gst_object_ref (target);
350 GST_PROXY_UNLOCK (pad);
356 gst_proxy_pad_init (GstProxyPad * pad)
358 pad->proxy_lock = g_mutex_new ();
362 gst_proxy_pad_dispose (GObject * object)
364 GstPad *pad = GST_PAD (object);
366 GST_PROXY_LOCK (pad);
367 gst_object_replace ((GstObject **) & GST_PROXY_PAD_TARGET (pad), NULL);
368 GST_PROXY_UNLOCK (pad);
370 G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
374 gst_proxy_pad_finalize (GObject * object)
376 GstProxyPad *pad = GST_PROXY_PAD (object);
378 g_mutex_free (pad->proxy_lock);
379 pad->proxy_lock = NULL;
381 G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
384 #ifndef GST_DISABLE_LOADSAVE
386 * gst_proxy_pad_save_thyself:
387 * @pad: a ghost #GstPad to save.
388 * @parent: the parent #xmlNodePtr to save the description in.
390 * Saves the ghost pad into an xml representation.
392 * Returns: the #xmlNodePtr representation of the pad.
395 gst_proxy_pad_save_thyself (GstObject * object, xmlNodePtr parent)
399 g_return_val_if_fail (GST_IS_PROXY_PAD (object), NULL);
401 self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
402 xmlNewChild (self, NULL, (xmlChar *) "name",
403 (xmlChar *) GST_OBJECT_NAME (object));
404 xmlNewChild (self, NULL, (xmlChar *) "parent",
405 (xmlChar *) GST_OBJECT_NAME (GST_OBJECT_PARENT (object)));
407 /* FIXME FIXME FIXME! */
411 #endif /* GST_DISABLE_LOADSAVE */
414 /***********************************************************************
415 * Ghost pads, implemented as a pair of proxy pads (sort of)
423 /* with PROXY_LOCK */
428 gpointer _gst_reserved[GST_PADDING];
431 struct _GstGhostPadClass
433 GstProxyPadClass parent_class;
436 gpointer _gst_reserved[GST_PADDING];
440 G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
442 static gboolean gst_ghost_pad_set_internal (GstGhostPad * pad,
445 static void gst_ghost_pad_dispose (GObject * object);
447 /* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we
448 * use for GST_PTR_FORMAT. */
450 gst_critical (const gchar * format, ...)
454 va_start (args, format);
455 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
460 gst_ghost_pad_get_internal (GstPad * pad)
464 GST_PROXY_LOCK (pad);
465 internal = GST_GHOST_PAD (pad)->internal;
466 gst_object_ref (internal);
467 GST_PROXY_UNLOCK (pad);
473 gst_ghost_pad_class_init (GstGhostPadClass * klass)
475 GObjectClass *gobject_class = (GObjectClass *) klass;
477 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
480 /* see gstghostpad design docs */
482 gst_ghost_pad_internal_do_activate_push (GstPad * pad, gboolean active)
486 if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
487 GstPad *parent = GST_PAD (gst_object_get_parent (GST_OBJECT (pad)));
490 g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE);
492 ret = gst_pad_activate_push (parent, active);
494 gst_object_unref (parent);
499 GstPad *peer = gst_pad_get_peer (pad);
502 ret = gst_pad_activate_push (peer, active);
503 gst_object_unref (peer);
513 gst_ghost_pad_internal_do_activate_pull (GstPad * pad, gboolean active)
517 if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
518 GstPad *peer = gst_pad_get_peer (pad);
521 ret = gst_pad_activate_pull (peer, active);
522 gst_object_unref (peer);
527 GstPad *parent = GST_PAD (gst_object_get_parent (GST_OBJECT (pad)));
530 g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE);
532 ret = gst_pad_activate_pull (parent, active);
534 gst_object_unref (parent);
544 gst_ghost_pad_do_activate_push (GstPad * pad, gboolean active)
548 if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
549 GstPad *internal = gst_ghost_pad_get_internal (pad);
552 ret = gst_pad_activate_push (internal, active);
553 gst_object_unref (internal);
565 gst_ghost_pad_do_activate_pull (GstPad * pad, gboolean active)
569 if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
570 GstPad *peer = gst_pad_get_peer (pad);
573 ret = gst_pad_activate_pull (peer, active);
574 gst_object_unref (peer);
579 GstPad *internal = gst_ghost_pad_get_internal (pad);
582 ret = gst_pad_activate_pull (internal, active);
583 gst_object_unref (internal);
592 static GstPadLinkReturn
593 gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
595 GstPad *internal, *target;
596 GstPadLinkReturn ret;
598 target = gst_proxy_pad_get_target (pad);
600 g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED);
602 /* proxy the peer into the bin */
603 internal = g_object_new (GST_TYPE_PROXY_PAD,
605 "direction", GST_PAD_DIRECTION (peer),
606 "template", GST_PAD_PAD_TEMPLATE (peer), NULL);
608 gst_proxy_pad_set_target (internal, peer);
609 gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), internal);
611 if (GST_PAD_IS_SRC (internal))
612 ret = gst_pad_link (internal, target);
614 ret = gst_pad_link (target, internal);
616 gst_object_unref (target);
618 if (ret == GST_PAD_LINK_OK)
619 gst_pad_set_active (internal, GST_PAD_ACTIVATE_MODE (pad));
621 gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), NULL);
627 gst_ghost_pad_do_unlink (GstPad * pad)
629 GstPad *target = gst_proxy_pad_get_target (pad);
631 g_return_if_fail (target != NULL);
633 GST_DEBUG_OBJECT (pad, "unlinking ghostpad");
635 if (target->unlinkfunc)
636 target->unlinkfunc (target);
638 gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), NULL);
640 gst_object_unref (target);
644 on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
648 g_object_get (internal, "caps", &caps, NULL);
651 gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
654 g_object_notify (G_OBJECT (pad), "caps");
656 gst_caps_unref (caps);
660 gst_ghost_pad_set_internal (GstGhostPad * pad, GstPad * internal)
662 GST_PROXY_LOCK (pad);
663 /* first remove old internal pad */
667 gst_pad_set_activatepull_function (pad->internal, NULL);
668 gst_pad_set_activatepush_function (pad->internal, NULL);
670 g_signal_handler_disconnect (pad->internal, pad->notify_id);
672 intpeer = gst_pad_get_peer (pad->internal);
674 if (GST_PAD_IS_SRC (pad->internal))
675 gst_pad_unlink (pad->internal, intpeer);
677 gst_pad_unlink (intpeer, pad->internal);
678 gst_object_unref (intpeer);
680 /* should dispose it */
681 gst_object_unparent (GST_OBJECT_CAST (pad->internal));
684 /* then set new internal pad */
686 if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
687 GST_OBJECT_CAST (pad)))
690 /* could be more general here, iterating over all writable properties...
691 * taking the short road for now tho */
692 pad->notify_id = g_signal_connect (internal, "notify::caps",
693 G_CALLBACK (on_int_notify), pad);
694 on_int_notify (internal, NULL, pad);
695 gst_pad_set_activatepull_function (GST_PAD (internal),
696 gst_ghost_pad_internal_do_activate_pull);
697 gst_pad_set_activatepush_function (GST_PAD (internal),
698 gst_ghost_pad_internal_do_activate_push);
699 /* a ref was taken by set_parent */
701 pad->internal = internal;
703 GST_PROXY_UNLOCK (pad);
710 gst_critical ("Could not set internal pad %" GST_PTR_FORMAT, internal);
711 GST_PROXY_UNLOCK (pad);
717 gst_ghost_pad_init (GstGhostPad * pad)
719 gst_pad_set_activatepull_function (GST_PAD (pad),
720 gst_ghost_pad_do_activate_pull);
721 gst_pad_set_activatepush_function (GST_PAD (pad),
722 gst_ghost_pad_do_activate_push);
726 gst_ghost_pad_dispose (GObject * object)
728 gst_ghost_pad_set_internal (GST_GHOST_PAD (object), NULL);
730 G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
734 * gst_ghost_pad_new_notarget:
735 * @name: the name of the new pad, or NULL to assign a default name.
736 * @dir: the direction of the ghostpad
738 * Create a new ghostpad without a target with the given direction.
739 * A target can be set on the ghostpad later with the
740 * #gst_ghost_pad_set_target() function.
742 * The created ghostpad will not have a padtemplate.
744 * Returns: a new #GstPad, or NULL in case of an error.
747 gst_ghost_pad_new_notarget (const gchar * name, GstPadDirection dir)
751 ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name, "direction", dir, NULL);
753 gst_pad_set_activatepush_function (ret, gst_ghost_pad_do_activate_push);
754 gst_pad_set_link_function (ret, gst_ghost_pad_do_link);
755 gst_pad_set_unlink_function (ret, gst_ghost_pad_do_unlink);
762 * @name: the name of the new pad, or NULL to assign a default name.
763 * @target: the pad to ghost.
765 * Create a new ghostpad with @target as the target. The direction and
766 * padtemplate will be taken from the target pad.
768 * Will ref the target.
770 * Returns: a new #GstPad, or NULL in case of an error.
773 gst_ghost_pad_new (const gchar * name, GstPad * target)
777 g_return_val_if_fail (GST_IS_PAD (target), NULL);
778 g_return_val_if_fail (!gst_pad_is_linked (target), NULL);
780 if ((ret = gst_ghost_pad_new_notarget (name, GST_PAD_DIRECTION (target)))) {
781 g_object_set (G_OBJECT (ret),
782 "template", GST_PAD_PAD_TEMPLATE (target), NULL);
783 gst_ghost_pad_set_target (GST_GHOST_PAD (ret), target);
789 * gst_ghost_pad_get_target:
790 * @gpad: the #GstGhostpad
792 * Get the target pad of #gpad. Unref after usage.
794 * Returns: the target #GstPad, can be NULL if the ghostpad
795 * has no target set. Unref after usage.
798 gst_ghost_pad_get_target (GstGhostPad * gpad)
800 g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), NULL);
802 return gst_proxy_pad_get_target (GST_PAD_CAST (gpad));
806 * gst_ghost_pad_set_target:
807 * @gpad: the #GstGhostpad
808 * @newtarget: the new pad target
810 * Set the new target of the ghostpad @gpad. Any existing target
813 * Returns: TRUE if the new target could be set, FALSE otherwise.
816 gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget)
822 g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE);
824 GST_PROXY_LOCK (gpad);
825 internal = gpad->internal;
827 GST_DEBUG ("set target %s:%s on %s:%s",
828 GST_DEBUG_PAD_NAME (newtarget), GST_DEBUG_PAD_NAME (gpad));
830 /* clear old target */
831 if ((oldtarget = GST_PROXY_PAD_TARGET (gpad))) {
832 /* if we have an internal pad, unlink */
834 if (GST_PAD_IS_SRC (internal))
835 gst_pad_unlink (internal, oldtarget);
837 gst_pad_unlink (oldtarget, internal);
841 result = gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), newtarget);
843 if (result && newtarget) {
844 /* and link to internal pad if we have one */
846 if (GST_PAD_IS_SRC (internal))
847 result = gst_pad_link (internal, newtarget);
849 result = gst_pad_link (newtarget, internal);
852 GST_PROXY_UNLOCK (gpad);