docs/design/part-block.txt: Fix typo.
authorWim Taymans <wim.taymans@gmail.com>
Wed, 25 Jun 2008 10:53:52 +0000 (10:53 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Wed, 25 Jun 2008 10:53:52 +0000 (10:53 +0000)
Original commit message from CVS:
* docs/design/part-block.txt:
Fix typo.
* docs/design/part-element-transform.txt:
Add notes about why transform needs to know input/output sizes.
Add some issues that need to be solved.
Add some more use cases.
* tests/check/libs/test_transform.c: (gst_test_trans_base_init),
(gst_test_trans_class_init), (result_sink_chain),
(result_buffer_alloc), (gst_test_trans_new), (gst_test_trans_free),
(gst_test_trans_push), (gst_test_trans_pop):
* tests/check/libs/transform1.c: (buffer_alloc_pt1),
(set_caps_pt1), (GST_START_TEST), (set_caps_pt2), (transform_ip_1),
(set_caps_1), (set_caps_ct1), (transform_ct1),
(transform_caps_ct1), (transform_size_ct1), (buffer_alloc_ct1),
(gst_basetransform_suite):
Add suport for different pad templates and buffer-alloc.
Add more checks for caps and buffer-alloc.
Add checks for proxy buffer alloc.
Add unit test for copy transform.

ChangeLog
docs/design/part-block.txt
docs/design/part-element-transform.txt
tests/check/libs/test_transform.c
tests/check/libs/transform1.c

index 3de3107..5e78243 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2008-06-25  Wim Taymans  <wim.taymans@collabora.co.uk>
+
+       * docs/design/part-block.txt:
+       Fix typo.
+
+       * docs/design/part-element-transform.txt:
+       Add notes about why transform needs to know input/output sizes.
+       Add some issues that need to be solved.
+       Add some more use cases.
+
+       * tests/check/libs/test_transform.c: (gst_test_trans_base_init),
+       (gst_test_trans_class_init), (result_sink_chain),
+       (result_buffer_alloc), (gst_test_trans_new), (gst_test_trans_free),
+       (gst_test_trans_push), (gst_test_trans_pop):
+       * tests/check/libs/transform1.c: (buffer_alloc_pt1),
+       (set_caps_pt1), (GST_START_TEST), (set_caps_pt2), (transform_ip_1),
+       (set_caps_1), (set_caps_ct1), (transform_ct1),
+       (transform_caps_ct1), (transform_size_ct1), (buffer_alloc_ct1),
+       (gst_basetransform_suite):
+       Add suport for different pad templates and buffer-alloc.
+       Add more checks for caps and buffer-alloc.
+       Add checks for proxy buffer alloc.
+       Add unit test for copy transform.
+
 2008-06-24  Tim-Philipp Müller  <tim.muller at collabora co uk>
 
        Patch by: Luc Pionchon  <luc.pionchon@nokia.com>
index 8be9dbf..7459c4d 100644 (file)
@@ -125,7 +125,7 @@ Use cases:
  pipeline. 
 
   1) block element1 src pad. This can be done async.
-  2) wait for block to happen. at this point nothing flowing between
+  2) wait for block to happen. at that point nothing is flowing between
      element1 and element2 and nothing will flow until unblocked.
   3) unlink element1 and element2
   4) optional step: make sure data is flushed out of element2:
index 3e13765..3de4ad7 100644 (file)
@@ -397,3 +397,80 @@ Negotiation
     <----------------------------------|                      |
                   |                    |                      |
 
