gstreamer_core/gst/gstghostpad.c
changeset 8 4a7fac7dd34a
parent 0 0e761a78d257
child 30 7e817e7e631c
--- a/gstreamer_core/gst/gstghostpad.c	Fri Mar 19 09:35:09 2010 +0200
+++ b/gstreamer_core/gst/gstghostpad.c	Fri Apr 16 15:15:52 2010 +0300
@@ -56,43 +56,24 @@
 
 #define GST_CAT_DEFAULT GST_CAT_PADS
 
-#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_CAST(obj)         ((GstProxyPad *)obj)
-
-#define GST_PROXY_PAD_TARGET(pad)       (GST_PROXY_PAD_CAST (pad)->target)
-#define GST_PROXY_PAD_INTERNAL(pad)     (GST_PROXY_PAD_CAST (pad)->internal)
-
-typedef struct _GstProxyPad GstProxyPad;
-typedef struct _GstProxyPadClass GstProxyPadClass;
-
-#define GST_PROXY_GET_LOCK(pad) (GST_PROXY_PAD_CAST (pad)->proxy_lock)
+#define GST_PROXY_PAD_PRIVATE(obj)      (GST_PROXY_PAD_CAST (obj)->priv)
+#define GST_PROXY_PAD_TARGET(pad)       (GST_PROXY_PAD_PRIVATE (pad)->target)
+#define GST_PROXY_PAD_INTERNAL(pad)     (GST_PROXY_PAD_PRIVATE (pad)->internal)
+#define GST_PROXY_PAD_RETARGET(pad)     (GST_PROXY_PAD_PRIVATE (pad)->retarget)
+#define GST_PROXY_GET_LOCK(pad) (GST_PROXY_PAD_PRIVATE (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
+struct _GstProxyPadPrivate
 {
-  GstPad pad;
-
   /* with PROXY_LOCK */
   GMutex *proxy_lock;
   GstPad *target;
   GstPad *internal;
+  gboolean retarget;
 };
 
-struct _GstProxyPadClass
-{
-  GstPadClass parent_class;
-
-  /*< private > */
-  gpointer _gst_reserved[1];
-};
-
-static GType gst_proxy_pad_get_type (void);
-
 G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);
 
 static GstPad *gst_proxy_pad_get_target (GstPad * pad);
@@ -105,12 +86,17 @@
     xmlNodePtr parent);
 #endif
 
+static void on_src_target_notify (GstPad * target,
+    GParamSpec * unused, GstGhostPad * pad);
+
 
 static void
 gst_proxy_pad_class_init (GstProxyPadClass * klass)
 {
   GObjectClass *gobject_class = (GObjectClass *) klass;
 
+  g_type_class_add_private (klass, sizeof (GstProxyPadPrivate));
+
   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_proxy_pad_dispose);
   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_proxy_pad_finalize);
 
@@ -162,14 +148,14 @@
   return res;
 }
 
-static GList *
-gst_proxy_pad_do_internal_link (GstPad * pad)
+static GstIterator *
+gst_proxy_pad_do_iterate_internal_links (GstPad * pad)
 {
-  GList *res = NULL;
+  GstIterator *res = NULL;
   GstPad *target = gst_proxy_pad_get_target (pad);
 
   if (target) {
-    res = gst_pad_get_internal_links (target);
+    res = gst_pad_iterate_internal_links (target);
     gst_object_unref (target);
   }
 
@@ -200,6 +186,17 @@
 }
 
 static GstFlowReturn
