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 GstFlowReturn result;
156 GstPad *target = GST_PROXY_PAD_TARGET (pad);
159 g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
161 peer = gst_pad_get_peer (target);
163 GST_DEBUG ("buffer alloc on %s:%s", GST_DEBUG_PAD_NAME (target));
165 result = gst_pad_alloc_buffer (peer, offset, size, caps, buf);
167 gst_object_unref (peer);
169 result = GST_FLOW_NOT_LINKED;
176 gst_proxy_pad_do_activate (GstPad * pad)
178 GstPad *target = GST_PROXY_PAD_TARGET (pad);
180 g_return_val_if_fail (target != NULL, FALSE);
182 return gst_pad_activate_push (pad, TRUE);
186 gst_proxy_pad_do_activatepull (GstPad * pad, gboolean active)
189 GstPad *target = GST_PROXY_PAD_TARGET (pad);
191 g_return_val_if_fail (target != NULL, FALSE);
194 old = GST_PAD_ACTIVATE_MODE (target);
197 if ((active && old == GST_ACTIVATE_PULL)
198 || (!active && old == GST_ACTIVATE_NONE))
201 return gst_pad_activate_pull (target, active);
205 gst_proxy_pad_do_activatepush (GstPad * pad, gboolean active)
208 GstPad *target = GST_PROXY_PAD_TARGET (pad);
210 g_return_val_if_fail (target != NULL, FALSE);
213 old = GST_PAD_ACTIVATE_MODE (target);
216 if ((active && old == GST_ACTIVATE_PUSH)
217 || (!active && old == GST_ACTIVATE_NONE))
220 return gst_pad_activate_push (target, active);
224 gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
226 GstPad *target = GST_PROXY_PAD_TARGET (pad);
228 g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
230 return gst_pad_chain (target, buffer);
234 gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
237 GstPad *target = GST_PROXY_PAD_TARGET (pad);
239 g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
241 return gst_pad_get_range (target, offset, size, buffer);
245 gst_proxy_pad_do_checkgetrange (GstPad * pad)
247 GstPad *target = GST_PROXY_PAD_TARGET (pad);
249 g_return_val_if_fail (target != NULL, FALSE);
251 return gst_pad_check_pull_range (target);
255 gst_proxy_pad_do_getcaps (GstPad * pad)
257 GstPad *target = GST_PROXY_PAD_TARGET (pad);
259 g_return_val_if_fail (target != NULL, NULL);
261 return gst_pad_get_caps (target);
265 gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
267 GstPad *target = GST_PROXY_PAD_TARGET (pad);
269 g_return_val_if_fail (target != NULL, FALSE);
271 return gst_pad_accept_caps (target, caps);
275 gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
277 GstPad *target = GST_PROXY_PAD_TARGET (pad);
279 g_return_val_if_fail (target != NULL, NULL);
281 return gst_pad_fixate_caps (target, caps);
285 gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
287 GstPad *target = GST_PROXY_PAD_TARGET (pad);
289 g_return_val_if_fail (target != NULL, FALSE);
291 return gst_pad_set_caps (target, caps);
294 #define SETFUNC(member, kind) \
295 if (target->member) \
296 gst_pad_set_##kind##_function (pad, gst_proxy_pad_do_##kind)
299 gst_proxy_pad_set_property (GObject * object, guint prop_id,
300 const GValue * value, GParamSpec * pspec)
302 GstPad *pad = GST_PAD (object);
305 case PROXY_PROP_TARGET:{
308 target = GST_PAD_CAST (gst_object_ref
309 (GST_OBJECT_CAST (g_value_get_object (value))));
310 GST_PROXY_PAD_TARGET (object) = target;
312 /* really, all these should have default implementations so I can set them
313 * in the _init() instead of here */
314 SETFUNC (querytypefunc, query_type);
315 SETFUNC (eventfunc, event);
316 SETFUNC (queryfunc, query);
317 SETFUNC (intlinkfunc, internal_link);
318 SETFUNC (activatefunc, activate);
319 SETFUNC (activatepullfunc, activatepull);
320 SETFUNC (activatepushfunc, activatepush);
321 SETFUNC (getcapsfunc, getcaps);
322 SETFUNC (acceptcapsfunc, acceptcaps);
323 SETFUNC (fixatecapsfunc, fixatecaps);
324 SETFUNC (setcapsfunc, setcaps);
326 if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
327 SETFUNC (bufferallocfunc, bufferalloc);
328 SETFUNC (chainfunc, chain);
330 SETFUNC (getrangefunc, getrange);
331 SETFUNC (checkgetrangefunc, checkgetrange);
337 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
343 gst_proxy_pad_get_property (GObject * object, guint prop_id,
344 GValue * value, GParamSpec * pspec)
347 case PROXY_PROP_TARGET:
348 g_value_set_object (value, GST_PROXY_PAD_TARGET (object));
351 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
357 gst_proxy_pad_init (GstProxyPad * pad)
359 pad->property_lock = g_mutex_new ();
363 gst_proxy_pad_dispose (GObject * object)
365 GstPad *pad = GST_PAD (object);
367 if (GST_PROXY_PAD_TARGET (pad)) {
368 gst_object_replace ((GstObject **) & GST_PROXY_PAD_TARGET (pad), NULL);
371 G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
375 gst_proxy_pad_finalize (GObject * object)
377 GstProxyPad *pad = GST_PROXY_PAD (object);
379 g_mutex_free (pad->property_lock);
380 pad->property_lock = NULL;
382 G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
385 #ifndef GST_DISABLE_LOADSAVE
387 * gst_proxy_pad_save_thyself:
388 * @pad: a ghost #GstPad to save.
389 * @parent: the parent #xmlNodePtr to save the description in.
391 * Saves the ghost pad into an xml representation.
393 * Returns: the #xmlNodePtr representation of the pad.
396 gst_proxy_pad_save_thyself (GstObject * object, xmlNodePtr parent)
400 g_return_val_if_fail (GST_IS_PROXY_PAD (object), NULL);
402 self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
403 xmlNewChild (self, NULL, (xmlChar *) "name",
404 (xmlChar *) GST_OBJECT_NAME (object));
405 xmlNewChild (self, NULL, (xmlChar *) "parent",
406 (xmlChar *) GST_OBJECT_NAME (GST_OBJECT_PARENT (object)));
408 /* FIXME FIXME FIXME! */
412 #endif /* GST_DISABLE_LOADSAVE */
415 /***********************************************************************
416 * Ghost pads, implemented as a pair of proxy pads (sort of)
434 gpointer _gst_reserved[GST_PADDING];
437 struct _GstGhostPadClass
439 GstProxyPadClass parent_class;
442 gpointer _gst_reserved[GST_PADDING];
446 G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
449 static void gst_ghost_pad_dispose (GObject * object);
450 static void gst_ghost_pad_set_property (GObject * object, guint prop_id,
451 const GValue * value, GParamSpec * pspec);
452 static void gst_ghost_pad_get_property (GObject * object, guint prop_id,
453 GValue * value, GParamSpec * pspec);
456 /* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we
457 * use for GST_PTR_FORMAT. */
459 gst_critical (const gchar * format, ...)
463 va_start (args, format);
464 g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
469 gst_ghost_pad_class_init (GstGhostPadClass * klass)
471 GObjectClass *gobject_class = (GObjectClass *) klass;
473 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
474 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_set_property);
475 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_get_property);
477 g_object_class_install_property (G_OBJECT_CLASS (klass), GHOST_PROP_INTERNAL,
478 g_param_spec_object ("internal", "Internal",
479 "The ghost pad's internal pad", GST_TYPE_PAD, G_PARAM_READWRITE));
482 /* will only be called for src pads (afaict) */
484 gst_ghost_proxy_pad_do_activate_pull (GstPad * pad, gboolean active)
487 gboolean ret = FALSE;
489 parent = gst_object_get_parent (GST_OBJECT (pad));
492 if (GST_IS_GHOST_PAD (parent))
493 ret = gst_pad_activate_pull (GST_PAD (parent), active);
495 gst_object_unref (parent);
501 static GstPadLinkReturn
502 gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
504 GstPad *internal, *target;
506 target = GST_PROXY_PAD_TARGET (pad);
507 g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED);
509 /* proxy the peer into the bin */
510 internal = g_object_new (GST_TYPE_PROXY_PAD,
512 "direction", GST_PAD_DIRECTION (peer),
513 "template", GST_PAD_PAD_TEMPLATE (peer), "target", peer, NULL);
514 g_object_set (pad, "internal", internal, NULL);
516 if ((GST_PAD_IS_SRC (internal) &&
517 gst_pad_link (internal, target) == GST_PAD_LINK_OK) ||
518 (GST_PAD_IS_SINK (internal) &&
519 (gst_pad_link (target, internal) == GST_PAD_LINK_OK))) {
520 gst_pad_set_active (internal, GST_PAD_ACTIVATE_MODE (pad));
521 return GST_PAD_LINK_OK;
523 g_object_set (pad, "internal", NULL, NULL);
524 return GST_PAD_LINK_REFUSED;
529 gst_ghost_pad_do_unlink (GstPad * pad)
531 GstPad *target = GST_PROXY_PAD_TARGET (pad);
533 g_return_if_fail (target != NULL);
535 if (target->unlinkfunc)
536 target->unlinkfunc (target);
538 /* FIXME: should do this here, but locks in deep_notify prevent it */
539 /* g_object_set (pad, "internal", NULL, NULL); */
543 on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
547 g_object_get (internal, "caps", &caps, NULL);
550 gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
553 g_object_notify (G_OBJECT (pad), "caps");
555 gst_caps_unref (caps);
559 gst_ghost_pad_set_property (GObject * object, guint prop_id,
560 const GValue * value, GParamSpec * pspec)
562 GstGhostPad *pad = GST_GHOST_PAD (object);
565 case GHOST_PROP_INTERNAL:{
568 g_mutex_lock (GST_PROXY_PAD (pad)->property_lock);
573 gst_pad_set_activatepull_function (pad->internal, NULL);
575 g_signal_handler_disconnect (pad->internal, pad->notify_id);
577 intpeer = gst_pad_get_peer (pad->internal);
579 if (GST_PAD_IS_SRC (pad->internal)) {
580 gst_pad_unlink (pad->internal, intpeer);
582 gst_pad_unlink (intpeer, pad->internal);
584 gst_object_unref (intpeer);
587 /* should dispose it */
588 gst_object_unparent (GST_OBJECT_CAST (pad->internal));
591 internal = g_value_get_object (value); /* no extra refcount... */
594 if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
595 GST_OBJECT_CAST (pad))) {
596 gst_critical ("Could not set internal pad %" GST_PTR_FORMAT,
598 g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
602 /* could be more general here, iterating over all writable properties...
603 * taking the short road for now tho */
604 pad->notify_id = g_signal_connect (internal, "notify::caps",
605 G_CALLBACK (on_int_notify), pad);
606 on_int_notify (internal, NULL, pad);
607 gst_pad_set_activatepull_function (internal,
608 gst_ghost_proxy_pad_do_activate_pull);
610 /* a ref was taken by set_parent */
613 pad->internal = internal;
615 g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
620 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
626 gst_ghost_pad_get_property (GObject * object, guint prop_id,
627 GValue * value, GParamSpec * pspec)
630 case GHOST_PROP_INTERNAL:
631 g_value_set_object (value, GST_GHOST_PAD (object)->internal);
634 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
640 gst_ghost_pad_init (GstGhostPad * pad)
646 gst_ghost_pad_dispose (GObject * object)
648 g_object_set (object, "internal", NULL, NULL);
650 G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
655 * @name: the name of the new pad, or NULL to assign a default name.
656 * @target: the pad to ghost.
658 * Will ref the target.
660 * Returns: a new #GstPad, or NULL in case of an error.
663 gst_ghost_pad_new (const gchar * name, GstPad * target)
667 g_return_val_if_fail (GST_IS_PAD (target), NULL);
668 g_return_val_if_fail (!GST_PAD_IS_LINKED (target), NULL);
670 ret = g_object_new (GST_TYPE_GHOST_PAD,
672 "direction", GST_PAD_DIRECTION (target),
673 "template", GST_PAD_PAD_TEMPLATE (target), "target", target, NULL);
675 gst_pad_set_link_function (ret, gst_ghost_pad_do_link);
676 gst_pad_set_unlink_function (ret, gst_ghost_pad_do_unlink);