+ In order to perform passthrough buffer-alloc or pad-alloc, we need to be able
+ to get the size of the output buffer after the transform.
+ For passthrough buffer-alloc, this is trivial: the input size equals the output
+ size.
+
+ For the copy transform or the in-place transform we need additional function to
+ retrieve the size. There are two functions:
+
+  - transform_size()
+    
+    Given a caps and a size on one pad, and a caps on the other pad, calculate
+    the size of the other buffer. This function is able to perform all size
+    transforms and is the prefered method of transforming a size.
+
+  - get_unit_size()
+
+    When the input size and output size are always a multiple of eachother
+    (audio conversion, ..) we can define a more simple get_unit_size() function.
+    The transform will use this function to get the same amount of units in the
+    source and destination buffers.
+
+ For performance reasons, the mapping between caps and size is kept in a cache.
+
+
+Issues
+------
+
+ passthrough and in-place transforms (with writable buffers) never need to
+ perform a pad-alloc on the srcpad. This means that if upstream negotiation
+ happens, the transform element will never know about it.
+
+ The transform element will keep therefore track of the allocation pattern of
+ the peer elements. We can see the following cases:
+
+  - upstream peer calls buffer-alloc on the sinkpad of the transform. In some
+    cases (see above) this call gets proxied or not.
+
+  - upstream peer does never call buffer-alloc.
+
+ We will keeps state about this allocation pattern and perform the following in
+ each case respectively:
+
+  - Upstream calls buffer-alloc: In passthrough and (some) in-place we proxy
+    this call onto the downstream element. If the caps are changed, we mark
+    a flag that we will require a new pad-alloc for the output of the next
+    output buffer.
+
+  - upstream peer does not call buffer-alloc: We always perform a pad-alloc
+    when processing buffers. We can further optimize by only looking at the
+    returned caps instead of doing a full, needless buffer copy.
+
+
+Use cases
+---------
+
+ videotestsrc ! ximagesink
+  
+   - resizing happens because videotestsrc performs pad-alloc.
+
+ videotestsrc peer-alloc=0 ! ximagesink
+
+   - resizing cannot happen because videotestsrc never performs pad-alloc.
+
+ videotestsrc ! videoscale ! ximagesink
+  
+   - videoscale is initially configured in passthrough mode, pad-alloc from
+     videotestsrc is proxied through videoscale.
+   - pad-alloc will renegotiate a new size in videotestsrc.
+
+ videotestsrc peer-alloc=0 ! videoscale ! ximagesink
+  
+   - videoscale is initially configured in passthrough mode.
+   - videoscale performs pad-alloc because no buffer-alloc is called on the
+     sinkpad
+   - resizing the videosink makes videoscale perform the scaling.
+   
index ccb8d01..7493450 100644 (file)
@@ -9,6 +9,8 @@ typedef struct
   GList *buffers;
   GstElement *trans;
   GstBaseTransformClass *klass;
+
+  GstPadBufferAllocFunction buffer_alloc;
 } TestTransData;
 
 static GstStaticPadTemplate gst_test_trans_src_template =
@@ -56,6 +58,21 @@ struct _GstTestTransClass
 GST_BOILERPLATE (GstTestTrans, gst_test_trans, GstBaseTransform,
     GST_TYPE_BASE_TRANSFORM);
 
+static GstFlowReturn (*klass_transform) (GstBaseTransform * trans,
+    GstBuffer * inbuf, GstBuffer * outbuf) = NULL;
+static GstFlowReturn (*klass_transform_ip) (GstBaseTransform * trans,
+    GstBuffer * buf) = NULL;
+static gboolean (*klass_set_caps) (GstBaseTransform * trans, GstCaps * incaps,
+    GstCaps * outcaps) = NULL;
+static GstCaps *(*klass_transform_caps) (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps) = NULL;
+static gboolean (*klass_transform_size) (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
+    guint * othersize) = NULL;
+
+static GstStaticPadTemplate *sink_template = &gst_test_trans_sink_template;
+static GstStaticPadTemplate *src_template = &gst_test_trans_src_template;
+
 static void
 gst_test_trans_base_init (gpointer g_class)
 {
@@ -67,18 +84,11 @@ gst_test_trans_base_init (gpointer g_class)
       "Filter/Test", "Test transform", "Wim Taymans <wim.taymans@gmail.com>");
 
   gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_test_trans_sink_template));
+      gst_static_pad_template_get (sink_template));
   gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_test_trans_src_template));
+      gst_static_pad_template_get (src_template));
 }
 