+gst_proxy_pad_do_chain_list (GstPad * pad, GstBufferList * list)
+{
+  GstFlowReturn res;
+  GstPad *internal = GST_PROXY_PAD_INTERNAL (pad);
+
+  res = gst_pad_push_list (internal, list);
+
+  return res;
+}
+
+static GstFlowReturn
 gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
     GstBuffer ** buffer)
 {
@@ -234,7 +231,8 @@
     res = gst_pad_get_caps (target);
     gst_object_unref (target);
 
-    GST_DEBUG_OBJECT (pad, "get caps of target: %" GST_PTR_FORMAT, res);
+    GST_DEBUG_OBJECT (pad, "get caps of target %s:%s : %" GST_PTR_FORMAT,
+        GST_DEBUG_PAD_NAME (target), res);
 
     /* filter against the template */
     if (templ && res) {
@@ -331,9 +329,9 @@
     GST_LOG_OBJECT (pad, "clearing target");
 
   /* clear old target */
-  if ((oldtarget = GST_PROXY_PAD_TARGET (pad))) {
+  if ((oldtarget = GST_PROXY_PAD_TARGET (pad)))
     gst_object_unref (oldtarget);
-  }
+
   /* set and ref new target if any */
   if (target)
     GST_PROXY_PAD_TARGET (pad) = gst_object_ref (target);
@@ -378,6 +376,24 @@
 }
 
 static void
+gst_proxy_pad_do_unlink (GstPad * pad)
+{
+  GstPad *internal;
+
+  /* don't do anything if this unlink resulted from retargeting the pad
+   * controlled by the ghostpad. We only want to invalidate the target pad when
+   * the element suddently unlinked with our internal pad. */
+  if (GST_PROXY_PAD_RETARGET (pad))
+    return;
+
+  internal = GST_PROXY_PAD_INTERNAL (pad);
+
+  GST_DEBUG_OBJECT (pad, "pad is unlinked");
+
+  gst_proxy_pad_set_target (internal, NULL);
+}
+
+static void
 gst_proxy_pad_dispose (GObject * object)
 {
   GstPad *pad = GST_PAD (object);
@@ -400,8 +416,8 @@
 {
   GstProxyPad *pad = GST_PROXY_PAD (object);
 
-  g_mutex_free (pad->proxy_lock);
-  pad->proxy_lock = NULL;
+  g_mutex_free (GST_PROXY_GET_LOCK (pad));
+  GST_PROXY_GET_LOCK (pad) = NULL;
 
   G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
 }
@@ -411,14 +427,16 @@
 {
   GstPad *pad = (GstPad *) ppad;
 
-  ppad->proxy_lock = g_mutex_new ();
+  GST_PROXY_PAD_PRIVATE (ppad) = G_TYPE_INSTANCE_GET_PRIVATE (ppad,
+      GST_TYPE_PROXY_PAD, GstProxyPadPrivate);
+  GST_PROXY_GET_LOCK (pad) = 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_iterate_internal_links_function (pad,
+      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_iterate_internal_links));
 
   gst_pad_set_getcaps_function (pad,
       GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getcaps));
@@ -428,6 +446,8 @@
       GST_DEBUG_FUNCPTR (gst_proxy_pad_do_fixatecaps));
   gst_pad_set_setcaps_function (pad,
       GST_DEBUG_FUNCPTR (gst_proxy_pad_do_setcaps));
+  gst_pad_set_unlink_function (pad,
+      GST_DEBUG_FUNCPTR (gst_proxy_pad_do_unlink));
 }
 
 #ifndef GST_DISABLE_LOADSAVE
@@ -490,26 +510,16 @@
  */
 
 
-struct _GstGhostPad
+#define GST_GHOST_PAD_PRIVATE(obj)	(GST_GHOST_PAD_CAST (obj)->priv)
+
+struct _GstGhostPadPrivate
 {
-  GstProxyPad pad;
-
   /* with PROXY_LOCK */
   gulong notify_id;
 
-  /*< private > */
-  gpointer _gst_reserved[GST_PADDING];
+  gboolean constructed;
 };
 
-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);
@@ -519,6 +529,8 @@
 {
   GObjectClass *gobject_class = (GObjectClass *) klass;
 
+  g_type_class_add_private (klass, sizeof (GstGhostPadPrivate));
+
   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
 }
 
