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 #include "gst_private.h"
27 #include "gstghostpad.h"
28 #include "gstelement.h"
32 #define GST_TYPE_PROXY_PAD (gst_proxy_pad_get_type ())
33 #define GST_IS_PROXY_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PROXY_PAD))
34 #define GST_IS_PROXY_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PROXY_PAD))
35 #define GST_PROXY_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PROXY_PAD, GstProxyPad))
36 #define GST_PROXY_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PROXY_PAD, GstProxyPadClass))
37 #define GST_PROXY_PAD_TARGET(pad) (GST_PROXY_PAD (pad)->target)
40 typedef struct _GstProxyPad GstProxyPad;
41 typedef struct _GstProxyPadClass GstProxyPadClass;
56 GMutex *property_lock;
59 gpointer _gst_reserved[1];
62 struct _GstProxyPadClass
64 GstPadClass parent_class;
67 gpointer _gst_reserved[1];
71 G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);
74 static void gst_proxy_pad_dispose (GObject * object);
75 static void gst_proxy_pad_set_property (GObject * object, guint prop_id,
76 const GValue * value, GParamSpec * pspec);
77 static void gst_proxy_pad_get_property (GObject * object, guint prop_id,
78 GValue * value, GParamSpec * pspec);
79 static void gst_proxy_pad_finalize (GObject * object);
81 #ifndef GST_DISABLE_LOADSAVE
82 static xmlNodePtr gst_proxy_pad_save_thyself (GstObject * object,
88 gst_proxy_pad_class_init (GstProxyPadClass * klass)
90 GObjectClass *gobject_class = (GObjectClass *) klass;
92 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_proxy_pad_dispose);
93 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_proxy_pad_finalize);
94 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_proxy_pad_set_property);
95 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_proxy_pad_get_property);
97 g_object_class_install_property (G_OBJECT_CLASS (klass), PROXY_PROP_TARGET,
98 g_param_spec_object ("target", "Target", "The proxy pad's target",
99 GST_TYPE_PAD, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
101 #ifndef GST_DISABLE_LOADSAVE
103 GstObjectClass *gstobject_class = (GstObjectClass *) klass;
105 gstobject_class->save_thyself =
106 GST_DEBUG_FUNCPTR (gst_proxy_pad_save_thyself);
112 gst_proxy_pad_do_query_type (GstPad * pad)
114 GstPad *target = GST_PROXY_PAD_TARGET (pad);
116 g_return_val_if_fail (target != NULL, NULL);
118 return gst_pad_get_query_types (target);
122 gst_proxy_pad_do_event (GstPad * pad, GstEvent * event)
124 GstPad *target = GST_PROXY_PAD_TARGET (pad);
126 g_return_val_if_fail (target != NULL, FALSE);
128 return gst_pad_send_event (target, event);
132 gst_proxy_pad_do_query (GstPad * pad, GstQuery * query)
134 GstPad *target = GST_PROXY_PAD_TARGET (pad);
136 g_return_val_if_fail (target != NULL, FALSE);
138 return gst_pad_query (target, query);
142 gst_proxy_pad_do_internal_link (GstPad * pad)
144 GstPad *target = GST_PROXY_PAD_TARGET (pad);
146 g_return_val_if_fail (target != NULL, NULL);
148 return gst_pad_get_internal_links (target);
152 gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size,
153 GstCaps * caps, GstBuffer ** buf)
155 GstPad *target = GST_PROXY_PAD_TARGET (pad);
157 g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
159 return target->bufferallocfunc (target, offset, size, caps, buf);
163 gst_proxy_pad_do_activate (GstPad * pad)
165 GstPad *target = GST_PROXY_PAD_TARGET (pad);
167 g_return_val_if_fail (target != NULL, FALSE);
169 return gst_pad_activate_push (pad, TRUE);
173 gst_proxy_pad_do_activatepull (GstPad * pad, gboolean active)
176 GstPad *target = GST_PROXY_PAD_TARGET (pad);
178 g_return_val_if_fail (target != NULL, FALSE);
181 old = GST_PAD_ACTIVATE_MODE (target);
184 if ((active && old == GST_ACTIVATE_PULL)
185 || (!active && old == GST_ACTIVATE_NONE))
188 return gst_pad_activate_pull (target, active);
192 gst_proxy_pad_do_activatepush (GstPad * pad, gboolean active)
195 GstPad *target = GST_PROXY_PAD_TARGET (pad);
197 g_return_val_if_fail (target != NULL, FALSE);
200 old = GST_PAD_ACTIVATE_MODE (target);
203 if ((active && old == GST_ACTIVATE_PUSH)
204 || (!active && old == GST_ACTIVATE_NONE))
207 return gst_pad_activate_push (target, active);
211 gst_proxy_pad_do_loop (GstPad * pad)
213 GstPad *target = GST_PROXY_PAD_TARGET (pad);
215 g_return_if_fail (target != NULL);
217 target->loopfunc (target);
221 gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
223 GstPad *target = GST_PROXY_PAD_TARGET (pad);
225 g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
227 return gst_pad_chain (target, buffer);
231 gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
234 GstPad *target = GST_PROXY_PAD_TARGET (pad);
236 g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
238 return target->getrangefunc (target, offset, size, buffer);
242 gst_proxy_pad_do_checkgetrange (GstPad * pad)
244 GstPad *target = GST_PROXY_PAD_TARGET (pad);
246 g_return_val_if_fail (target != NULL, FALSE);
248 return target->checkgetrangefunc (target);
252 gst_proxy_pad_do_getcaps (GstPad * pad)
254 GstPad *target = GST_PROXY_PAD_TARGET (pad);
256 g_return_val_if_fail (target != NULL, NULL);
258 return target->getcapsfunc (target);
262 gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
264 GstPad *target = GST_PROXY_PAD_TARGET (pad);
266 g_return_val_if_fail (target != NULL, FALSE);
268 return target->acceptcapsfunc (target, caps);
272 gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
274 GstPad *target = GST_PROXY_PAD_TARGET (pad);
276 g_return_val_if_fail (target != NULL, NULL);
278 return target->fixatecapsfunc (target, caps);
282 gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
284 GstPad *target = GST_PROXY_PAD_TARGET (pad);
286 g_return_val_if_fail (target != NULL, FALSE);
288 return gst_pad_set_caps (target, caps);
291 #define SETFUNC(member, kind) \
292 if (target->member) \
293 gst_pad_set_##kind##_function (pad, gst_proxy_pad_do_##kind)
296 gst_proxy_pad_set_property (GObject * object, guint prop_id,
297 const GValue * value, GParamSpec * pspec)
299 GstPad *pad = GST_PAD (object);
302 case PROXY_PROP_TARGET:{
305 target = GST_PAD_CAST (gst_object_ref
306 (GST_OBJECT_CAST (g_value_get_object (value))));
307 GST_PROXY_PAD_TARGET (object) = target;
309 /* really, all these should have default implementations so I can set them
310 * in the _init() instead of here */
311 SETFUNC (querytypefunc, query_type);
312 SETFUNC (eventfunc, event);
313 SETFUNC (queryfunc, query);
314 SETFUNC (intlinkfunc, internal_link);
315 SETFUNC (activatefunc, activate);
316 SETFUNC (activatepullfunc, activatepull);
317 SETFUNC (activatepushfunc, activatepush);
318 SETFUNC (loopfunc, loop);
319 SETFUNC (getcapsfunc, getcaps);
320 SETFUNC (acceptcapsfunc, acceptcaps);
321 SETFUNC (fixatecapsfunc, fixatecaps);
322 SETFUNC (setcapsfunc, setcaps);
324 if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
325 SETFUNC (bufferallocfunc, bufferalloc);
326 SETFUNC (chainfunc, chain);
328 SETFUNC (getrangefunc, getrange);
329 SETFUNC (checkgetrangefunc, checkgetrange);
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
341 gst_proxy_pad_get_property (GObject * object, guint prop_id,
342 GValue * value, GParamSpec * pspec)
345 case PROXY_PROP_TARGET:
346 g_value_set_object (value, GST_PROXY_PAD_TARGET (object));
349 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
355 gst_proxy_pad_init (GstProxyPad * pad)
357 pad->property_lock = g_mutex_new ();
361 gst_proxy_pad_dispose (GObject * object)
363 GstPad *pad = GST_PAD (object);
365 if (GST_PROXY_PAD_TARGET (pad)) {
366 gst_object_replace ((GstObject **) & GST_PROXY_PAD_TARGET (pad), NULL);
369 G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
373 gst_proxy_pad_finalize (GObject * object)
375 GstProxyPad *pad = GST_PROXY_PAD (object);
377 g_mutex_free (pad->property_lock);
378 pad->property_lock = NULL;
380 G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
383 #ifndef GST_DISABLE_LOADSAVE
385 * gst_proxy_pad_save_thyself:
386 * @pad: a ghost #GstPad to save.
387 * @parent: the parent #xmlNodePtr to save the description in.
389 * Saves the ghost pad into an xml representation.
391 * Returns: the #xmlNodePtr representation of the pad.
394 gst_proxy_pad_save_thyself (GstObject * object, xmlNodePtr parent)
398 g_return_val_if_fail (GST_IS_PROXY_PAD (object), NULL);
400 self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
401 xmlNewChild (self, NULL, (xmlChar *) "name",
402 (xmlChar *) GST_OBJECT_NAME (object));
403 xmlNewChild (self, NULL, (xmlChar *) "parent",
404 (xmlChar *) GST_OBJECT_NAME (GST_OBJECT_PARENT (object)));
406 /* FIXME FIXME FIXME! */
410 #endif /* GST_DISABLE_LOADSAVE */
413 /***********************************************************************
414 * Ghost pads, implemented as a pair of proxy pads (sort of)
432 gpointer _gst_reserved[GST_PADDING];
435 struct _GstGhostPadClass
437 GstProxyPadClass parent_class;
440 gpointer _gst_reserved[GST_PADDING];
444 G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
447 static void gst_ghost_pad_dispose (GObject * object);
448 static void gst_ghost_pad_set_property (GObject * object, guint prop_id,
449 const GValue * value, GParamSpec * pspec);
450 static void gst_ghost_pad_get_property (GObject * object, guint prop_id,
451 GValue * value, GParamSpec * pspec);
454 /* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we
455 * use for GST_PTR_FORMAT. */
457 gst_critical (const gchar * format, ...)
461 va_start (args, format);
462 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
467 gst_ghost_pad_class_init (GstGhostPadClass * klass)
469 GObjectClass *gobject_class = (GObjectClass *) klass;
471 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
472 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_set_property);
473 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_get_property);
475 g_object_class_install_property (G_OBJECT_CLASS (klass), GHOST_PROP_INTERNAL,
476 g_param_spec_object ("internal", "Internal",
477 "The ghost pad's internal pad", GST_TYPE_PAD, G_PARAM_READWRITE));
480 /* will only be called for src pads (afaict) */
482 gst_ghost_proxy_pad_do_activate_pull (GstPad * pad, gboolean active)
485 gboolean ret = FALSE;
487 parent = gst_object_get_parent (GST_OBJECT (pad));
490 if (GST_IS_GHOST_PAD (parent))
491 ret = gst_pad_activate_pull (GST_PAD (parent), active);
493 gst_object_unref (parent);
499 static GstPadLinkReturn
500 gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
502 GstPad *internal, *target;
504 target = GST_PROXY_PAD_TARGET (pad);
505 g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED);
507 /* proxy the peer into the bin */
508 internal = g_object_new (GST_TYPE_PROXY_PAD,
510 "direction", GST_PAD_DIRECTION (peer),
511 "template", GST_PAD_PAD_TEMPLATE (peer), "target", peer, NULL);
512 g_object_set (pad, "internal", internal, NULL);
514 if ((GST_PAD_IS_SRC (internal) &&
515 gst_pad_link (internal, target) == GST_PAD_LINK_OK) ||
516 (GST_PAD_IS_SINK (internal) &&
517 (gst_pad_link (target, internal) == GST_PAD_LINK_OK))) {
518 gst_pad_set_active (internal, GST_PAD_ACTIVATE_MODE (pad));
519 return GST_PAD_LINK_OK;
521 g_object_set (pad, "internal", NULL, NULL);
522 return GST_PAD_LINK_REFUSED;
527 gst_ghost_pad_do_unlink (GstPad * pad)
529 GstPad *target = GST_PROXY_PAD_TARGET (pad);
531 g_return_if_fail (target != NULL);
533 if (target->unlinkfunc)
534 target->unlinkfunc (target);
536 /* FIXME: should do this here, but locks in deep_notify prevent it */
537 /* g_object_set (pad, "internal", NULL, NULL); */
541 on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
545 g_object_get (internal, "caps", &caps, NULL);
548 gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
551 g_object_notify (G_OBJECT (pad), "caps");
553 gst_caps_unref (caps);
557 gst_ghost_pad_set_property (GObject * object, guint prop_id,
558 const GValue * value, GParamSpec * pspec)
560 GstGhostPad *pad = GST_GHOST_PAD (object);
563 case GHOST_PROP_INTERNAL:{
566 g_mutex_lock (GST_PROXY_PAD (pad)->property_lock);
571 gst_pad_set_activatepull_function (pad->internal, NULL);
573 g_signal_handler_disconnect (pad->internal, pad->notify_id);
575 intpeer = gst_pad_get_peer (pad->internal);
577 if (GST_PAD_IS_SRC (pad->internal)) {
578 gst_pad_unlink (pad->internal, intpeer);
580 gst_pad_unlink (intpeer, pad->internal);
582 gst_object_unref (GST_OBJECT (intpeer));
585 /* should dispose it */
586 gst_object_unparent (GST_OBJECT_CAST (pad->internal));
589 internal = g_value_get_object (value); /* no extra refcount... */
592 if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
593 GST_OBJECT_CAST (pad))) {
594 gst_critical ("Could not set internal pad %" GST_PTR_FORMAT,
596 g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
600 /* could be more general here, iterating over all writable properties...
601 * taking the short road for now tho */
602 pad->notify_id = g_signal_connect (internal, "notify::caps",
603 G_CALLBACK (on_int_notify), pad);
604 on_int_notify (internal, NULL, pad);
605 gst_pad_set_activatepull_function (internal,
606 gst_ghost_proxy_pad_do_activate_pull);
608 /* a ref was taken by set_parent */
611 pad->internal = internal;
613 g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
618 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
624 gst_ghost_pad_get_property (GObject * object, guint prop_id,
625 GValue * value, GParamSpec * pspec)
628 case GHOST_PROP_INTERNAL:
629 g_value_set_object (value, GST_GHOST_PAD (object)->internal);
632 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
638 gst_ghost_pad_init (GstGhostPad * pad)
644 gst_ghost_pad_dispose (GObject * object)
646 g_object_set (object, "internal", NULL, NULL);
648 G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
653 * @name: the name of the new pad, or NULL to assign a default name.
654 * @target: the pad to ghost.
656 * Will ref the target.
658 * Returns: a new #GstPad, or NULL in case of an error.
661 gst_ghost_pad_new (const gchar * name, GstPad * target)
665 g_return_val_if_fail (GST_IS_PAD (target), NULL);
666 g_return_val_if_fail (!GST_PAD_IS_LINKED (target), NULL);
668 ret = g_object_new (GST_TYPE_GHOST_PAD,
670 "direction", GST_PAD_DIRECTION (target),
671 "template", GST_PAD_PAD_TEMPLATE (target), "target", target, NULL);
673 gst_pad_set_link_function (ret, gst_ghost_pad_do_link);
674 gst_pad_set_unlink_function (ret, gst_ghost_pad_do_unlink);