-static GstFlowReturn (*klass_transform) (GstBaseTransform * trans,
-    GstBuffer * inbuf, GstBuffer * outbuf) = NULL;
-static GstFlowReturn (*klass_transform_ip) (GstBaseTransform * trans,
-    GstBuffer * buf) = NULL;
-static gboolean (*klass_set_caps) (GstBaseTransform * trans, GstCaps * incaps,
-    GstCaps * outcaps) = NULL;
-
 static void
 gst_test_trans_class_init (GstTestTransClass * klass)
 {
@@ -90,6 +100,8 @@ gst_test_trans_class_init (GstTestTransClass * klass)
 
   trans_class->transform_ip = klass_transform_ip;
   trans_class->transform = klass_transform;
+  trans_class->transform_caps = klass_transform_caps;
+  trans_class->transform_size = klass_transform_size;
   trans_class->set_caps = klass_set_caps;
 }
 
@@ -116,23 +128,52 @@ result_sink_chain (GstPad * pad, GstBuffer * buffer)
   return GST_FLOW_OK;
 }
 
+static GstFlowReturn
+result_buffer_alloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps,
+    GstBuffer ** buf)
+{
+  GstFlowReturn res;
+  TestTransData *data;
+
+  data = gst_pad_get_element_private (pad);
+
+  if (data->buffer_alloc) {
+    res = data->buffer_alloc (pad, offset, size, caps, buf);
+  } else {
+    *buf = gst_buffer_new_and_alloc (size);
+    gst_buffer_set_caps (*buf, caps);
+    res = GST_FLOW_OK;
+  }
+
+  return res;
+}
+
 static TestTransData *
 gst_test_trans_new (void)
 {
   TestTransData *res;
   GstPad *tmp;
+  GstPadTemplate *templ;
 
   res = g_new0 (TestTransData, 1);
   res->trans = g_object_new (GST_TYPE_TEST_TRANS, NULL);
-  res->srcpad =
-      gst_pad_new_from_static_template (&gst_test_trans_src_template, "src");
-  res->sinkpad =
-      gst_pad_new_from_static_template (&gst_test_trans_sink_template, "sink");
+
+  templ = gst_static_pad_template_get (sink_template);
+  templ->direction = GST_PAD_SRC;
+  res->srcpad = gst_pad_new_from_template (templ, "src");
+  gst_object_unref (templ);
+
+  templ = gst_static_pad_template_get (src_template);
+  templ->direction = GST_PAD_SINK;
+  res->sinkpad = gst_pad_new_from_template (templ, "sink");
+  gst_object_unref (templ);
+
   res->klass = GST_BASE_TRANSFORM_GET_CLASS (res->trans);
 
   gst_test_trans_set_data (GST_TEST_TRANS (res->trans), res);
   gst_pad_set_element_private (res->sinkpad, res);
 
+  gst_pad_set_bufferalloc_function (res->sinkpad, result_buffer_alloc);
   gst_pad_set_chain_function (res->sinkpad, result_sink_chain);
 
   tmp = gst_element_get_static_pad (res->trans, "sink");
index 592aa8c..004dad1 100644 (file)
 
 #include "test_transform.c"
 
+static gboolean buffer_alloc_pt1_called;
+
+static GstFlowReturn
+buffer_alloc_pt1 (GstPad * pad, guint64 offset, guint size, GstCaps * caps,
+    GstBuffer ** buf)
+{
+  GST_DEBUG_OBJECT (pad, "buffer_alloc called %" G_GUINT64_FORMAT ", %u, %"
+      GST_PTR_FORMAT, offset, size, caps);
+
+  buffer_alloc_pt1_called = TRUE;
+
+  *buf = gst_buffer_new_and_alloc (size);
+  gst_buffer_set_caps (*buf, caps);
+
+  return GST_FLOW_OK;
+}
+
+static gboolean set_caps_pt1_called;
+
+static gboolean
+set_caps_pt1 (GstBaseTransform * trans, GstCaps * incaps, GstCaps * outcaps)
+{
+  GST_DEBUG_OBJECT (trans, "set_caps called");
+
+  set_caps_pt1_called = TRUE;
+
+  return TRUE;
+}
+
 /* basic passthrough, we don't have any transform functions so we can only
  * perform passthrough. We also don't have caps, which is fine */
 GST_START_TEST (basetransform_chain_pt1)
@@ -38,13 +67,25 @@ GST_START_TEST (basetransform_chain_pt1)
   TestTransData *trans;
   GstBuffer *buffer;
   GstFlowReturn res;
+  GstCaps *caps;
 
+  klass_set_caps = set_caps_pt1;
   trans = gst_test_trans_new ();
+  trans->buffer_alloc = buffer_alloc_pt1;
+
+  GST_DEBUG_OBJECT (trans, "buffer without caps, size 20");
 
   buffer = gst_buffer_new_and_alloc (20);
 
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt1_called = FALSE;;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
+#ifdef FAILING_TESTS
+  /* FIXME, passthough without pad-alloc, do pad-alloc on the srcpad */
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+#endif
+  fail_unless (set_caps_pt1_called == FALSE);
 
   buffer = gst_test_trans_pop (trans);
   fail_unless (buffer != NULL);
@@ -54,9 +95,18 @@ GST_START_TEST (basetransform_chain_pt1)
 
   gst_buffer_unref (buffer);
 
+  GST_DEBUG_OBJECT (trans, "buffer without caps, size 10");
+
   buffer = gst_buffer_new_and_alloc (10);
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt1_called = FALSE;;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
+#ifdef FAILING_TESTS
+  /* FIXME, passthough without pad-alloc, do pad-alloc on the srcpad */
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+#endif
+  fail_unless (set_caps_pt1_called == FALSE);
 
   buffer = gst_test_trans_pop (trans);
   fail_unless (buffer != NULL);
@@ -66,11 +116,59 @@ GST_START_TEST (basetransform_chain_pt1)
 
   gst_buffer_unref (buffer);
 
+  /* proxy buffer-alloc without caps */
+  GST_DEBUG_OBJECT (trans, "alloc without caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt1_called = FALSE;;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, NULL, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (set_caps_pt1_called == FALSE);
+  gst_buffer_unref (buffer);
+
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with caps, size 10");
+
+  caps = gst_caps_new_simple ("foo/x-bar", NULL);
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt1_called = FALSE;;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 10, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (set_caps_pt1_called == FALSE);
+  gst_buffer_unref (buffer);
+
+  /* once more */
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt1_called = FALSE;;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 10, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (set_caps_pt1_called == FALSE);
+  gst_buffer_unref (buffer);
+
+  gst_caps_unref (caps);
+
   gst_test_trans_free (trans);
 }
 
 GST_END_TEST;
 
+static gboolean set_caps_pt2_called;
+
+static gboolean
+set_caps_pt2 (GstBaseTransform * trans, GstCaps * incaps, GstCaps * outcaps)
+{
+  GST_DEBUG_OBJECT (trans, "set_caps called");
+
+  set_caps_pt2_called = TRUE;
+
+  fail_unless (gst_caps_is_equal (incaps, outcaps));
+
+  return TRUE;
+}
+
 /* basic passthrough, we don't have any transform functions so we can only
  * perform passthrough with same caps */
 GST_START_TEST (basetransform_chain_pt2)
@@ -80,16 +178,27 @@ GST_START_TEST (basetransform_chain_pt2)
   GstCaps *caps;
   GstFlowReturn res;
 
+  klass_set_caps = set_caps_pt2;
   trans = gst_test_trans_new ();
+  trans->buffer_alloc = buffer_alloc_pt1;
 
   /* first buffer */
   caps = gst_caps_new_simple ("foo/x-bar", NULL);
 
+  GST_DEBUG_OBJECT (trans, "buffer with caps, size 20");
+
   buffer = gst_buffer_new_and_alloc (20);
   gst_buffer_set_caps (buffer, caps);
 
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt2_called = FALSE;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
+#ifdef FAILING_TESTS
+  /* FIXME, passthough without pad-alloc, do pad-alloc on the srcpad */
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+#endif
+  fail_unless (set_caps_pt2_called == TRUE);
 
   buffer = gst_test_trans_pop (trans);
   fail_unless (buffer != NULL);
@@ -97,16 +206,37 @@ GST_START_TEST (basetransform_chain_pt2)
   fail_unless (GST_BUFFER_CAPS (buffer) == caps);
 
   gst_buffer_unref (buffer);
+
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt2_called = FALSE;;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (set_caps_pt2_called == FALSE);
+  gst_buffer_unref (buffer);
+
   gst_caps_unref (caps);
 
   /* second buffer, renegotiates, keeps extra type arg in caps */
   caps = gst_caps_new_simple ("foo/x-bar", "type", G_TYPE_INT, 1, NULL);
 
+  GST_DEBUG_OBJECT (trans, "buffer with caps, size 10");
+
   buffer = gst_buffer_new_and_alloc (10);
   gst_buffer_set_caps (buffer, caps);
 
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt2_called = FALSE;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
+#ifdef FAILING_TESTS
+  /* FIXME, passthough without pad-alloc, do pad-alloc on the srcpad */
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+#endif
+  fail_unless (set_caps_pt2_called == TRUE);
 
   buffer = gst_test_trans_pop (trans);
   fail_unless (buffer != NULL);
@@ -114,6 +244,33 @@ GST_START_TEST (basetransform_chain_pt2)
   fail_unless (GST_BUFFER_CAPS (buffer) == caps);
 
   gst_buffer_unref (buffer);
+
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt2_called = FALSE;;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (set_caps_pt2_called == FALSE);
+  gst_buffer_unref (buffer);
+
+  gst_caps_unref (caps);
+
+  /* with caps that is a superset */
+  caps = gst_caps_new_simple ("foo/x-bar", NULL);
+
+  GST_DEBUG_OBJECT (trans, "alloc with superset caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  set_caps_pt2_called = FALSE;;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (set_caps_pt2_called == FALSE);
+  gst_buffer_unref (buffer);
+
   gst_caps_unref (caps);
 
   gst_test_trans_free (trans);
@@ -147,21 +304,31 @@ GST_START_TEST (basetransform_chain_ip1)
 
   klass_transform_ip = transform_ip_1;
   trans = gst_test_trans_new ();
+  trans->buffer_alloc = buffer_alloc_pt1;
+
+  GST_DEBUG_OBJECT (trans, "buffer without caps, size 20");
 
   buffer = gst_buffer_new_and_alloc (20);
 
   transform_ip_1_called = FALSE;;
   transform_ip_1_writable = TRUE;;
+  buffer_alloc_pt1_called = FALSE;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
   fail_unless (transform_ip_1_called == TRUE);
   fail_unless (transform_ip_1_writable == TRUE);
+#ifdef FAILING_TESTS
+  /* FIXME, in-place without pad-alloc, do pad-alloc on the srcpad */
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+#endif
 
   buffer = gst_test_trans_pop (trans);
   fail_unless (buffer != NULL);
   fail_unless (GST_BUFFER_SIZE (buffer) == 20);
   gst_buffer_unref (buffer);
 
+  GST_DEBUG_OBJECT (trans, "buffer without caps extra ref, size 20");
+
   buffer = gst_buffer_new_and_alloc (20);
   /* take additional ref to make it non-writable */
   gst_buffer_ref (buffer);
@@ -170,11 +337,13 @@ GST_START_TEST (basetransform_chain_ip1)
 
   transform_ip_1_called = FALSE;;
   transform_ip_1_writable = FALSE;;
+  buffer_alloc_pt1_called = FALSE;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
   fail_unless (transform_ip_1_called == TRUE);
-  /* copy should have been taken */
+  /* copy should have been taken with pad-alloc */
   fail_unless (transform_ip_1_writable == TRUE);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
   /* after push, get rid of the final ref we had */
   gst_buffer_unref (buffer);
 
@@ -186,6 +355,15 @@ GST_START_TEST (basetransform_chain_ip1)
   fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (buffer) == 1);
   gst_buffer_unref (buffer);
 
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc without caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, NULL, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  gst_buffer_unref (buffer);
+
   gst_test_trans_free (trans);
 }
 