@@ -662,44 +674,74 @@
 static void
 gst_ghost_pad_do_unlink (GstPad * pad)
 {
-  GstPad *target;
   GstPad *internal;
 
-  target = gst_proxy_pad_get_target (pad);
   internal = GST_PROXY_PAD_INTERNAL (pad);
 
   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) {
-    if (GST_PAD_UNLINKFUNC (target))
-      GST_PAD_UNLINKFUNC (target) (target);
-
-    gst_object_unref (target);
-  }
 }
 
 static void
 on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
 {
   GstCaps *caps;
+  gboolean changed;
 
   g_object_get (internal, "caps", &caps, NULL);
 
   GST_OBJECT_LOCK (pad);
-  gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
+  changed = (GST_PAD_CAPS (pad) != caps);
+  if (changed)
+    gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
   GST_OBJECT_UNLOCK (pad);
 
-  g_object_notify (G_OBJECT (pad), "caps");
+  if (changed)
+    g_object_notify (G_OBJECT (pad), "caps");
+
   if (caps)
     gst_caps_unref (caps);
 }
 
 static void
+on_src_target_notify (GstPad * target, GParamSpec * unused, GstGhostPad * pad)
+{
+  GstCaps *caps;
+  gboolean changed;
+
+  g_object_get (target, "caps", &caps, NULL);
+
+  GST_OBJECT_LOCK (pad);
+  changed = (GST_PAD_CAPS (pad) != caps);
+  gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
+  GST_OBJECT_UNLOCK (pad);
+
+  if (changed)
+    g_object_notify (G_OBJECT (pad), "caps");
+
+  if (caps)
+    gst_caps_unref (caps);
+}
+
+static gboolean
+gst_ghost_pad_do_setcaps (GstPad * pad, GstCaps * caps)
+{
+  if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC)
+    return TRUE;
+
+  return gst_proxy_pad_do_setcaps (pad, caps);
+}
+
+static void
 gst_ghost_pad_init (GstGhostPad * pad)
 {
+  GST_GHOST_PAD_PRIVATE (pad) = G_TYPE_INSTANCE_GET_PRIVATE (pad,
+      GST_TYPE_GHOST_PAD, GstGhostPadPrivate);
+
+  gst_pad_set_setcaps_function (GST_PAD_CAST (pad),
+      GST_DEBUG_FUNCPTR (gst_ghost_pad_do_setcaps));
   gst_pad_set_activatepull_function (GST_PAD_CAST (pad),
       GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_pull));
   gst_pad_set_activatepush_function (GST_PAD_CAST (pad),
@@ -711,85 +753,109 @@
 {
   GstPad *pad;
   GstPad *internal;
-  GstPad *intpeer;
+  GstPad *peer;
 
   pad = GST_PAD (object);
 
   GST_DEBUG_OBJECT (pad, "dispose");
 
+  gst_ghost_pad_set_target (GST_GHOST_PAD (pad), NULL);
+
+  /* Unlink here so that gst_pad_dispose doesn't. That would lead to a call to
+   * gst_ghost_pad_do_unlink when the ghost pad is in an inconsistent state */
+  peer = gst_pad_get_peer (pad);
+  if (peer) {
+    if (GST_PAD_IS_SRC (pad))
+      gst_pad_unlink (pad, peer);
+    else
+      gst_pad_unlink (peer, pad);
+
+    gst_object_unref (peer);
+  }
+
   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_CAST (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;
+  g_signal_handler_disconnect (internal,
+      GST_GHOST_PAD_PRIVATE (pad)->notify_id);
 
   /* 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_PAD_INTERNAL (pad) = NULL;
 
   GST_PROXY_UNLOCK (pad);
 
   G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
 }
 
-static GstPad *
-gst_ghost_pad_new_full (const gchar * name, GstPadDirection dir,
-    GstPadTemplate * templ)
-{
-  GstPad *ret;
-  GstPad *internal;
-  GstPadDirection otherdir;
+/**
+ * gst_ghost_pad_construct:
+ * @gpad: the newly allocated ghost pad
+ *
+ * Finish initialization of a newly allocated ghost pad.
+ *
+ * This function is most useful in language bindings and when subclassing
+ * #GstGhostPad; plugin and application developers normally will not call this
+ * function. Call this function directly after a call to g_object_new
+ * (GST_TYPE_GHOST_PAD, "direction", @dir, ..., NULL).
+ *
+ * Returns: %TRUE if the construction succeeds, %FALSE otherwise.
+ *
+ * Since: 0.10.22
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
 
-  g_return_val_if_fail (dir != GST_PAD_UNKNOWN, NULL);
+gboolean
+gst_ghost_pad_construct (GstGhostPad * gpad)
+{
+  GstPadDirection dir, otherdir;
+  GstPadTemplate *templ;
+  GstPad *pad, *internal;
 
-  /* OBJECT CREATION */
-  if (templ) {
-    ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
-        "direction", dir, "template", templ, NULL);
-  } else {
-    ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
-        "direction", dir, NULL);
-  }
+  g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE);
+  g_return_val_if_fail (GST_GHOST_PAD_PRIVATE (gpad)->constructed == FALSE,
+      FALSE);
+
+  g_object_get (gpad, "direction", &dir, "template", &templ, NULL);
+
+  g_return_val_if_fail (dir != GST_PAD_UNKNOWN, FALSE);
+
+  pad = GST_PAD (gpad);
 
   /* Set directional padfunctions for ghostpad */
   if (dir == GST_PAD_SINK) {
-    gst_pad_set_bufferalloc_function (ret,
+    gst_pad_set_bufferalloc_function (pad,
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc));
-    gst_pad_set_chain_function (ret,
+    gst_pad_set_chain_function (pad,
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain));
+    gst_pad_set_chain_list_function (pad,
+        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain_list));
   } else {
-    gst_pad_set_getrange_function (ret,
+    gst_pad_set_getrange_function (pad,
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange));
-    gst_pad_set_checkgetrange_function (ret,
+    gst_pad_set_checkgetrange_function (pad,
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange));
   }
 
   /* link/unlink functions */
