More work on capsnego, mostly proxying
authorWim Taymans <wim.taymans@gmail.com>
Sun, 18 Mar 2001 16:17:39 +0000 (16:17 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Sun, 18 Mar 2001 16:17:39 +0000 (16:17 +0000)
Original commit message from CVS:
More work on capsnego, mostly proxying
Added another testsuite for capsnego
Added caps to vorbisdec, mp3parse, mp1videoparse
Redid the queue proxy handling a bit.

gst/autoplug/gststaticautoplugrender.c
gst/gstpad.c
gst/gstqueue.c
plugins/elements/gstqueue.c
tests/old/testsuite/capsnego/.gitignore
tests/old/testsuite/capsnego/Makefile.am
tests/old/testsuite/capsnego/converter2.c [new file with mode: 0644]
testsuite/capsnego/.gitignore
testsuite/capsnego/Makefile.am
testsuite/capsnego/converter2.c [new file with mode: 0644]

index 180c29f..a9989ff 100644 (file)
@@ -140,8 +140,8 @@ gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink)
   GList *sinkpads;
   gboolean connected = FALSE;
 
-  GST_DEBUG (0,"gstpipeline: autoplug pad connect function for \"%s\" to \"%s\"\n",
-                 GST_ELEMENT_NAME(src), GST_ELEMENT_NAME(sink));
+  GST_DEBUG (0,"gstpipeline: autoplug pad connect function for %s %s:%s to \"%s\"\n",
+                 GST_ELEMENT_NAME (src), GST_DEBUG_PAD_NAME(pad), GST_ELEMENT_NAME(sink));
 
   sinkpads = gst_element_get_pad_list(sink);
   while (sinkpads) {
index 5021e51..14c46a1 100644 (file)
@@ -737,6 +737,12 @@ gst_pad_set_caps (GstPad *pad,
   g_return_val_if_fail (pad != NULL, FALSE);
   g_return_val_if_fail (GST_IS_REAL_PAD (pad), FALSE);         // NOTE this restriction
 
+  if (!gst_caps_check_compatibility (caps, gst_pad_get_padtemplate_caps (pad))) {
+    g_warning ("pad %s:%s tried to set caps incompatible with its padtemplate\n",
+                   GST_DEBUG_PAD_NAME (pad));
+    return FALSE;
+  }
+  
   GST_PAD_CAPS(pad) = caps;
 
   return gst_pad_renegotiate (pad);
@@ -920,117 +926,156 @@ cleanup:
   g_strfreev (split);
 }
 
-/**
- * gst_pad_renegotiate:
- * @pad: the pad to perform the negotiation on
- *
- * Perform the negotiation process with the peer pad.
- *
- * Returns: TRUE if the negotiation process succeded
- */
-gboolean
-gst_pad_renegotiate (GstPad *pad)
+static gboolean
+gst_pad_renegotiate_func (GstPad *pad, GstPad *peerpad, GstCaps **newcaps, gint *counter)
 {
-  gint counter = 0;
-  GstCaps *newcaps = NULL;
-  GstRealPad *peerpad, *currentpad, *otherpad;
-  GstPadDirection currentdirection;
+  GstRealPad *currentpad, *otherpad;
   GstPadNegotiateReturn result;
   
   g_return_val_if_fail (pad != NULL, FALSE);
 
-  peerpad = GST_PAD_PEER (pad);
-
   currentpad = GST_PAD_REALIZE (pad);
-
-  if (!peerpad) {
-    GST_DEBUG (GST_CAT_ELEMENT_PADS, "no peer pad for pad %s:%s\n",
-                 GST_DEBUG_PAD_NAME(currentpad));
-    return TRUE;
-  }
-   
   otherpad = GST_REAL_PAD (peerpad);
 
-  GST_INFO (GST_CAT_ELEMENT_PADS, "negotiating pad %s:%s and %s:%s",
-            GST_DEBUG_PAD_NAME(pad), GST_DEBUG_PAD_NAME(peerpad));
-
-  newcaps = GST_PAD_CAPS (pad);
-  g_assert (newcaps != NULL);
-
-  currentdirection = GST_PAD_DIRECTION (currentpad);
+  GST_DEBUG (GST_CAT_ELEMENT_PADS, "negotiating pad %s:%s and %s:%s counter:%d\n",
+            GST_DEBUG_PAD_NAME(pad), GST_DEBUG_PAD_NAME(peerpad), *counter);
 
   do {
     gboolean matchtempl;
 
-    matchtempl = gst_caps_check_compatibility (newcaps, gst_pad_get_padtemplate_caps (GST_PAD (otherpad)));
+    matchtempl = gst_caps_check_compatibility (*newcaps, gst_pad_get_padtemplate_caps (GST_PAD (otherpad)));
+
+    GST_DEBUG (GST_CAT_ELEMENT_PADS, "caps compatibility check %d\n", matchtempl);
 
     if (matchtempl) {
       if (otherpad->negotiatefunc) {
         GstRealPad *temp;
 
+        GST_DEBUG (GST_CAT_ELEMENT_PADS, "switching pad for next phase\n");
+
         temp = currentpad;
         currentpad = otherpad;
         otherpad = temp;
       }
-      else if (gst_caps_check_compatibility (newcaps, GST_PAD_CAPS (otherpad))) {
-        break;
+      else if (gst_caps_check_compatibility (*newcaps, GST_PAD_CAPS (otherpad))) {
+        GST_DEBUG (GST_CAT_ELEMENT_PADS, "negotiation succeeded\n");
+        return TRUE;
       }
       else {
-       newcaps = GST_PAD_CAPS (otherpad);
+       *newcaps = GST_PAD_CAPS (otherpad);
       }
     }
     else {
-      newcaps = GST_PAD_CAPS (otherpad);
+      *newcaps = GST_PAD_CAPS (otherpad);
     }
 
-    counter++;
+    (*counter)++;
 
     if (currentpad->negotiatefunc) {
-      result = currentpad->negotiatefunc (GST_PAD (currentpad), &newcaps, counter);
+      GST_DEBUG (GST_CAT_ELEMENT_PADS, "calling negotiate function on pad %s:%s\n",
+                     GST_DEBUG_PAD_NAME (currentpad));
+      result = currentpad->negotiatefunc (GST_PAD (currentpad), newcaps, *counter);
 
       switch (result) {
         case GST_PAD_NEGOTIATE_FAIL:
+          GST_DEBUG (GST_CAT_ELEMENT_PADS, "negotiation failed\n");
           return FALSE;
         case GST_PAD_NEGOTIATE_AGREE:
-          goto succeed;
+          GST_DEBUG (GST_CAT_ELEMENT_PADS, "negotiation succeeded\n");
+          return TRUE;
         case GST_PAD_NEGOTIATE_TRY:
+          GST_DEBUG (GST_CAT_ELEMENT_PADS, "try another option\n");
           break;
       }
     }
     else {
+      GST_DEBUG (GST_CAT_ELEMENT_PADS, "negotiation failed, no more options\n");
       return FALSE;
     }
       
-  } while (counter < 100);
+  } while (*counter < 100);
 
-succeed:
+  GST_DEBUG (GST_CAT_ELEMENT_PADS, "negotiation failed, too many attempts\n");
   
-  GST_DEBUG (GST_CAT_ELEMENT_PADS, "pads aggreed on caps :)\n");
+  return FALSE;
+}
 
-  /* here we have some sort of aggreement of the caps */
-  GST_PAD_CAPS (currentpad) = newcaps;
-  GST_PAD_CAPS (otherpad) = newcaps;
+/**
+ * gst_pad_renegotiate:
+ * @pad: the pad to perform the negotiation on
+ *
+ * Perform the negotiation process with the peer pad.
+ *
+ * Returns: TRUE if the negotiation process succeded
+ */
+gboolean
+gst_pad_renegotiate (GstPad *pad)
+{
+  GstCaps *newcaps = NULL;
+  GstRealPad *peerpad, *currentpad, *otherpad;
+  gboolean result;
+  gint counter = 0;
+  
+  g_return_val_if_fail (pad != NULL, FALSE);
+
+  peerpad = GST_PAD_PEER (pad);
+
+  currentpad = GST_PAD_REALIZE (pad);
+
+  if (!peerpad) {
+    GST_DEBUG (GST_CAT_ELEMENT_PADS, "no peer pad for pad %s:%s\n",
+                 GST_DEBUG_PAD_NAME(currentpad));
+    return TRUE;
+  }
    
-  return TRUE;
+  otherpad = GST_REAL_PAD (peerpad);
+
+  GST_INFO (GST_CAT_ELEMENT_PADS, "negotiating pad %s:%s and %s:%s",
+            GST_DEBUG_PAD_NAME(pad), GST_DEBUG_PAD_NAME(peerpad));
+
+  newcaps = GST_PAD_CAPS (pad);
+  g_assert (newcaps != NULL);
+
+  result = gst_pad_renegotiate_func (pad, GST_PAD (peerpad), &newcaps, &counter);
+
+  if (result) {
+    GST_DEBUG (GST_CAT_ELEMENT_PADS, "pads aggreed on caps :)\n");
+
+    /* here we have some sort of aggreement of the caps */
+    GST_PAD_CAPS (currentpad) = newcaps;
+    GST_PAD_CAPS (otherpad) = newcaps;
+  }
+
+  return result;
 }
 
+   
 GstPadNegotiateReturn
 gst_pad_negotiate_proxy (GstPad *pad, GstCaps **caps, gint counter)
 {
   GstRealPad *peer;
+  gboolean result;
 
   g_return_val_if_fail (pad != NULL, GST_PAD_NEGOTIATE_FAIL);
 
-  GST_DEBUG (GST_CAT_ELEMENT_PADS, "pad (%s:%s) proxied\n", GST_DEBUG_PAD_NAME (pad));
+  GST_DEBUG (GST_CAT_ELEMENT_PADS, "negotiation proxied to pad (%s:%s)\n", GST_DEBUG_PAD_NAME (pad));
 
   peer = GST_RPAD_PEER (pad);
 
   //GST_PAD_CAPS (pad) = caps;
 
   if (peer) {
-    if (peer->negotiatefunc) {
-      GST_DEBUG (GST_CAT_ELEMENT_PADS, "calling negotiate on peer (%s:%s)\n", GST_DEBUG_PAD_NAME (peer));
-      return peer->negotiatefunc (peer, caps, counter);
+    result = gst_pad_renegotiate_func (pad, GST_PAD (peer), caps, &counter);
+
+    if (result) {
+      GST_DEBUG (GST_CAT_ELEMENT_PADS, "pads aggreed on caps :)\n");
+
+      /* here we have some sort of aggreement of the caps */
+      GST_PAD_CAPS (pad) = *caps;
+      GST_PAD_CAPS (peer) = *caps;
+    }
+    else {
+      return GST_PAD_NEGOTIATE_FAIL;
     }
   }
 
index b9521b0..43f3211 100644 (file)
@@ -153,23 +153,43 @@ gst_queue_init (GstQueue *queue)
 }
 
 static GstPadNegotiateReturn
-gst_queue_handle_negotiate_src (GstPad *pad, GstCaps **caps, gint count)
+gst_queue_handle_negotiate_src (GstPad *pad, GstCaps **caps, gint counter)
 {
   GstQueue *queue;
 
   queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
 
-  return gst_pad_negotiate_proxy (queue->sinkpad, caps, count);
+  if (counter == 0) {
+     *caps = NULL;
+     return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    if (counter == 1) {
+      return gst_pad_negotiate_proxy (queue->sinkpad, caps, counter);
+    }
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
 }
 
 static GstPadNegotiateReturn
-gst_queue_handle_negotiate_sink (GstPad *pad, GstCaps **caps, gint count)
+gst_queue_handle_negotiate_sink (GstPad *pad, GstCaps **caps, gint counter)
 {
   GstQueue *queue;
 
   queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
 
-  return gst_pad_negotiate_proxy (queue->srcpad, caps, count);
+  if (counter == 0) {
+     *caps = NULL;
+     return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    if (counter == 1) {
+      return gst_pad_negotiate_proxy (queue->srcpad, caps, counter);
+    }
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
 }
 
 static gboolean
index b9521b0..43f3211 100644 (file)
@@ -153,23 +153,43 @@ gst_queue_init (GstQueue *queue)
 }
 
 static GstPadNegotiateReturn
-gst_queue_handle_negotiate_src (GstPad *pad, GstCaps **caps, gint count)
+gst_queue_handle_negotiate_src (GstPad *pad, GstCaps **caps, gint counter)
 {
   GstQueue *queue;
 
   queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
 
-  return gst_pad_negotiate_proxy (queue->sinkpad, caps, count);
+  if (counter == 0) {
+     *caps = NULL;
+     return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    if (counter == 1) {
+      return gst_pad_negotiate_proxy (queue->sinkpad, caps, counter);
+    }
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
 }
 
 static GstPadNegotiateReturn
-gst_queue_handle_negotiate_sink (GstPad *pad, GstCaps **caps, gint count)
+gst_queue_handle_negotiate_sink (GstPad *pad, GstCaps **caps, gint counter)
 {
   GstQueue *queue;
 
   queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
 
-  return gst_pad_negotiate_proxy (queue->srcpad, caps, count);
+  if (counter == 0) {
+     *caps = NULL;
+     return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    if (counter == 1) {
+      return gst_pad_negotiate_proxy (queue->srcpad, caps, counter);
+    }
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
 }
 
 static gboolean
index 624ec50..99c7904 100644 (file)
@@ -1,6 +1,6 @@
 SUBDIRS =
 
-testprogs = capsnego converter
+testprogs = capsnego converter converter2
 
 TESTS = $(testprogs)
 
diff --git a/tests/old/testsuite/capsnego/converter2.c b/tests/old/testsuite/capsnego/converter2.c
new file mode 100644 (file)
index 0000000..1091dc2
--- /dev/null
@@ -0,0 +1,224 @@
+
+#include <gst/gst.h>
+
+GstPad *srcpad, *sinkpad;
+GstPad *srcconvpad, *sinkconvpad;
+GstPad *srcpadtempl, *sinkpadtempl;
+GstPad *srcconvtempl, *sinkconvtempl;
+
+gint converter_in = -1, converter_out = -1;
+gint target_rate = 2000;
+
+static GstPadFactory src_factory = {
+  "src",
+  GST_PAD_FACTORY_SRC,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_src",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstPadFactory src_conv_factory = {
+  "src",
+  GST_PAD_FACTORY_SRC,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_src",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstPadFactory sink_conv_factory = {
+  "src",
+  GST_PAD_FACTORY_SINK,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_src",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstPadFactory sink_factory = {
+  "sink",
+  GST_PAD_FACTORY_SINK,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_sink",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstCapsFactory sink_caps = {
+  "sink_caps",
+  "audio/raw",
+  "rate",     GST_PROPS_INT (6000),
+  NULL
+};
+
+static GstCapsFactory src_caps = {
+  "src_caps",
+  "audio/raw",
+  "rate",     GST_PROPS_INT (3000),
+  NULL
+};
+
+static GstPadTemplate *srctempl, *sinktempl;
+static GstCaps *srccaps, *sinkcaps;
+
+static GstPadNegotiateReturn
+converter_negotiate_src (GstPad *pad, GstCaps **caps, gint counter)
+{
+  g_print (">");
+
+  if (counter == 0) {
+    *caps = NULL;
+    return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    converter_out = gst_caps_get_int (*caps, "rate");
+    return GST_PAD_NEGOTIATE_AGREE;
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
+}
+
+static GstPadNegotiateReturn
+converter_negotiate_sink (GstPad *pad, GstCaps **caps, gint counter)
+{
+  g_print ("<");
+  if (counter == 0) {
+    *caps = GST_PAD_CAPS (srcconvpad);
+    return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    converter_in = gst_caps_get_int (*caps, "rate");
+
+    if (counter == 1) {
+      converter_out = gst_caps_get_int (*caps, "rate");
+      return gst_pad_negotiate_proxy (srcconvpad, caps, counter);
+    }
+    return GST_PAD_NEGOTIATE_AGREE;
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
+}
+
+static GstPadNegotiateReturn
+target_negotiate_sink (GstPad *pad, GstCaps **caps, gint counter)
+{
+  g_print ("{");
+  if (counter == 0) {
+    *caps = gst_caps_new_with_props (
+                   "target_caps",
+                   "audio/raw",
+                   gst_props_new (
+                           "rate", GST_PROPS_INT (target_rate),
+                           NULL)
+                   );
+    return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    target_rate = gst_caps_get_int (*caps, "rate");
+    g_print ("target set %d\n", target_rate);
+    return GST_PAD_NEGOTIATE_AGREE;
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
+}
+
+int 
+main (int argc, char *argv[])
+{
+  gboolean overall = TRUE;
+  gboolean result;
+  
+  gst_init (&argc, &argv);
+
+  srctempl = gst_padtemplate_new (&src_factory);
+  sinktempl = gst_padtemplate_new (&sink_factory);
+  srcpad = gst_pad_new_from_template (srctempl, "src");
+  sinkpad = gst_pad_new_from_template (sinktempl, "sink");
+
+  srcconvtempl = gst_padtemplate_new (&src_conv_factory);
+  sinkconvtempl = gst_padtemplate_new (&sink_conv_factory);
+  srcconvpad = gst_pad_new_from_template (srcconvtempl, "csrc");
+  sinkconvpad = gst_pad_new_from_template (sinkconvtempl, "csink");
+
+  gst_pad_set_negotiate_function (srcconvpad, converter_negotiate_src);
+  gst_pad_set_negotiate_function (sinkconvpad, converter_negotiate_sink);
+  gst_pad_set_negotiate_function (sinkpad, target_negotiate_sink);
+
+  sinkcaps  = gst_caps_register (&sink_caps);
+  srccaps  = gst_caps_register (&src_caps);
+
+  g_print ("-------)      (-----------)       (-----   \n");
+  g_print ("       !      ! converter !       !        \n");
+  g_print ("      src -- csink       csrc -- sink      \n");
+  g_print ("-------)      (-----------)       (-----   \n\n");
+  g_print ("The convertor first tries to proxy the caps received\n");
+  g_print ("on its csink pad to its csrc pad, when that fails, it\n");
+  g_print ("sets up the conversion.\n\n");
+  
+
+  g_print ("sink pad set caps (rate=%d), converter status: %d %d\n", target_rate, 
+                 converter_in, converter_out);
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("result: %d, converter status: %d %d, target: %d\n\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  result = gst_pad_connect (srcpad, sinkconvpad);
+  g_print ("pad connect 1: %d\n", result);
+  overall &= (result == TRUE);
+  result = gst_pad_connect (srcconvpad, sinkpad);
+  g_print ("pad connect 2: %d\n", result);
+  overall &= (result == TRUE);
+
+  g_print ("after connect, converter status: %d %d, target %d\n\n", converter_in, converter_out, target_rate);
+
+  g_print ("src pad set caps (rate=%d), converter status: %d %d, target %d \n", gst_caps_get_int (srccaps, "rate"),
+                 converter_in, converter_out, target_rate);
+  result = gst_pad_set_caps (srcpad, srccaps);
+  g_print ("result %d, converter status: %d %d, target %d\n\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  g_print ("sink pad set caps (rate=2000), converter status: %d %d, target %d \n",
+                 converter_in, converter_out, target_rate);
+  target_rate = 2000;
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("result %d, converter status: %d %d, target: %d\n\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  gst_caps_set (srccaps, "rate", GST_PROPS_INT (4000));
+  result = gst_pad_renegotiate (srcpad);
+  g_print ("sink pad renegotiate caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  gst_caps_set (srccaps, "rate", GST_PROPS_INT (40000));
+  result = gst_pad_set_caps (srcpad, srccaps);
+  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (40000));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  target_rate = 9000;
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  exit (!overall);
+}
index 848066a..e04eb91 100644 (file)
@@ -7,3 +7,4 @@ Makefile.in
 .libs
 capsnego
 converter
+converter2
index 624ec50..99c7904 100644 (file)
@@ -1,6 +1,6 @@
 SUBDIRS =
 
-testprogs = capsnego converter
+testprogs = capsnego converter converter2
 
 TESTS = $(testprogs)
 
diff --git a/testsuite/capsnego/converter2.c b/testsuite/capsnego/converter2.c
new file mode 100644 (file)
index 0000000..1091dc2
--- /dev/null
@@ -0,0 +1,224 @@
+
+#include <gst/gst.h>
+
+GstPad *srcpad, *sinkpad;
+GstPad *srcconvpad, *sinkconvpad;
+GstPad *srcpadtempl, *sinkpadtempl;
+GstPad *srcconvtempl, *sinkconvtempl;
+
+gint converter_in = -1, converter_out = -1;
+gint target_rate = 2000;
+
+static GstPadFactory src_factory = {
+  "src",
+  GST_PAD_FACTORY_SRC,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_src",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstPadFactory src_conv_factory = {
+  "src",
+  GST_PAD_FACTORY_SRC,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_src",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstPadFactory sink_conv_factory = {
+  "src",
+  GST_PAD_FACTORY_SINK,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_src",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstPadFactory sink_factory = {
+  "sink",
+  GST_PAD_FACTORY_SINK,
+  GST_PAD_FACTORY_ALWAYS,
+  GST_PAD_FACTORY_CAPS(
+  "test_sink",
+    "audio/raw",
+    "rate",    GST_PROPS_INT_RANGE (16, 20000)
+  ),
+  NULL,
+};
+
+static GstCapsFactory sink_caps = {
+  "sink_caps",
+  "audio/raw",
+  "rate",     GST_PROPS_INT (6000),
+  NULL
+};
+
+static GstCapsFactory src_caps = {
+  "src_caps",
+  "audio/raw",
+  "rate",     GST_PROPS_INT (3000),
+  NULL
+};
+
+static GstPadTemplate *srctempl, *sinktempl;
+static GstCaps *srccaps, *sinkcaps;
+
+static GstPadNegotiateReturn
+converter_negotiate_src (GstPad *pad, GstCaps **caps, gint counter)
+{
+  g_print (">");
+
+  if (counter == 0) {
+    *caps = NULL;
+    return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    converter_out = gst_caps_get_int (*caps, "rate");
+    return GST_PAD_NEGOTIATE_AGREE;
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
+}
+
+static GstPadNegotiateReturn
+converter_negotiate_sink (GstPad *pad, GstCaps **caps, gint counter)
+{
+  g_print ("<");
+  if (counter == 0) {
+    *caps = GST_PAD_CAPS (srcconvpad);
+    return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    converter_in = gst_caps_get_int (*caps, "rate");
+
+    if (counter == 1) {
+      converter_out = gst_caps_get_int (*caps, "rate");
+      return gst_pad_negotiate_proxy (srcconvpad, caps, counter);
+    }
+    return GST_PAD_NEGOTIATE_AGREE;
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
+}
+
+static GstPadNegotiateReturn
+target_negotiate_sink (GstPad *pad, GstCaps **caps, gint counter)
+{
+  g_print ("{");
+  if (counter == 0) {
+    *caps = gst_caps_new_with_props (
+                   "target_caps",
+                   "audio/raw",
+                   gst_props_new (
+                           "rate", GST_PROPS_INT (target_rate),
+                           NULL)
+                   );
+    return GST_PAD_NEGOTIATE_TRY;
+  }
+  if (*caps) {
+    target_rate = gst_caps_get_int (*caps, "rate");
+    g_print ("target set %d\n", target_rate);
+    return GST_PAD_NEGOTIATE_AGREE;
+  }
+
+  return GST_PAD_NEGOTIATE_FAIL;
+}
+
+int 
+main (int argc, char *argv[])
+{
+  gboolean overall = TRUE;
+  gboolean result;
+  
+  gst_init (&argc, &argv);
+
+  srctempl = gst_padtemplate_new (&src_factory);
+  sinktempl = gst_padtemplate_new (&sink_factory);
+  srcpad = gst_pad_new_from_template (srctempl, "src");
+  sinkpad = gst_pad_new_from_template (sinktempl, "sink");
+
+  srcconvtempl = gst_padtemplate_new (&src_conv_factory);
+  sinkconvtempl = gst_padtemplate_new (&sink_conv_factory);
+  srcconvpad = gst_pad_new_from_template (srcconvtempl, "csrc");
+  sinkconvpad = gst_pad_new_from_template (sinkconvtempl, "csink");
+
+  gst_pad_set_negotiate_function (srcconvpad, converter_negotiate_src);
+  gst_pad_set_negotiate_function (sinkconvpad, converter_negotiate_sink);
+  gst_pad_set_negotiate_function (sinkpad, target_negotiate_sink);
+
+  sinkcaps  = gst_caps_register (&sink_caps);
+  srccaps  = gst_caps_register (&src_caps);
+
+  g_print ("-------)      (-----------)       (-----   \n");
+  g_print ("       !      ! converter !       !        \n");
+  g_print ("      src -- csink       csrc -- sink      \n");
+  g_print ("-------)      (-----------)       (-----   \n\n");
+  g_print ("The convertor first tries to proxy the caps received\n");
+  g_print ("on its csink pad to its csrc pad, when that fails, it\n");
+  g_print ("sets up the conversion.\n\n");
+  
+
+  g_print ("sink pad set caps (rate=%d), converter status: %d %d\n", target_rate, 
+                 converter_in, converter_out);
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("result: %d, converter status: %d %d, target: %d\n\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  result = gst_pad_connect (srcpad, sinkconvpad);
+  g_print ("pad connect 1: %d\n", result);
+  overall &= (result == TRUE);
+  result = gst_pad_connect (srcconvpad, sinkpad);
+  g_print ("pad connect 2: %d\n", result);
+  overall &= (result == TRUE);
+
+  g_print ("after connect, converter status: %d %d, target %d\n\n", converter_in, converter_out, target_rate);
+
+  g_print ("src pad set caps (rate=%d), converter status: %d %d, target %d \n", gst_caps_get_int (srccaps, "rate"),
+                 converter_in, converter_out, target_rate);
+  result = gst_pad_set_caps (srcpad, srccaps);
+  g_print ("result %d, converter status: %d %d, target %d\n\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  g_print ("sink pad set caps (rate=2000), converter status: %d %d, target %d \n",
+                 converter_in, converter_out, target_rate);
+  target_rate = 2000;
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("result %d, converter status: %d %d, target: %d\n\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  gst_caps_set (srccaps, "rate", GST_PROPS_INT (4000));
+  result = gst_pad_renegotiate (srcpad);
+  g_print ("sink pad renegotiate caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  gst_caps_set (srccaps, "rate", GST_PROPS_INT (40000));
+  result = gst_pad_set_caps (srcpad, srccaps);
+  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (40000));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  target_rate = 9000;
+  gst_caps_set (sinkcaps, "rate", GST_PROPS_INT (target_rate));
+  result = gst_pad_set_caps (sinkpad, sinkcaps);
+  g_print ("sink pad set caps %d, converter status: %d %d, target: %d\n", result, 
+                 converter_in, converter_out, target_rate);
+
+  exit (!overall);
+}