@@ -225,33 +403,67 @@ GST_START_TEST (basetransform_chain_ip2)
   klass_set_caps = set_caps_1;
 
   trans = gst_test_trans_new ();
+  trans->buffer_alloc = buffer_alloc_pt1;
+
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc without caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, NULL, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (GST_BUFFER_SIZE (buffer) == 20);
+  fail_unless (GST_BUFFER_CAPS (buffer) == NULL);
+  gst_buffer_unref (buffer);
 
   caps = gst_caps_new_simple ("foo/x-bar", NULL);
 
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  fail_unless (GST_BUFFER_SIZE (buffer) == 20);
+  fail_unless (GST_BUFFER_CAPS (buffer) == caps);
+  gst_buffer_unref (buffer);
+
   /* first try to push a buffer without caps, this should fail */
   buffer = gst_buffer_new_and_alloc (20);
 
+  GST_DEBUG_OBJECT (trans, "buffer without caps, size 20");
+
   transform_ip_1_called = FALSE;;
   transform_ip_1_writable = FALSE;;
+  buffer_alloc_pt1_called = FALSE;
   set_caps_1_called = FALSE;;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_NOT_NEGOTIATED);
   fail_unless (transform_ip_1_called == FALSE);
   fail_unless (transform_ip_1_writable == FALSE);
   fail_unless (set_caps_1_called == FALSE);