-  gst_pad_set_link_function (ret, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_link));
-  gst_pad_set_unlink_function (ret,
+  gst_pad_set_link_function (pad, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_link));
+  gst_pad_set_unlink_function (pad,
       GST_DEBUG_FUNCPTR (gst_ghost_pad_do_unlink));
 
-
   /* INTERNAL PAD, it always exists and is child of the ghostpad */
   otherdir = (dir == GST_PAD_SRC) ? GST_PAD_SINK : GST_PAD_SRC;
   if (templ) {
     internal =
         g_object_new (GST_TYPE_PROXY_PAD, "name", NULL,
         "direction", otherdir, "template", templ, NULL);
+    /* release ref obtained via g_object_get */
+    gst_object_unref (templ);
   } else {
     internal =
         g_object_new (GST_TYPE_PROXY_PAD, "name", NULL,
@@ -803,6 +869,8 @@
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc));
     gst_pad_set_chain_function (internal,
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain));
+    gst_pad_set_chain_list_function (internal,
+        GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain_list));
   } else {
     gst_pad_set_getrange_function (internal,
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange));
@@ -810,11 +878,11 @@
         GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange));
   }
 
-  GST_PROXY_LOCK (ret);
+  GST_PROXY_LOCK (pad);
 
   /* now make the ghostpad a parent of the internal pad */
   if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
-          GST_OBJECT_CAST (ret)))
+          GST_OBJECT_CAST (pad)))
     goto parent_failed;
 
   /* The ghostpad is the parent of the internal pad and is the only object that
@@ -825,17 +893,17 @@
    * 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 (internal) = ret;
+  GST_PROXY_PAD_INTERNAL (pad) = internal;
+  GST_PROXY_PAD_INTERNAL (internal) = pad;
 
   /* could be more general here, iterating over all writable properties...
    * taking the short road for now tho */
