gstreamer/gst/gstghostpad.c
Edward Hervey 4f9d5adb40 gst/gstbin.c: (de)activate src pads before calling state_change on the childs.
Original commit message from CVS:
* gst/gstbin.c: (activate_pads),
(iterator_activate_fold_with_resync), (gst_bin_src_pads_activate),
(gst_bin_change_state_func):
(de)activate src pads before calling state_change on the childs.
This is to avoid the case where a src ghostpad is blocked (holding the
stream lock), which would block the deactivation of the ghostpad's
target pad.
* gst/gstghostpad.c: (gst_proxy_pad_do_query_type),
(gst_proxy_pad_do_event), (gst_proxy_pad_do_query),
(gst_proxy_pad_do_internal_link), (gst_proxy_pad_do_bufferalloc),
(gst_proxy_pad_do_chain), (gst_proxy_pad_do_getrange),
(gst_proxy_pad_do_checkgetrange), (gst_proxy_pad_do_getcaps),
(gst_proxy_pad_do_acceptcaps), (gst_proxy_pad_do_fixatecaps),
(gst_proxy_pad_do_setcaps), (gst_proxy_pad_set_target_unlocked),
(gst_proxy_pad_set_target), (gst_proxy_pad_get_internal),
(gst_proxy_pad_dispose), (gst_proxy_pad_init),
(gst_ghost_pad_parent_set), (gst_ghost_pad_parent_unset),
(gst_ghost_pad_class_init),
(gst_ghost_pad_internal_do_activate_push),
(gst_ghost_pad_internal_do_activate_pull),
(gst_ghost_pad_do_activate_push), (gst_ghost_pad_do_activate_pull),
(gst_ghost_pad_do_link), (gst_ghost_pad_do_unlink),
(gst_ghost_pad_dispose), (gst_ghost_pad_new_no_target),
(gst_ghost_pad_new), (gst_ghost_pad_set_target):
GhostPads now create their internal GstProxyPad at creation (and not
when they're linked, as it was being done previously).
The internal and target pads are linked straight away.
The data will also travel through the other pad in order to make
pad blocking and probes non-hackish (the probe/block now really happens
on the GhostPad and not on the target).
* gst/gstpad.c: (gst_pad_set_blocked_async),
(gst_pad_link_prepare), (gst_pad_push_event):
Remove previous ghostpad cruft.
* gst/gstutils.c: (gst_pad_add_data_probe),
(gst_pad_add_event_probe), (gst_pad_add_buffer_probe),
(gst_pad_remove_data_probe), (gst_pad_remove_event_probe),
(gst_pad_remove_buffer_probe):
Remove previous ghost pad cruft.
Added more detailed debug statements.
* tests/check/gst/gstghostpad.c: (GST_START_TEST):
Fix the testsuite for refcounting changes.
The comments about who has references were correct, but the refcount
being checked wasn't the same (!?!).
2006-07-11 16:20:09 +00:00

971 lines
25 KiB
C

/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2005 Andy Wingo <wingo@pobox.com>
* 2006 Edward Hervey <bilboed@bilboed.com>
*
* 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.
*/
/**
* SECTION:gstghostpad
* @short_description: Pseudo link pads
* @see_also: #GstPad
*
* 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
* are associated with sink and source pads of the child elements.
*
* If the target pad is known at creation time, gst_ghost_pad_new() is the
* function to use to get a ghost-pad. Otherwise one can use gst_ghost_pad_new_no_target()
* to create the ghost-pad and use gst_ghost_pad_set_target() to establish the
* association later on.
*
* Last reviewed on 2005-11-18 (0.9.5)
*/
#include "gst_private.h"
#include "gstghostpad.h"
#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))
#define GST_PROXY_PAD_TARGET(pad) (GST_PROXY_PAD (pad)->target)
#define GST_PROXY_PAD_INTERNAL(pad) (GST_PROXY_PAD (pad)->internal)
typedef struct _GstProxyPad GstProxyPad;
typedef struct _GstProxyPadClass GstProxyPadClass;
#define GST_PROXY_GET_LOCK(pad) (GST_PROXY_PAD (pad)->proxy_lock)
#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)))
struct _GstProxyPad
{
GstPad pad;
/* with PROXY_LOCK */
GMutex *proxy_lock;
GstPad *target;
GstPad *internal;
};
struct _GstProxyPadClass
{
GstPadClass parent_class;
/*< private > */
gpointer _gst_reserved[1];
};
G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);
static GstPad *gst_proxy_pad_get_target (GstPad * pad);
static GstPad *gst_proxy_pad_get_internal (GstPad * pad);
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)
{
GstPad *target = gst_proxy_pad_get_target (pad);
const GstQueryType *res = NULL;
if (target) {
res = gst_pad_get_query_types (target);
gst_object_unref (target);
}
return res;
}
static gboolean
gst_proxy_pad_do_event (GstPad * pad, GstEvent * event)
{
gboolean res = FALSE;
GstPad *internal = gst_proxy_pad_get_internal (pad);
g_return_val_if_fail (internal != NULL, FALSE);
res = gst_pad_push_event (internal, event);
gst_object_unref (internal);
return res;
}
static gboolean
gst_proxy_pad_do_query (GstPad * pad, GstQuery * query)
{
gboolean res = FALSE;
GstPad *target = gst_proxy_pad_get_target (pad);
if (target) {
res = gst_pad_query (target, query);
gst_object_unref (target);
}
return res;
}
static GList *
gst_proxy_pad_do_internal_link (GstPad * pad)
{
GstPad *target = gst_proxy_pad_get_target (pad);
GList *res = NULL;
if (target) {
res = gst_pad_get_internal_links (target);
gst_object_unref (target);
}
return res;
}
static GstFlowReturn
gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size,
GstCaps * caps, GstBuffer ** buf)
{
GstFlowReturn result;
GstPad *internal = gst_proxy_pad_get_internal (pad);
g_return_val_if_fail (internal != NULL, GST_FLOW_NOT_LINKED);
result = gst_pad_alloc_buffer (internal, offset, size, caps, buf);
gst_object_unref (internal);
return result;
}
static GstFlowReturn
gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
{
GstPad *internal = gst_proxy_pad_get_internal (pad);
GstFlowReturn res;
g_return_val_if_fail (internal != NULL, GST_FLOW_NOT_LINKED);
res = gst_pad_push (internal, buffer);
gst_object_unref (internal);
return res;
}
static GstFlowReturn
gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
GstBuffer ** buffer)
{
GstPad *internal = gst_proxy_pad_get_internal (pad);
GstFlowReturn res;
g_return_val_if_fail (internal != NULL, GST_FLOW_NOT_LINKED);
res = gst_pad_pull_range (internal, offset, size, buffer);
gst_object_unref (internal);
return res;
}
static gboolean
gst_proxy_pad_do_checkgetrange (GstPad * pad)
{
gboolean result;
GstPad *internal = gst_proxy_pad_get_internal (pad);
g_return_val_if_fail (internal != NULL, FALSE);
result = gst_pad_check_pull_range (internal);
gst_object_unref (internal);
return result;
}
static GstCaps *
gst_proxy_pad_do_getcaps (GstPad * pad)
{
GstPad *target = gst_proxy_pad_get_target (pad);
GstCaps *res;
if (target) {
res = gst_pad_get_caps (target);
gst_object_unref (target);
} else {
res = gst_caps_new_any ();
}
return res;
}
static gboolean
gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
{
GstPad *target = gst_proxy_pad_get_target (pad);
gboolean res = FALSE;
if (target) {
res = gst_pad_accept_caps (target, caps);
gst_object_unref (target);
}
return res;
}
static void
gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
{
GstPad *target = gst_proxy_pad_get_target (pad);
if (target) {
gst_pad_fixate_caps (target, caps);
gst_object_unref (target);
}
}
static gboolean
gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
{
GstPad *target = gst_proxy_pad_get_target (pad);
gboolean res;
if (target) {
res = gst_pad_set_caps (target, caps);
gst_object_unref (target);
} else {
/*
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.
*/
res = TRUE;
}
return res;
}
static gboolean
gst_proxy_pad_set_target_unlocked (GstPad * pad, GstPad * target)
{
GstPad *oldtarget;
GstPadTemplate **template_p;
if (target) {
GST_LOG_OBJECT (pad, "setting target %s:%s", GST_DEBUG_PAD_NAME (target));
if (G_UNLIKELY (GST_PAD_DIRECTION (pad) != GST_PAD_DIRECTION (target))) {
GST_ERROR_OBJECT (pad,
"target pad doesn't have the same direction as ourself");
return FALSE;
}
} else
GST_LOG_OBJECT (pad, "clearing target");
/* clear old target */
if ((oldtarget = GST_PROXY_PAD_TARGET (pad))) {
/* Clear previous pad template */
template_p = &GST_PAD_PAD_TEMPLATE (pad);
gst_object_replace ((GstObject **) template_p, NULL);
/* Get rid of target */
GST_PROXY_PAD_TARGET (pad) = NULL;
gst_object_unref (oldtarget);
}
if (target) {
/* set and ref new target if any */
GST_PROXY_PAD_TARGET (pad) = gst_object_ref (target);
/* Set new pad template */
template_p = &GST_PAD_PAD_TEMPLATE (pad);
gst_object_replace ((GstObject **) template_p,
(GstObject *) GST_PAD_PAD_TEMPLATE (target));
}
return TRUE;
}
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);
GST_PROXY_UNLOCK (pad);
return result;
}
static GstPad *
gst_proxy_pad_get_target (GstPad * pad)
{
GstPad *target;
GST_PROXY_LOCK (pad);
target = GST_PROXY_PAD_TARGET (pad);
if (target)
gst_object_ref (target);
GST_PROXY_UNLOCK (pad);
return target;
}
static GstPad *
gst_proxy_pad_get_internal (GstPad * pad)
{
GstPad *internal;
GST_PROXY_LOCK (pad);
internal = GST_PROXY_PAD_INTERNAL (pad);
if (internal)
gst_object_ref (internal);
GST_PROXY_UNLOCK (pad);
return internal;
}
static void
gst_proxy_pad_dispose (GObject * object)
{
GstPad *pad = GST_PAD (object);
GstPad **target_p;
GST_PROXY_LOCK (pad);
/* remove and unref the target */
target_p = &GST_PROXY_PAD_TARGET (pad);
gst_object_replace ((GstObject **) target_p, NULL);
/*
The internal is only cleared by GstGhostPad::dispose, since it is the
parent of non-ghost GstProxyPad and owns the refcount on the internal.
*/
GST_PROXY_UNLOCK (pad);
G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
}
static void
gst_proxy_pad_finalize (GObject * object)
{
GstProxyPad *pad = GST_PROXY_PAD (object);
g_mutex_free (pad->proxy_lock);
pad->proxy_lock = NULL;
G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
}
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));
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));
}
#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;
/* with PROXY_LOCK */
gulong notify_id;
/*< private > */
gpointer _gst_reserved[GST_PADDING];
};
struct _GstGhostPadClass
{
GstProxyPadClass parent_class;
/*< private > */
gpointer _gst_reserved[GST_PADDING];
};
G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
static void gst_ghost_pad_dispose (GObject * object);
/* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we
* use for GST_PTR_FORMAT. */
static void
gst_critical (const gchar * format, ...)
{
va_list args;
va_start (args, format);
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
va_end (args);
}
/*
* 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)
{
GstPad *gpad = GST_PAD (object);
GstPad *target = gst_proxy_pad_get_target (gpad);
GstPad *internal = gst_proxy_pad_get_internal (gpad);
if (target) {
if (GST_PAD_IS_SRC (internal))
gst_pad_link (internal, target);
else
gst_pad_link (target, internal);
gst_object_unref (target);
}
gst_object_unref (internal);
if (GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_set)
GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_set (object, parent);
}
static void
gst_ghost_pad_parent_unset (GstObject * object, GstObject * parent)
{
GstPad *gpad = GST_PAD (object);
GstPad *target = gst_proxy_pad_get_target (gpad);
GstPad *internal = gst_proxy_pad_get_internal (gpad);
if (target) {
if (GST_PAD_IS_SRC (internal))
gst_pad_unlink (internal, target);
else
gst_pad_unlink (target, internal);
gst_object_unref (target);
}
gst_object_unref (internal);
if (GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_unset)
GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_unset (object,
parent);
}
static void
gst_ghost_pad_class_init (GstGhostPadClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstObjectClass *gstobject_class = (GstObjectClass *) klass;
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
gstobject_class->parent_set = GST_DEBUG_FUNCPTR (gst_ghost_pad_parent_set);
gstobject_class->parent_unset =
GST_DEBUG_FUNCPTR (gst_ghost_pad_parent_unset);
}
/* see gstghostpad design docs */
static gboolean
gst_ghost_pad_internal_do_activate_push (GstPad * pad, gboolean active)
{
gboolean ret;
if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
GstPad *parent = gst_proxy_pad_get_internal (pad);
if (parent) {
g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE);
ret = gst_pad_activate_push (parent, active);
gst_object_unref (parent);
} else {
ret = FALSE;
}
} else {
GstPad *peer = gst_pad_get_peer (pad);
if (peer) {
ret = gst_pad_activate_push (peer, active);
gst_object_unref (peer);
} else {
ret = FALSE;
}
}
return ret;
}
static gboolean
gst_ghost_pad_internal_do_activate_pull (GstPad * pad, gboolean active)
{
gboolean ret;
if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
GstPad *peer = gst_pad_get_peer (pad);
if (peer) {
ret = gst_pad_activate_pull (peer, active);
gst_object_unref (peer);
} else {
ret = FALSE;
}
} else {
GstPad *parent = gst_proxy_pad_get_internal (pad);
if (parent) {
g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE);
ret = gst_pad_activate_pull (parent, active);
gst_object_unref (parent);
} else {
ret = FALSE;
}
}
return ret;
}
static gboolean
gst_ghost_pad_do_activate_push (GstPad * pad, gboolean active)
{
gboolean ret;
if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
GstPad *internal = gst_proxy_pad_get_internal (pad);
if (internal) {
ret = gst_pad_activate_push (internal, active);
gst_object_unref (internal);
} else {
ret = TRUE;
}
} else {
ret = TRUE;
}
return ret;
}
static gboolean
gst_ghost_pad_do_activate_pull (GstPad * pad, gboolean active)
{
gboolean ret;
if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
GstPad *peer = gst_pad_get_peer (pad);
if (peer) {
ret = gst_pad_activate_pull (peer, active);
gst_object_unref (peer);
} else {
ret = FALSE;
}
} else {
GstPad *internal = gst_proxy_pad_get_internal (pad);
if (internal) {
ret = gst_pad_activate_pull (internal, active);
gst_object_unref (internal);
} else {
ret = FALSE;
}
}
return ret;
}
static GstPadLinkReturn
gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
{
GstPad *internal, *target;
GstPadLinkReturn ret = GST_PAD_LINK_OK;
target = gst_proxy_pad_get_target (pad);
g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED);
internal = gst_proxy_pad_get_internal (pad);
gst_proxy_pad_set_target (internal, peer);
/* if we are a source pad, we should call the peer link function
* if the peer has one */
if (GST_PAD_IS_SRC (pad)) {
if (GST_PAD_LINKFUNC (peer) && ret == GST_PAD_LINK_OK)
ret = GST_PAD_LINKFUNC (peer) (peer, pad);
}
gst_object_unref (target);
gst_object_unref (internal);
return ret;
}
static void
gst_ghost_pad_do_unlink (GstPad * pad)
{
GstPad *target = gst_proxy_pad_get_target (pad);
GstPad *internal = gst_proxy_pad_get_internal (pad);
g_return_if_fail (target != NULL);
GST_DEBUG_OBJECT (pad, "unlinking ghostpad");
/* The target of the internal pad is no longer valid */
gst_proxy_pad_set_target (internal, NULL);
if (target->unlinkfunc)
target->unlinkfunc (target);
gst_object_unref (target);
gst_object_unref (internal);
}
static void
on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
{
GstCaps *caps;
g_object_get (internal, "caps", &caps, NULL);
GST_OBJECT_LOCK (pad);
gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
GST_OBJECT_UNLOCK (pad);
g_object_notify (G_OBJECT (pad), "caps");
if (caps)
gst_caps_unref (caps);
}
static void
gst_ghost_pad_init (GstGhostPad * pad)
{
gst_pad_set_activatepull_function (GST_PAD (pad),
GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_pull));
gst_pad_set_activatepush_function (GST_PAD (pad),
GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_push));
}
static void
gst_ghost_pad_dispose (GObject * object)
{
GstPad *pad = GST_PAD (object);
GstPad *internal;
GstPad *intpeer;
GST_PROXY_LOCK (pad);
internal = GST_PROXY_PAD_INTERNAL (pad);
gst_pad_set_activatepull_function (internal, NULL);
gst_pad_set_activatepush_function (internal, NULL);
g_signal_handler_disconnect (internal, GST_GHOST_PAD (pad)->notify_id);
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);
gst_object_unref (intpeer);
}
GST_PROXY_PAD_INTERNAL (internal) = NULL;
/*
disposes of the internal pad, since the ghostpad is the only possible object
that has a refcount on the internal pad.
*/
gst_object_unparent (GST_OBJECT_CAST (internal));
GST_PROXY_UNLOCK (pad);
G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
}
/**
* 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)
{
GstPad *ret;
GstPad *internal;
GST_LOG ("name:%s, direction:%d", name, dir);
/* OBJECT CREATION */
ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name, "direction", dir, NULL);
/* 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));
}
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));
/* INTERNAL PAD */
internal =
g_object_new (GST_TYPE_PROXY_PAD, "name", NULL,
"direction", (dir == GST_PAD_SRC) ? GST_PAD_SINK : GST_PAD_SRC, NULL);
/* 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);
if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
GST_OBJECT_CAST (ret))) {
gst_critical ("Could not set internal pad%" GST_PTR_FORMAT, internal);
goto beach;
}
/*
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
*/
GST_PROXY_PAD_INTERNAL (ret) = internal;
GST_PROXY_PAD_INTERNAL (GST_PROXY_PAD_INTERNAL (ret)) = GST_PAD (ret);
/* could be more general here, iterating over all writable properties...
* taking the short road for now tho */
GST_GHOST_PAD (ret)->notify_id = g_signal_connect (internal, "notify::caps",
G_CALLBACK (on_int_notify), ret);
on_int_notify (internal, NULL, GST_GHOST_PAD (ret));
gst_pad_set_activatepull_function (GST_PAD (internal),
GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_pull));
gst_pad_set_activatepush_function (GST_PAD (internal),
GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_push));
beach:
GST_PROXY_UNLOCK (ret);
return ret;
}
/**
* gst_ghost_pad_new:
* @name: the name of the new pad, or NULL to assign a default name.
* @target: the pad to ghost.
*
* Create a new ghostpad with @target as the target. The direction and
* padtemplate will be taken from the target pad.
*
* 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;
GST_LOG ("name:%s, target:%s:%s", name, GST_DEBUG_PAD_NAME (target));
g_return_val_if_fail (GST_IS_PAD (target), NULL);
g_return_val_if_fail (!gst_pad_is_linked (target), NULL);
if ((ret = gst_ghost_pad_new_no_target (name, GST_PAD_DIRECTION (target)))) {
gst_ghost_pad_set_target (GST_GHOST_PAD (ret), target);
}
return ret;
}
/**
* gst_ghost_pad_get_target:
* @gpad: the #GstGhostpad
*
* Get the target pad of #gpad. Unref target pad after usage.
*
* Returns: the target #GstPad, can be NULL if the ghostpad
* has no target set. Unref target pad after usage.
*/
GstPad *
gst_ghost_pad_get_target (GstGhostPad * gpad)
{
g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), NULL);
return gst_proxy_pad_get_target (GST_PAD_CAST (gpad));
}
/**
* gst_ghost_pad_set_target:
* @gpad: the #GstGhostpad
* @newtarget: the new pad target
*
* Set the new target of the ghostpad @gpad. Any existing target
* is unlinked and links to the new target are established.
*
* Returns: TRUE if the new target could be set, FALSE otherwise.
*/
gboolean
gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget)
{
GstPad *internal;
GstPad *oldtarget;
GstObject *parent;
gboolean result;
g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE);
GST_PROXY_LOCK (gpad);
internal = GST_PROXY_PAD_INTERNAL (GST_PAD (gpad));
GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget));
/* 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) {
/* and link to internal pad if we are not unparent-ed */
if ((parent = gst_object_get_parent (GST_OBJECT (gpad)))) {
if (GST_PAD_IS_SRC (internal))
result = gst_pad_link (internal, newtarget);
else
result = gst_pad_link (newtarget, internal);
gst_object_unref (parent);
} else {
/* we need to connect the internal pad once we have a parent */
GST_DEBUG_OBJECT (gpad,
"GhostPad doesn't have a parent, will connect internal pad later");
}
}
GST_PROXY_UNLOCK (gpad);
return result;
}