+  fail_unless (buffer_alloc_pt1_called == FALSE);
 
   /* try to push a buffer with caps */
+  GST_DEBUG_OBJECT (trans, "buffer with caps, size 20");
+
   buffer = gst_buffer_new_and_alloc (20);
   gst_buffer_set_caps (buffer, caps);
 
   transform_ip_1_called = FALSE;
   transform_ip_1_writable = FALSE;
   set_caps_1_called = FALSE;;
+  buffer_alloc_pt1_called = FALSE;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
   fail_unless (transform_ip_1_called == TRUE);
   fail_unless (transform_ip_1_writable == TRUE);
   fail_unless (set_caps_1_called == TRUE);
+#ifdef FAILING_TESTS
+  /* FIXME, in-place without pad-alloc, do pad-alloc on the srcpad */
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+#endif
 
   buffer = gst_test_trans_pop (trans);
   fail_unless (buffer != NULL);
@@ -259,6 +471,17 @@ GST_START_TEST (basetransform_chain_ip2)
   fail_unless (GST_BUFFER_CAPS (buffer) == caps);
   gst_buffer_unref (buffer);
 
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  gst_buffer_unref (buffer);
+
+  GST_DEBUG_OBJECT (trans, "buffer with caps extra ref, size 20");
+
   buffer = gst_buffer_new_and_alloc (20);
   gst_buffer_set_caps (buffer, caps);
   /* take additional ref to make it non-writable */