-  GST_GHOST_PAD_CAST (ret)->notify_id =
+  GST_GHOST_PAD_PRIVATE (pad)->notify_id =
       g_signal_connect (internal, "notify::caps", G_CALLBACK (on_int_notify),
-      ret);
+      pad);
 
   /* call function to init values of the pad caps */
-  on_int_notify (internal, NULL, GST_GHOST_PAD_CAST (ret));
+  on_int_notify (internal, NULL, GST_GHOST_PAD_CAST (pad));
 
   /* special activation functions for the internal pad */
   gst_pad_set_activatepull_function (internal,
@@ -843,24 +911,52 @@
   gst_pad_set_activatepush_function (internal,
       GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_push));
 
-  GST_PROXY_UNLOCK (ret);
+  GST_PROXY_UNLOCK (pad);
 
-  return ret;
+  GST_GHOST_PAD_PRIVATE (gpad)->constructed = TRUE;
+  return TRUE;
 
   /* ERRORS */
 parent_failed:
   {
-    GST_WARNING_OBJECT (ret, "Could not set internal pad %s:%s",
+    GST_WARNING_OBJECT (gpad, "Could not set internal pad %s:%s",
         GST_DEBUG_PAD_NAME (internal));
     g_critical ("Could not set internal pad %s:%s",
         GST_DEBUG_PAD_NAME (internal));
-    GST_PROXY_UNLOCK (ret);
-    gst_object_unref (ret);
+    GST_PROXY_UNLOCK (pad);
     gst_object_unref (internal);
-    return NULL;
+    return FALSE;
   }
 }
 
+static GstPad *
+gst_ghost_pad_new_full (const gchar * name, GstPadDirection dir,
+    GstPadTemplate * templ)
+{
+  GstGhostPad *ret;
+
+  g_return_val_if_fail (dir != GST_PAD_UNKNOWN, NULL);
+
+  /* OBJECT CREATION */
+  if (templ) {
+    ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
+        "direction", dir, "template", templ, NULL);
+  } else {
+    ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
+        "direction", dir, NULL);
+  }
+
+  if (!gst_ghost_pad_construct (ret))
+    goto construct_failed;
+
+  return GST_PAD (ret);
+
+construct_failed:
+  /* already logged */
+  gst_object_unref (ret);
+  return NULL;
+}
+
 /**
  * gst_ghost_pad_new_no_target:
  * @name: the name of the new pad, or NULL to assign a default name.
@@ -1049,7 +1145,8 @@
  * @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.
+ * is unlinked and links to the new target are established. if @newtarget is
+ * NULL the target will be cleared.
  *
  * Returns: TRUE if the new target could be set. This function can return FALSE
  * when the internal pads could not be linked.
@@ -1070,23 +1167,39 @@
 
   GST_PROXY_LOCK (gpad);
   internal = GST_PROXY_PAD_INTERNAL (gpad);
+  g_assert (internal);
 
-  GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget));
+  if (newtarget)
+    GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget));
+  else
+    GST_DEBUG_OBJECT (gpad, "clearing target");
 
   /* 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);
+    if (GST_PAD_IS_SRC (oldtarget)) {
+      g_signal_handlers_disconnect_by_func (oldtarget,
+          (gpointer) on_src_target_notify, gpad);
     }
+
+    GST_PROXY_PAD_RETARGET (internal) = TRUE;
+
+    /* unlink internal pad */
+    if (GST_PAD_IS_SRC (internal))
+      gst_pad_unlink (internal, oldtarget);
+    else
+      gst_pad_unlink (oldtarget, internal);
+
+    GST_PROXY_PAD_RETARGET (internal) = FALSE;
   }
 
   result = gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), newtarget);
 
   if (result && newtarget) {
+    if (GST_PAD_IS_SRC (newtarget)) {
+      g_signal_connect (newtarget, "notify::caps",
+          G_CALLBACK (on_src_target_notify), gpad);
+    }
+
     /* and link to internal pad */
     GST_DEBUG_OBJECT (gpad, "connecting internal pad to target");