@@ -268,10 +491,12 @@ GST_START_TEST (basetransform_chain_ip2)
 
   transform_ip_1_called = FALSE;;
   transform_ip_1_writable = FALSE;;
+  buffer_alloc_pt1_called = FALSE;
   res = gst_test_trans_push (trans, buffer);
   fail_unless (res == GST_FLOW_OK);
   fail_unless (transform_ip_1_called == TRUE);
   fail_unless (transform_ip_1_writable == TRUE);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
   /* after push, get rid of the final ref we had */
   gst_buffer_unref (buffer);
 
@@ -284,14 +509,243 @@ GST_START_TEST (basetransform_chain_ip2)
   fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (buffer) == 1);
   gst_buffer_unref (buffer);
 
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with caps, size 20");
+
+  buffer_alloc_pt1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, caps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (buffer_alloc_pt1_called == TRUE);
+  gst_buffer_unref (buffer);
+
   gst_caps_unref (caps);
 
-  trans->klass->transform_ip = NULL;
   gst_test_trans_free (trans);
 }
 
 GST_END_TEST;
 
+static GstStaticPadTemplate sink_template_ct1 = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("baz/x-foo")
+    );
+
+static gboolean set_caps_ct1_called;
+
+static gboolean
+set_caps_ct1 (GstBaseTransform * trans, GstCaps * incaps, GstCaps * outcaps)
+{
+  GstCaps *caps1, *caps2;
+
+  GST_DEBUG_OBJECT (trans, "set_caps called");
+
+  caps1 = gst_caps_new_simple ("baz/x-foo", NULL);
+  caps2 = gst_caps_new_simple ("foo/x-bar", NULL);
+
+  fail_unless (gst_caps_is_equal (incaps, caps1));
+  fail_unless (gst_caps_is_equal (outcaps, caps2));
+
+  set_caps_ct1_called = TRUE;;
+
+  gst_caps_unref (caps1);
+  gst_caps_unref (caps2);
+
+  return TRUE;
+}
+
+static gboolean transform_ct_1_called;
+static gboolean transform_ct_1_writable;
+
+static GstFlowReturn
+transform_ct1 (GstBaseTransform * trans, GstBuffer * in, GstBuffer * out)
+{
+  transform_ct_1_called = TRUE;
+  transform_ct_1_writable = gst_buffer_is_writable (out);
+
+  GST_DEBUG_OBJECT (trans, "writable: %d", transform_ct_1_writable);
+
+  return GST_FLOW_OK;
+}
+
+static GstCaps *
+transform_caps_ct1 (GstBaseTransform * trans, GstPadDirection dir,
+    GstCaps * caps)
+{
+  GstCaps *res;
+
+  if (dir == GST_PAD_SINK) {
+    res = gst_caps_new_simple ("foo/x-bar", NULL);
+  } else {
+    res = gst_caps_new_simple ("baz/x-foo", NULL);
+  }
+  return res;
+}
+
+static gboolean
+transform_size_ct1 (GstBaseTransform * trans, GstPadDirection direction,
+    GstCaps * caps, guint size, GstCaps * othercaps, guint * othersize)
+{
+  if (direction == GST_PAD_SINK) {
+    *othersize = size * 2;
+  } else {
+    *othersize = size / 2;
+  }
+
+  return TRUE;
+}
+
+gboolean buffer_alloc_ct1_called;
+
+static GstFlowReturn
+buffer_alloc_ct1 (GstPad * pad, guint64 offset, guint size, GstCaps * caps,
+    GstBuffer ** buf)
+{
+  GstCaps *outcaps;
+
+  GST_DEBUG_OBJECT (pad, "buffer_alloc called %" G_GUINT64_FORMAT ", %u, %"
+      GST_PTR_FORMAT, offset, size, caps);
+
+  buffer_alloc_ct1_called = TRUE;
+
+  outcaps = gst_caps_new_simple ("foo/x-bar", NULL);
+  fail_unless (gst_caps_is_equal (outcaps, caps));
+  gst_caps_unref (outcaps);
+
+  *buf = gst_buffer_new_and_alloc (size);
+  gst_buffer_set_caps (*buf, caps);
+
+  return GST_FLOW_OK;
+}
+
+/* basic copy-transform, check if the transform function is called,
+ * buffer should be writable. we also set a setcaps function and
+ * see if it's called. */
+GST_START_TEST (basetransform_chain_ct1)
+{
+  TestTransData *trans;
+  GstBuffer *buffer;
+  GstFlowReturn res;
+  GstCaps *incaps, *outcaps;
+
+  sink_template = &sink_template_ct1;
+  klass_transform = transform_ct1;
+  klass_set_caps = set_caps_ct1;
+  klass_transform_caps = transform_caps_ct1;
+  klass_transform_size = transform_size_ct1;
+
+  trans = gst_test_trans_new ();
+  trans->buffer_alloc = buffer_alloc_ct1;
+
+  incaps = gst_caps_new_simple ("baz/x-foo", NULL);
+  outcaps = gst_caps_new_simple ("foo/x-bar", NULL);
+
+#if 0
+  /* without caps buffer, I think this should fail */
+  GST_DEBUG_OBJECT (trans, "alloc without caps, size 20");
+
+  buffer_alloc_ct1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, NULL, &buffer);
+  fail_unless (res == GST_FLOW_NOT_NEGOTIATED);
+  /* should not call pad-alloc because the caps and sizes are different */
+  fail_unless (buffer_alloc_ct1_called == FALSE);
+#endif
+
+  /* with wrong caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with wrong caps, size 20");
+
+  buffer_alloc_ct1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, outcaps, &buffer);
+  fail_unless (res == GST_FLOW_NOT_NEGOTIATED);
+  fail_unless (buffer_alloc_ct1_called == TRUE);
+
+  /* with caps buffer */
+  GST_DEBUG_OBJECT (trans, "alloc with caps, size 20");
+
+  buffer_alloc_ct1_called = FALSE;
+  res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, incaps, &buffer);
+  fail_unless (res == GST_FLOW_OK);
+  /* should not call pad-alloc because the caps and sizes are different */
+  fail_unless (buffer_alloc_ct1_called == FALSE);
+  gst_buffer_unref (buffer);
+
+  /* first try to push a buffer without caps, this should fail */
+  buffer = gst_buffer_new_and_alloc (20);
+
+  GST_DEBUG_OBJECT (trans, "buffer without caps");
+
+  transform_ct_1_called = FALSE;;
+  transform_ct_1_writable = FALSE;;
+  set_caps_ct1_called = FALSE;;
+  buffer_alloc_ct1_called = FALSE;
+  res = gst_test_trans_push (trans, buffer);
+  fail_unless (res == GST_FLOW_NOT_NEGOTIATED);
+  fail_unless (transform_ct_1_called == FALSE);
+  fail_unless (transform_ct_1_writable == FALSE);
+  fail_unless (set_caps_ct1_called == FALSE);
+  fail_unless (buffer_alloc_ct1_called == FALSE);
+
+  /* try to push a buffer with caps */
+  buffer = gst_buffer_new_and_alloc (20);
+  gst_buffer_set_caps (buffer, incaps);
+
+  GST_DEBUG_OBJECT (trans, "buffer with caps %" GST_PTR_FORMAT, incaps);
+
+  transform_ct_1_called = FALSE;
+  transform_ct_1_writable = FALSE;
+  set_caps_ct1_called = FALSE;;
+  buffer_alloc_ct1_called = FALSE;
+  res = gst_test_trans_push (trans, buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (transform_ct_1_called == TRUE);
+  fail_unless (transform_ct_1_writable == TRUE);
+  fail_unless (set_caps_ct1_called == TRUE);
+  fail_unless (buffer_alloc_ct1_called == TRUE);
+
+  buffer = gst_test_trans_pop (trans);
+  fail_unless (buffer != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer) == 40);
+  fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (buffer), outcaps));
+  gst_buffer_unref (buffer);
+
+  buffer = gst_buffer_new_and_alloc (20);
+  gst_buffer_set_caps (buffer, incaps);
+  /* take additional ref to make it non-writable */
+  gst_buffer_ref (buffer);
+
+  fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (buffer) == 2);
+
+  GST_DEBUG_OBJECT (trans, "buffer with caps %" GST_PTR_FORMAT, incaps);
+
+  transform_ct_1_called = FALSE;;
+  transform_ct_1_writable = FALSE;;
+  buffer_alloc_ct1_called = FALSE;
+  res = gst_test_trans_push (trans, buffer);
+  fail_unless (res == GST_FLOW_OK);
+  fail_unless (transform_ct_1_called == TRUE);
+  fail_unless (transform_ct_1_writable == TRUE);
+  fail_unless (buffer_alloc_ct1_called == TRUE);
+  /* after push, get rid of the final ref we had */
+  gst_buffer_unref (buffer);
+
+  buffer = gst_test_trans_pop (trans);
+  fail_unless (buffer != NULL);
+  fail_unless (GST_BUFFER_SIZE (buffer) == 40);
+  fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (buffer), outcaps));
+
+  /* output buffer has refcount 1 */
+  fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (buffer) == 1);
+  gst_buffer_unref (buffer);
+
+  gst_caps_unref (incaps);
+  gst_caps_unref (outcaps);
+
+  gst_test_trans_free (trans);
+}
+
+GST_END_TEST;
+
+
 static Suite *
 gst_basetransform_suite (void)
 {
@@ -303,6 +757,7 @@ gst_basetransform_suite (void)
   tcase_add_test (tc, basetransform_chain_pt2);
   tcase_add_test (tc, basetransform_chain_ip1);
   tcase_add_test (tc, basetransform_chain_ip2);
+  tcase_add_test (tc, basetransform_chain_ct1);
 
   return s;
 }