aggregator: Assert if the sink/src pad type that is to be used is not a GstAggregator...
[platform/upstream/gstreamer.git] / libs / gst / base / gstbasetransform.c
index b3d6a8c..5ddad78 100644 (file)
  *
  * 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.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 /**
  * SECTION:gstbasetransform
+ * @title: GstBaseTransform
  * @short_description: Base class for simple transform filters
  * @see_also: #GstBaseSrc, #GstBaseSink
  *
- * This base class is for filter elements that process data.
+ * This base class is for filter elements that process data. Elements
+ * that are suitable for implementation using #GstBaseTransform are ones
+ * where the size and caps of the output is known entirely from the input
+ * caps and buffer sizes. These include elements that directly transform
+ * one buffer into another, modify the contents of a buffer in-place, as
+ * well as elements that collate multiple input buffers into one output buffer,
+ * or that expand one input buffer into multiple output buffers. See below
+ * for more concrete use cases.
  *
  * It provides for:
- * <itemizedlist>
- *   <listitem><para>one sinkpad and one srcpad</para></listitem>
- *   <listitem><para>
- *      Possible formats on sink and source pad implemented
- *      with custom transform_caps function. By default uses
- *      same format on sink and source.
- *   </para></listitem>
- *   <listitem><para>Handles state changes</para></listitem>
- *   <listitem><para>Does flushing</para></listitem>
- *   <listitem><para>Push mode</para></listitem>
- *   <listitem><para>
- *       Pull mode if the sub-class transform can operate on arbitrary data
- *    </para></listitem>
- * </itemizedlist>
- *
- * <refsect2>
- * <title>Use Cases</title>
- * <para>
- * <orderedlist>
- * <listitem>
- *   <itemizedlist><title>Passthrough mode</title>
- *   <listitem><para>
- *     Element has no interest in modifying the buffer. It may want to inspect it,
+ *
+ * * one sinkpad and one srcpad
+ * * Possible formats on sink and source pad implemented
+ *   with custom transform_caps function. By default uses
+ *   same format on sink and source.
+ *
+ * * Handles state changes
+ * * Does flushing
+ * * Push mode
+ * * Pull mode if the sub-class transform can operate on arbitrary data
+ *
+ * # Use Cases
+ *
+ * ## Passthrough mode
+ *
+ *   * Element has no interest in modifying the buffer. It may want to inspect it,
  *     in which case the element should have a transform_ip function. If there
  *     is no transform_ip function in passthrough mode, the buffer is pushed
  *     intact.
- *   </para></listitem>
- *   <listitem><para>
- *     On the GstBaseTransformClass is the passthrough_on_same_caps variable
- *     which will automatically set/unset passthrough based on whether the
+ *
+ *   * The #GstBaseTransformClass.passthrough_on_same_caps variable
+ *     will automatically set/unset passthrough based on whether the
  *     element negotiates the same caps on both pads.
- *   </para></listitem>
- *   <listitem><para>
- *     passthrough_on_same_caps on an element that doesn't implement a
- *     transform_caps function is useful for elements that only inspect data
- *     (such as level)
- *   </para></listitem>
- *   </itemizedlist>
- *   <itemizedlist>
- *   <title>Example elements</title>
- *     <listitem>Level</listitem>
- *     <listitem>Videoscale, audioconvert, ffmpegcolorspace, audioresample in
- *     certain modes.</listitem>
- *   </itemizedlist>
- * </listitem>
- * <listitem>
- *   <itemizedlist>
- *     <title>Modifications in-place - input buffer and output buffer are the
- *     same thing.</title>
- *   <listitem><para>
- *     The element must implement a transform_ip function.
- *   </para></listitem>
- *   <listitem><para>
- *     Output buffer size must <= input buffer size
- *   </para></listitem>
- *   <listitem><para>
- *     If the always_in_place flag is set, non-writable buffers will be copied
- *     and passed to the transform_ip function, otherwise a new buffer will be
- *     created and the transform function called.
- *   </para></listitem>
- *   <listitem><para>
- *     Incoming writable buffers will be passed to the transform_ip function
- *     immediately.  </para></listitem>
- *   <listitem><para>
- *     only implementing transform_ip and not transform implies always_in_place
- *     = TRUE
- *   </para></listitem>
- *   </itemizedlist>
- *   <itemizedlist>
- *   <title>Example elements</title>
- *     <listitem>Volume</listitem>
- *     <listitem>Audioconvert in certain modes (signed/unsigned
- *     conversion)</listitem>
- *     <listitem>ffmpegcolorspace in certain modes (endianness
- *     swapping)</listitem>
- *   </itemizedlist>
- *  </listitem>
- * <listitem>
- *   <itemizedlist>
- *   <title>Modifications only to the caps/metadata of a buffer</title>
- *   <listitem><para>
- *     The element does not require writable data, but non-writable buffers
- *     should be subbuffered so that the meta-information can be replaced.
- *   </para></listitem>
- *   <listitem><para>
- *     Elements wishing to operate in this mode should replace the
- *     prepare_output_buffer method to create subbuffers of the input buffer
- *     and set always_in_place to TRUE
- *   </para></listitem>
- *   </itemizedlist>
- *   <itemizedlist>
- *   <title>Example elements</title>
- *     <listitem>Capsfilter when setting caps on outgoing buffers that have
- *     none.</listitem>
- *     <listitem>identity when it is going to re-timestamp buffers by
- *     datarate.</listitem>
- *   </itemizedlist>
- * </listitem>
- * <listitem>
- *   <itemizedlist><title>Normal mode</title>
- *   <listitem><para>
- *     always_in_place flag is not set, or there is no transform_ip function
- *   </para></listitem>
- *   <listitem><para>
- *     Element will receive an input buffer and output buffer to operate on.
- *   </para></listitem>
- *   <listitem><para>
- *     Output buffer is allocated by calling the prepare_output_buffer function.
- *   </para></listitem>
- *   </itemizedlist>
- *   <itemizedlist>
- *   <title>Example elements</title>
- *     <listitem>Videoscale, ffmpegcolorspace, audioconvert when doing
- *     scaling/conversions</listitem>
- *   </itemizedlist>
- * </listitem>
- * <listitem>
- *   <itemizedlist><title>Special output buffer allocations</title>
- *   <listitem><para>
- *     Elements which need to do special allocation of their output buffers
- *     other than what gst_buffer_pad_alloc allows should implement a
- *     prepare_output_buffer method, which calls the parent implementation and
- *     passes the newly allocated buffer.
- *   </para></listitem>
- *   </itemizedlist>
- *   <itemizedlist>
- *   <title>Example elements</title>
- *     <listitem>efence</listitem>
- *   </itemizedlist>
- * </listitem>
- * </orderedlist>
- * </para>
- * </refsect2>
- * <refsect2>
- * <title>Sub-class settable flags on GstBaseTransform</title>
- * <para>
- * <itemizedlist>
- * <listitem><para>
- *   <itemizedlist><title>passthrough</title>
- *     <listitem><para>
- *       Implies that in the current configuration, the sub-class is not
- *       interested in modifying the buffers.
- *     </para></listitem>
- *     <listitem><para>
- *       Elements which are always in passthrough mode whenever the same caps
- *       has been negotiated on both pads can set the class variable
- *       passthrough_on_same_caps to have this behaviour automatically.
- *     </para></listitem>
- *   </itemizedlist>
- * </para></listitem>
- * <listitem><para>
- *   <itemizedlist><title>always_in_place</title>
- *     <listitem><para>
- *       Determines whether a non-writable buffer will be copied before passing
- *       to the transform_ip function.
- *     </para></listitem>
- *     <listitem><para>
- *       Implied TRUE if no transform function is implemented.
- *     </para></listitem>
- *     <listitem><para>
- *       Implied FALSE if ONLY transform function is implemented.
- *     </para></listitem>
- *   </itemizedlist>
- * </para></listitem>
- * </itemizedlist>
- * </para>
- * </refsect2>
+ *
+ *   * #GstBaseTransformClass.passthrough_on_same_caps on an element that
+ *     doesn't implement a transform_caps function is useful for elements that
+ *     only inspect data (such as level)
+ *
+ *   * Example elements
+ *
+ *     * Level
+ *     * Videoscale, audioconvert, videoconvert, audioresample in certain modes.
+ *
+ * ## Modifications in-place - input buffer and output buffer are the same thing.
+ *
+ * * The element must implement a transform_ip function.
+ * * Output buffer size must <= input buffer size
+ * * If the always_in_place flag is set, non-writable buffers will be copied
+ *   and passed to the transform_ip function, otherwise a new buffer will be
+ *   created and the transform function called.
+ *
+ * * Incoming writable buffers will be passed to the transform_ip function
+ *   immediately.
+ * * only implementing transform_ip and not transform implies always_in_place = %TRUE
+ *
+ *   * Example elements:
+ *     * Volume
+ *     * Audioconvert in certain modes (signed/unsigned conversion)
+ *     * videoconvert in certain modes (endianness swapping)
+ *
+ * ## Modifications only to the caps/metadata of a buffer
+ *
+ * * The element does not require writable data, but non-writable buffers
+ *   should be subbuffered so that the meta-information can be replaced.
+ *
+ * * Elements wishing to operate in this mode should replace the
+ *   prepare_output_buffer method to create subbuffers of the input buffer
+ *   and set always_in_place to %TRUE
+ *
+ * * Example elements
+ *   * Capsfilter when setting caps on outgoing buffers that have
+ *     none.
+ *   * identity when it is going to re-timestamp buffers by
+ *     datarate.
+ *
+ * ## Normal mode
+ *   * always_in_place flag is not set, or there is no transform_ip function
+ *   * Element will receive an input buffer and output buffer to operate on.
+ *   * Output buffer is allocated by calling the prepare_output_buffer function.
+ *   * Example elements:
+ *     * Videoscale, videoconvert, audioconvert when doing
+ *     scaling/conversions
+ *
+ * ## Special output buffer allocations
+ *   * Elements which need to do special allocation of their output buffers
+ *     beyond allocating output buffers via the negotiated allocator or
+ *     buffer pool should implement the prepare_output_buffer method.
+ *
+ *   * Example elements:
+ *     * efence
+ *
+ * # Sub-class settable flags on GstBaseTransform
+ *
+ * * passthrough
+ *
+ *   * Implies that in the current configuration, the sub-class is not interested in modifying the buffers.
+ *   * Elements which are always in passthrough mode whenever the same caps has been negotiated on both pads can set the class variable passthrough_on_same_caps to have this behaviour automatically.
+ *
+ * * always_in_place
+ *   * Determines whether a non-writable buffer will be copied before passing
+ *     to the transform_ip function.
+ *
+ *   * Implied %TRUE if no transform function is implemented.
+ *   * Implied %FALSE if ONLY transform function is implemented.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -228,11 +163,20 @@ enum
   PROP_QOS
 };
 
-#define GST_BASE_TRANSFORM_GET_PRIVATE(obj)  \
-    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_TRANSFORM, GstBaseTransformPrivate))
-
 struct _GstBaseTransformPrivate
 {
+  /* Set by sub-class */
+  gboolean passthrough;
+  gboolean always_in_place;
+
+  GstCaps *cache_caps1;
+  gsize cache_caps1_size;
+  GstCaps *cache_caps2;
+  gsize cache_caps2_size;
+  gboolean have_same_caps;
+
+  gboolean negotiated;
+
   /* QoS *//* with LOCK */
   gboolean qos_enabled;
   gdouble proportion;
@@ -243,8 +187,7 @@ struct _GstBaseTransformPrivate
   GstPadMode pad_mode;
 
   gboolean gap_aware;
-
-  gboolean reconfigure;
+  gboolean prefer_passthrough;
 
   /* QoS stats */
   guint64 processed;
@@ -255,17 +198,21 @@ struct _GstBaseTransformPrivate
   GstBufferPool *pool;
   gboolean pool_active;
   GstAllocator *allocator;
-  guint prefix;
-  guint alignment;
+  GstAllocationParams params;
   GstQuery *query;
 };
 
 
 static GstElementClass *parent_class = NULL;
+static gint private_offset = 0;
 
 static void gst_base_transform_class_init (GstBaseTransformClass * klass);
 static void gst_base_transform_init (GstBaseTransform * trans,
     GstBaseTransformClass * klass);
+static GstFlowReturn default_submit_input_buffer (GstBaseTransform * trans,
+    gboolean is_discont, GstBuffer * input);
+static GstFlowReturn default_generate_output (GstBaseTransform * trans,
+    GstBuffer ** outbuf);
 
 /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
  * method to get to the padtemplates */
@@ -290,11 +237,21 @@ gst_base_transform_get_type (void)
 
     _type = g_type_register_static (GST_TYPE_ELEMENT,
         "GstBaseTransform", &base_transform_info, G_TYPE_FLAG_ABSTRACT);
+
+    private_offset =
+        g_type_add_instance_private (_type, sizeof (GstBaseTransformPrivate));
+
     g_once_init_leave (&base_transform_type, _type);
   }
   return base_transform_type;
 }
 
+static inline GstBaseTransformPrivate *
+gst_base_transform_get_instance_private (GstBaseTransform * self)
+{
+  return (G_STRUCT_MEMBER_P (self, private_offset));
+}
+
 static void gst_base_transform_finalize (GObject * object);
 static void gst_base_transform_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
@@ -347,6 +304,9 @@ static GstFlowReturn default_prepare_output_buffer (GstBaseTransform * trans,
     GstBuffer * inbuf, GstBuffer ** outbuf);
 static gboolean default_copy_metadata (GstBaseTransform * trans,
     GstBuffer * inbuf, GstBuffer * outbuf);
+static gboolean
+gst_base_transform_default_transform_meta (GstBaseTransform * trans,
+    GstBuffer * inbuf, GstMeta * meta, GstBuffer * outbuf);
 
 /* static guint gst_base_transform_signals[LAST_SIGNAL] = { 0 }; */
 
@@ -364,13 +324,14 @@ gst_base_transform_class_init (GstBaseTransformClass * klass)
 
   gobject_class = G_OBJECT_CLASS (klass);
 
+  if (private_offset != 0)
+    g_type_class_adjust_private_offset (klass, &private_offset);
+
   GST_DEBUG_CATEGORY_INIT (gst_base_transform_debug, "basetransform", 0,
       "basetransform element");
 
   GST_DEBUG ("gst_base_transform_class_init");
 
-  g_type_class_add_private (klass, sizeof (GstBaseTransformPrivate));
-
   parent_class = g_type_class_peek_parent (klass);
 
   gobject_class->set_property = gst_base_transform_set_property;
@@ -383,6 +344,7 @@ gst_base_transform_class_init (GstBaseTransformClass * klass)
   gobject_class->finalize = gst_base_transform_finalize;
 
   klass->passthrough_on_same_caps = FALSE;
+  klass->transform_ip_on_passthrough = TRUE;
 
   klass->transform_caps =
       GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_caps);
@@ -397,12 +359,16 @@ gst_base_transform_class_init (GstBaseTransformClass * klass)
       GST_DEBUG_FUNCPTR (gst_base_transform_default_propose_allocation);
   klass->transform_size =
       GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_size);
+  klass->transform_meta =
+      GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_meta);
 
   klass->sink_event = GST_DEBUG_FUNCPTR (gst_base_transform_sink_eventfunc);
   klass->src_event = GST_DEBUG_FUNCPTR (gst_base_transform_src_eventfunc);
   klass->prepare_output_buffer =
       GST_DEBUG_FUNCPTR (default_prepare_output_buffer);
   klass->copy_metadata = GST_DEBUG_FUNCPTR (default_copy_metadata);
+  klass->submit_input_buffer = GST_DEBUG_FUNCPTR (default_submit_input_buffer);
+  klass->generate_output = GST_DEBUG_FUNCPTR (default_generate_output);
 }
 
 static void
@@ -410,10 +376,11 @@ gst_base_transform_init (GstBaseTransform * trans,
     GstBaseTransformClass * bclass)
 {
   GstPadTemplate *pad_template;
+  GstBaseTransformPrivate *priv;
 
   GST_DEBUG ("gst_base_transform_init");
 
-  trans->priv = GST_BASE_TRANSFORM_GET_PRIVATE (trans);
+  priv = trans->priv = gst_base_transform_get_instance_private (trans);
 
   pad_template =
       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink");
@@ -443,26 +410,27 @@ gst_base_transform_init (GstBaseTransform * trans,
       GST_DEBUG_FUNCPTR (gst_base_transform_query));
   gst_element_add_pad (GST_ELEMENT (trans), trans->srcpad);
 
-  trans->priv->qos_enabled = DEFAULT_PROP_QOS;
-  trans->cache_caps1 = NULL;
-  trans->cache_caps2 = NULL;
-  trans->priv->pad_mode = GST_PAD_MODE_NONE;
-  trans->priv->gap_aware = FALSE;
+  priv->qos_enabled = DEFAULT_PROP_QOS;
+  priv->cache_caps1 = NULL;
+  priv->cache_caps2 = NULL;
+  priv->pad_mode = GST_PAD_MODE_NONE;
+  priv->gap_aware = FALSE;
+  priv->prefer_passthrough = TRUE;
 
-  trans->passthrough = FALSE;
+  priv->passthrough = FALSE;
   if (bclass->transform == NULL) {
     /* If no transform function, always_in_place is TRUE */
     GST_DEBUG_OBJECT (trans, "setting in_place TRUE");
-    trans->always_in_place = TRUE;
+    priv->always_in_place = TRUE;
 
     if (bclass->transform_ip == NULL) {
       GST_DEBUG_OBJECT (trans, "setting passthrough TRUE");
-      trans->passthrough = TRUE;
+      priv->passthrough = TRUE;
     }
   }
 
-  trans->priv->processed = 0;
-  trans->priv->dropped = 0;
+  priv->processed = 0;
+  priv->dropped = 0;
 }
 
 static GstCaps *
@@ -506,7 +474,7 @@ gst_base_transform_transform_caps (GstBaseTransform * trans,
     ret = klass->transform_caps (trans, direction, caps, filter);
     GST_LOG_OBJECT (trans, "  to: %" GST_PTR_FORMAT, ret);
 
-#ifndef G_DISABLE_ASSERT
+#ifdef GST_ENABLE_EXTRA_CHECKS
     if (filter) {
       if (!gst_caps_is_subset (ret, filter)) {
         GstCaps *intersection;
@@ -533,6 +501,21 @@ gst_base_transform_transform_caps (GstBaseTransform * trans,
 }
 
 static gboolean
+gst_base_transform_default_transform_meta (GstBaseTransform * trans,
+    GstBuffer * inbuf, GstMeta * meta, GstBuffer * outbuf)
+{
+  const GstMetaInfo *info = meta->info;
+  const gchar *const *tags;
+
+  tags = gst_meta_api_type_get_tags (info->api);
+
+  if (!tags)
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
 gst_base_transform_default_transform_size (GstBaseTransform * trans,
     GstPadDirection direction, GstCaps * caps, gsize size,
     GstCaps * othercaps, gsize * othersize)
@@ -634,7 +617,7 @@ gst_base_transform_transform_size (GstBaseTransform * trans,
 /* get the caps that can be handled by @pad. We perform:
  *
  *  - take the caps of peer of otherpad,
- *  - filter against the padtemplate of otherpad, 
+ *  - filter against the padtemplate of otherpad,
  *  - calculate all transforms of remaining caps
  *  - filter against template of @pad
  *
@@ -645,22 +628,23 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad,
     GstCaps * filter)
 {
   GstPad *otherpad;
-  GstCaps *peercaps, *caps, *temp, *peerfilter = NULL;
-  GstCaps *templ;
+  GstCaps *peercaps = NULL, *caps, *temp, *peerfilter = NULL;
+  GstCaps *templ, *otempl;
 
   otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad;
 
-  /* we can do what the peer can */
-  if (filter) {
+  templ = gst_pad_get_pad_template_caps (pad);
+  otempl = gst_pad_get_pad_template_caps (otherpad);
 
+  /* first prepare the filter to be send onwards. We need to filter and
+   * transform it to valid caps for the otherpad. */
+  if (filter) {
     GST_DEBUG_OBJECT (pad, "filter caps  %" GST_PTR_FORMAT, filter);
 
-    /* filtered against our padtemplate on the other side */
-    templ = gst_pad_get_pad_template_caps (pad);
+    /* filtered against our padtemplate of this pad */
     GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
     temp = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST);
     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
-    gst_caps_unref (templ);
 
     /* then see what we can transform this to */
     peerfilter = gst_base_transform_transform_caps (trans,
@@ -668,18 +652,29 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad,
     GST_DEBUG_OBJECT (pad, "transformed  %" GST_PTR_FORMAT, peerfilter);
     gst_caps_unref (temp);
 
-    /* and filter against the template of this pad */
-    templ = gst_pad_get_pad_template_caps (otherpad);
-    GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
-    /* We keep the caps sorted like the returned caps */
-    temp =
-        gst_caps_intersect_full (peerfilter, templ, GST_CAPS_INTERSECT_FIRST);
-    GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
-    gst_caps_unref (peerfilter);
-    gst_caps_unref (templ);
-    peerfilter = temp;
+    if (peerfilter && !gst_caps_is_empty (peerfilter)) {
+      /* and filter against the template of the other pad */
+      GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, otempl);
+      /* We keep the caps sorted like the returned caps */
+      temp =
+          gst_caps_intersect_full (peerfilter, otempl,
+          GST_CAPS_INTERSECT_FIRST);
+      GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
+      gst_caps_unref (peerfilter);
+      peerfilter = temp;
+    }
   }
 
+  GST_DEBUG_OBJECT (pad, "peer filter caps %" GST_PTR_FORMAT, peerfilter);
+
+  if (peerfilter && gst_caps_is_empty (peerfilter)) {
+    GST_DEBUG_OBJECT (pad, "peer filter caps are empty");
+    caps = peerfilter;
+    peerfilter = NULL;
+    goto done;
+  }
+
+  /* query the peer with the transformed filter */
   peercaps = gst_pad_peer_query_caps (otherpad, peerfilter);
 
   if (peerfilter)
@@ -689,13 +684,11 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad,
     GST_DEBUG_OBJECT (pad, "peer caps  %" GST_PTR_FORMAT, peercaps);
 
     /* filtered against our padtemplate on the other side */
-    templ = gst_pad_get_pad_template_caps (otherpad);
-    GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
-    temp = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
+    GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, otempl);
+    temp = gst_caps_intersect_full (peercaps, otempl, GST_CAPS_INTERSECT_FIRST);
     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
-    gst_caps_unref (templ);
   } else {
-    temp = gst_pad_get_pad_template_caps (otherpad);
+    temp = gst_caps_ref (otempl);
   }
 
   /* then see what we can transform this to */
@@ -703,39 +696,34 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad,
       GST_PAD_DIRECTION (otherpad), temp, filter);
   GST_DEBUG_OBJECT (pad, "transformed  %" GST_PTR_FORMAT, caps);
   gst_caps_unref (temp);
-  if (caps == NULL)
+  if (caps == NULL || gst_caps_is_empty (caps))
     goto done;
 
   if (peercaps) {
     /* and filter against the template of this pad */
-    templ = gst_pad_get_pad_template_caps (pad);
     GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
     /* We keep the caps sorted like the returned caps */
     temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
     gst_caps_unref (caps);
-    gst_caps_unref (templ);
     caps = temp;
 
-    /* Now try if we can put the untransformed downstream caps first */
-    temp = gst_caps_intersect_full (peercaps, caps, GST_CAPS_INTERSECT_FIRST);
-    if (!gst_caps_is_empty (temp)) {
-      gst_caps_merge (temp, caps);
-      caps = temp;
-    } else {
-      gst_caps_unref (temp);
+    if (trans->priv->prefer_passthrough) {
+      /* Now try if we can put the untransformed downstream caps first */
+      temp = gst_caps_intersect_full (peercaps, caps, GST_CAPS_INTERSECT_FIRST);
+      if (!gst_caps_is_empty (temp)) {
+        caps = gst_caps_merge (temp, caps);
+      } else {
+        gst_caps_unref (temp);
+      }
     }
   } else {
     gst_caps_unref (caps);
     /* no peer or the peer can do anything, our padtemplate is enough then */
-    caps = gst_pad_get_pad_template_caps (pad);
-
     if (filter) {
-      GstCaps *temp;
-
-      temp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
-      gst_caps_unref (caps);
-      caps = temp;
+      caps = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST);
+    } else {
+      caps = gst_caps_ref (templ);
     }
   }
 
@@ -745,14 +733,17 @@ done:
   if (peercaps)
     gst_caps_unref (peercaps);
 
+  gst_caps_unref (templ);
+  gst_caps_unref (otempl);
+
   return caps;
 }
 
 /* takes ownership of the pool, allocator and query */
 static gboolean
 gst_base_transform_set_allocation (GstBaseTransform * trans,
-    GstBufferPool * pool, GstAllocator * allocator, guint prefix,
-    guint alignment, GstQuery * query)
+    GstBufferPool * pool, GstAllocator * allocator,
+    GstAllocationParams * params, GstQuery * query)
 {
   GstAllocator *oldalloc;
   GstBufferPool *oldpool;
@@ -763,12 +754,17 @@ gst_base_transform_set_allocation (GstBaseTransform * trans,
   oldpool = priv->pool;
   priv->pool = pool;
   priv->pool_active = FALSE;
+
   oldalloc = priv->allocator;
   priv->allocator = allocator;
+
   oldquery = priv->query;
   priv->query = query;
-  priv->prefix = prefix;
-  priv->alignment = alignment;
+
+  if (params)
+    priv->params = *params;
+  else
+    gst_allocation_params_init (&priv->params);
   GST_OBJECT_UNLOCK (trans);
 
   if (oldpool) {
@@ -777,13 +773,12 @@ gst_base_transform_set_allocation (GstBaseTransform * trans,
     gst_object_unref (oldpool);
   }
   if (oldalloc) {
-    gst_allocator_unref (oldalloc);
+    gst_object_unref (oldalloc);
   }
   if (oldquery) {
     gst_query_unref (oldquery);
   }
   return TRUE;
-
 }
 
 static gboolean
@@ -792,19 +787,27 @@ gst_base_transform_default_decide_allocation (GstBaseTransform * trans,
 {
   guint i, n_metas;
   GstBaseTransformClass *klass;
+  GstCaps *outcaps;
+  GstBufferPool *pool;
+  guint size, min, max;
+  GstAllocator *allocator;
+  GstAllocationParams params;
+  GstStructure *config;
+  gboolean update_allocator;
 
   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
   n_metas = gst_query_get_n_allocation_metas (query);
   for (i = 0; i < n_metas; i++) {
     GType api;
+    const GstStructure *params;
     gboolean remove;
 
-    api = gst_query_parse_nth_allocation_meta (query, i);
+    api = gst_query_parse_nth_allocation_meta (query, i, &params);
 
     /* by default we remove all metadata, subclasses should implement a
      * filter_meta function */
-    if (gst_meta_api_type_has_tag (api, GST_META_TAG_MEMORY)) {
+    if (gst_meta_api_type_has_tag (api, _gst_meta_tag_memory)) {
       /* remove all memory dependent metadata because we are going to have to
        * allocate different memory for input and output. */
       GST_LOG_OBJECT (trans, "removing memory specific metadata %s",
@@ -812,7 +815,7 @@ gst_base_transform_default_decide_allocation (GstBaseTransform * trans,
       remove = TRUE;
     } else if (G_LIKELY (klass->filter_meta)) {
       /* remove if the subclass said so */
-      remove = !klass->filter_meta (trans, query, api);
+      remove = !klass->filter_meta (trans, query, api, params);
       GST_LOG_OBJECT (trans, "filter_meta for api %s returned: %s",
           g_type_name (api), (remove ? "remove" : "keep"));
     } else {
@@ -826,7 +829,82 @@ gst_base_transform_default_decide_allocation (GstBaseTransform * trans,
       n_metas--;
     }
   }
+
+  gst_query_parse_allocation (query, &outcaps, NULL);
+
+  /* we got configuration from our peer or the decide_allocation method,
+   * parse them */
+  if (gst_query_get_n_allocation_params (query) > 0) {
+    /* try the allocator */
+    gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
+    update_allocator = TRUE;
+  } else {
+    allocator = NULL;
+    gst_allocation_params_init (&params);
+    update_allocator = FALSE;
+  }
+
+  if (gst_query_get_n_allocation_pools (query) > 0) {
+    gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
+
+    if (pool == NULL) {
+      /* no pool, we can make our own */
+      GST_DEBUG_OBJECT (trans, "no pool, making new pool");
+      pool = gst_buffer_pool_new ();
+    }
+  } else {
+    pool = NULL;
+    size = min = max = 0;
+  }
+
+  /* now configure */
+  if (pool) {
+    config = gst_buffer_pool_get_config (pool);
+    gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
+    gst_buffer_pool_config_set_allocator (config, allocator, &params);
+
+    /* buffer pool may have to do some changes */
+    if (!gst_buffer_pool_set_config (pool, config)) {
+      config = gst_buffer_pool_get_config (pool);
+
+      /* If change are not acceptable, fallback to generic pool */
+      if (!gst_buffer_pool_config_validate_params (config, outcaps, size, min,
+              max)) {
+        GST_DEBUG_OBJECT (trans, "unsupported pool, making new pool");
+
+        gst_object_unref (pool);
+        pool = gst_buffer_pool_new ();
+        gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
+        gst_buffer_pool_config_set_allocator (config, allocator, &params);
+      }
+
+      if (!gst_buffer_pool_set_config (pool, config))
+        goto config_failed;
+    }
+  }
+
+  if (update_allocator)
+    gst_query_set_nth_allocation_param (query, 0, allocator, &params);
+  else
+    gst_query_add_allocation_param (query, allocator, &params);
+  if (allocator)
+    gst_object_unref (allocator);
+
+  if (pool) {
+    gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
+    gst_object_unref (pool);
+  }
+
   return TRUE;
+
+config_failed:
+  if (pool)
+    gst_object_unref (pool);
+
+  GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS,
+      ("Failed to configure the buffer pool"),
+      ("Configuration is most likely invalid, please report this issue."));
+  return FALSE;
 }
 
 static gboolean
@@ -835,9 +913,10 @@ gst_base_transform_do_bufferpool (GstBaseTransform * trans, GstCaps * outcaps)
   GstQuery *query;
   gboolean result = TRUE;
   GstBufferPool *pool = NULL;
-  guint size, min, max, prefix, alignment;
   GstBaseTransformClass *klass;
-  GstAllocator *allocator = NULL;
+  GstBaseTransformPrivate *priv = trans->priv;
+  GstAllocator *allocator;
+  GstAllocationParams params;
 
   /* there are these possibilities:
    *
@@ -848,13 +927,13 @@ gst_base_transform_do_bufferpool (GstBaseTransform * trans, GstCaps * outcaps)
    *    propose_allocation vmethod will be called and we will configure the
    *    upstream allocator with our proposed values then.
    */
-  if (trans->passthrough || trans->always_in_place) {
+  if (priv->passthrough || priv->always_in_place) {
     /* we are in passthrough, the input buffer is never copied and always passed
      * along. We never allocate an output buffer on the srcpad. What we do is
      * let the upstream element decide if it wants to use a bufferpool and
      * then we will proxy the downstream pool */
     GST_DEBUG_OBJECT (trans, "we're passthough, delay bufferpool");
-    gst_base_transform_set_allocation (trans, NULL, NULL, 0, 0, NULL);
+    gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL);
     return TRUE;
   }
 
@@ -870,38 +949,40 @@ gst_base_transform_do_bufferpool (GstBaseTransform * trans, GstCaps * outcaps)
   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
   GST_DEBUG_OBJECT (trans, "calling decide_allocation");
-  if (G_LIKELY (klass->decide_allocation))
-    if ((result = klass->decide_allocation (trans, query)) == FALSE)
-      goto no_decide_allocation;
-
-  /* we got configuration from our peer, parse them */
-  gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
-      &alignment, &pool);
-
-  if (size == 0) {
-    /* no size, we have variable size buffers */
-    if (gst_query_get_n_allocation_memories (query) > 0) {
-      if ((allocator = gst_query_parse_nth_allocation_memory (query, 0)))
-        gst_allocator_ref (allocator);
-    }
-    GST_DEBUG_OBJECT (trans, "no size, using allocator %p", allocator);
-  } else if (pool == NULL) {
-    GstStructure *config;
+  g_assert (klass->decide_allocation != NULL);
+  result = klass->decide_allocation (trans, query);
 
-    /* we did not get a pool, make one ourselves then */
-    pool = gst_buffer_pool_new ();
+  GST_DEBUG_OBJECT (trans, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,
+      query);
 
-    GST_DEBUG_OBJECT (trans, "no pool, making one");
-    config = gst_buffer_pool_get_config (pool);
-    gst_buffer_pool_config_set (config, outcaps, size, min, max, prefix,
-        alignment);
-    gst_buffer_pool_set_config (pool, config);
+  if (!result)
+    goto no_decide_allocation;
+
+  /* check again in case the sub-class have switch to passthrough/in-place
+   * after looking at the meta APIs */
+  if (priv->passthrough || priv->always_in_place) {
+    GST_DEBUG_OBJECT (trans, "no doing passthrough, delay bufferpool");
+    gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL);
+    gst_query_unref (query);
+    return TRUE;
   }
 
-  /* and store */
+  /* we got configuration from our peer or the decide_allocation method,
+   * parse them */
+  if (gst_query_get_n_allocation_params (query) > 0) {
+    gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
+  } else {
+    allocator = NULL;
+    gst_allocation_params_init (&params);
+  }
+
+  if (gst_query_get_n_allocation_pools (query) > 0)
+    gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
+
+  /* now store */
   result =
-      gst_base_transform_set_allocation (trans, pool, allocator, prefix,
-      alignment, query);
+      gst_base_transform_set_allocation (trans, pool, allocator, &params,
+      query);
 
   return result;
 
@@ -923,6 +1004,7 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in,
 {
   gboolean ret = TRUE;
   GstBaseTransformClass *klass;
+  GstBaseTransformPrivate *priv = trans->priv;
 
   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
@@ -930,23 +1012,17 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in,
   GST_DEBUG_OBJECT (trans, "out caps: %" GST_PTR_FORMAT, out);
 
   /* clear the cache */
-  gst_caps_replace (&trans->cache_caps1, NULL);
-  gst_caps_replace (&trans->cache_caps2, NULL);
+  gst_caps_replace (&priv->cache_caps1, NULL);
+  gst_caps_replace (&priv->cache_caps2, NULL);
 
   /* figure out same caps state */
-  trans->have_same_caps = gst_caps_is_equal (in, out);
-  GST_DEBUG_OBJECT (trans, "have_same_caps: %d", trans->have_same_caps);
-
-  /* If we've a transform_ip method and same input/output caps, set in_place
-   * by default. If for some reason the sub-class prefers using a transform
-   * function, it can clear the in place flag in the set_caps */
-  gst_base_transform_set_in_place (trans,
-      klass->transform_ip && trans->have_same_caps);
+  priv->have_same_caps = gst_caps_is_equal (in, out);
+  GST_DEBUG_OBJECT (trans, "have_same_caps: %d", priv->have_same_caps);
 
   /* Set the passthrough if the class wants passthrough_on_same_caps
    * and we have the same caps on each pad */
   if (klass->passthrough_on_same_caps)
-    gst_base_transform_set_passthrough (trans, trans->have_same_caps);
+    gst_base_transform_set_passthrough (trans, priv->have_same_caps);
 
   /* now configure the element with the caps */
   if (klass->set_caps) {
@@ -954,8 +1030,6 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in,
     ret = klass->set_caps (trans, in, out);
   }
 
-  trans->negotiated = ret;
-
   return ret;
 }
 
@@ -963,8 +1037,7 @@ static GstCaps *
 gst_base_transform_default_fixate_caps (GstBaseTransform * trans,
     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
 {
-  othercaps = gst_caps_make_writable (othercaps);
-  gst_caps_fixate (othercaps);
+  othercaps = gst_caps_fixate (othercaps);
   GST_DEBUG_OBJECT (trans, "fixated to %" GST_PTR_FORMAT, othercaps);
 
   return othercaps;
@@ -1011,7 +1084,7 @@ gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad,
 
   /* The caps we can actually output is the intersection of the transformed
    * caps with the pad template for the pad */
-  if (othercaps) {
+  if (othercaps && !gst_caps_is_empty (othercaps)) {
     GstCaps *intersect, *templ_caps;
 
     templ_caps = gst_pad_get_pad_template_caps (otherpad);
@@ -1075,6 +1148,7 @@ gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad,
         gst_caps_unref (othercaps);
         othercaps = intersection;
       } else {
+        gst_caps_unref (othercaps);
         othercaps = peercaps;
       }
 
@@ -1161,68 +1235,51 @@ static gboolean
 gst_base_transform_acceptcaps_default (GstBaseTransform * trans,
     GstPadDirection direction, GstCaps * caps)
 {
-#if 0
-  GstPad *otherpad;
-  GstCaps *othercaps = NULL;
-#endif
+  GstPad *pad, *otherpad;
+  GstCaps *templ, *otempl, *ocaps = NULL;
   gboolean ret = TRUE;
 
-#if 0
-  otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad;
-
-  /* we need fixed caps for the check, fall back to the default implementation
-   * if we don't */
-  if (!gst_caps_is_fixed (caps))
-#endif
-  {
-    GstCaps *allowed;
-
-    GST_DEBUG_OBJECT (trans, "accept caps %" GST_PTR_FORMAT, caps);
-
-    /* get all the formats we can handle on this pad */
-    if (direction == GST_PAD_SRC)
-      allowed = gst_pad_query_caps (trans->srcpad, NULL);
-    else
-      allowed = gst_pad_query_caps (trans->sinkpad, NULL);
-
-    if (!allowed) {
-      GST_DEBUG_OBJECT (trans, "gst_pad_query_caps() failed");
-      goto no_transform_possible;
-    }
-
-    GST_DEBUG_OBJECT (trans, "allowed caps %" GST_PTR_FORMAT, allowed);
-
-    /* intersect with the requested format */
-    ret = gst_caps_is_subset (caps, allowed);
-    gst_caps_unref (allowed);
-
-    if (!ret)
-      goto no_transform_possible;
-  }
-#if 0
-  else {
-    GST_DEBUG_OBJECT (pad, "accept caps %" GST_PTR_FORMAT, caps);
-
-    /* find best possible caps for the other pad as a way to see if we can
-     * transform this caps. */
-    othercaps = gst_base_transform_find_transform (trans, pad, caps, FALSE);
-    if (!othercaps || gst_caps_is_empty (othercaps))
-      goto no_transform_possible;
-
-    GST_DEBUG_OBJECT (pad, "we can transform to %" GST_PTR_FORMAT, othercaps);
-  }
-#endif
+  pad =
+      (direction ==
+      GST_PAD_SINK) ? GST_BASE_TRANSFORM_SINK_PAD (trans) :
+      GST_BASE_TRANSFORM_SRC_PAD (trans);
+  otherpad =
+      (direction ==
+      GST_PAD_SINK) ? GST_BASE_TRANSFORM_SRC_PAD (trans) :
+      GST_BASE_TRANSFORM_SINK_PAD (trans);
+
+  GST_DEBUG_OBJECT (trans, "accept caps %" GST_PTR_FORMAT, caps);
+
+  templ = gst_pad_get_pad_template_caps (pad);
+  otempl = gst_pad_get_pad_template_caps (otherpad);
+
+  /* get all the formats we can handle on this pad */
+  GST_DEBUG_OBJECT (trans, "intersect with pad template: %" GST_PTR_FORMAT,
+      templ);
+  if (!gst_caps_can_intersect (caps, templ))
+    goto reject_caps;
+
+  GST_DEBUG_OBJECT (trans, "trying to transform with filter: %"
+      GST_PTR_FORMAT " (the other pad template)", otempl);
+  ocaps = gst_base_transform_transform_caps (trans, direction, caps, otempl);
+  if (!ocaps || gst_caps_is_empty (ocaps))
+    goto no_transform_possible;
 
 done:
-#if 0
-  /* We know it's always NULL since we never use it */
-  if (othercaps)
-    gst_caps_unref (othercaps);
-#endif
-
+  GST_DEBUG_OBJECT (trans, "accept-caps result: %d", ret);
+  if (ocaps)
+    gst_caps_unref (ocaps);
+  gst_caps_unref (templ);
+  gst_caps_unref (otempl);
   return ret;
 
   /* ERRORS */
+reject_caps:
+  {
+    GST_DEBUG_OBJECT (trans, "caps can't intersect with the template");
+    ret = FALSE;
+    goto done;
+  }
 no_transform_possible:
   {
     GST_DEBUG_OBJECT (trans,
@@ -1242,7 +1299,8 @@ static gboolean
 gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad,
     GstCaps * incaps)
 {
-  GstCaps *outcaps;
+  GstBaseTransformPrivate *priv = trans->priv;
+  GstCaps *outcaps, *prev_incaps = NULL, *prev_outcaps = NULL;
   gboolean ret = TRUE;
 
   GST_DEBUG_OBJECT (pad, "have new caps %p %" GST_PTR_FORMAT, incaps, incaps);
@@ -1261,17 +1319,23 @@ gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad,
     outcaps = gst_caps_ref (incaps);
   }
 
-  /* call configure now */
-  if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps)))
-    goto failed_configure;
-
-  GST_OBJECT_LOCK (trans->sinkpad);
-  GST_OBJECT_FLAG_UNSET (trans->srcpad, GST_PAD_FLAG_NEED_RECONFIGURE);
-  trans->priv->reconfigure = FALSE;
-  GST_OBJECT_UNLOCK (trans->sinkpad);
+  prev_incaps = gst_pad_get_current_caps (trans->sinkpad);
+  prev_outcaps = gst_pad_get_current_caps (trans->srcpad);
+  if (prev_incaps && prev_outcaps && gst_caps_is_equal (prev_incaps, incaps)
+      && gst_caps_is_equal (prev_outcaps, outcaps)) {
+    GST_DEBUG_OBJECT (trans,
+        "New caps equal to old ones: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT,
+        incaps, outcaps);
+    ret = TRUE;
+  } else {
+    /* call configure now */
+    if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps)))
+      goto failed_configure;
 
-  /* we know this will work, we implement the setcaps */
-  gst_pad_push_event (trans->srcpad, gst_event_new_caps (outcaps));
+    if (!prev_outcaps || !gst_caps_is_equal (outcaps, prev_outcaps))
+      /* let downstream know about our caps */
+      ret = gst_pad_set_caps (trans->srcpad, outcaps);
+  }
 
   if (ret) {
     /* try to get a pool when needed */
@@ -1281,8 +1345,14 @@ gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad,
 done:
   if (outcaps)
     gst_caps_unref (outcaps);
+  if (prev_incaps)
+    gst_caps_unref (prev_incaps);
+  if (prev_outcaps)
+    gst_caps_unref (prev_outcaps);
 
-  trans->negotiated = ret;
+  GST_OBJECT_LOCK (trans);
+  priv->negotiated = ret;
+  GST_OBJECT_UNLOCK (trans);
 
   return ret;
 
@@ -1320,10 +1390,11 @@ gst_base_transform_default_propose_allocation (GstBaseTransform * trans,
     n_metas = gst_query_get_n_allocation_metas (decide_query);
     for (i = 0; i < n_metas; i++) {
       GType api;
+      const GstStructure *params;
 
-      api = gst_query_parse_nth_allocation_meta (decide_query, i);
+      api = gst_query_parse_nth_allocation_meta (decide_query, i, &params);
       GST_DEBUG_OBJECT (trans, "proposing metadata %s", g_type_name (api));
-      gst_query_add_allocation_meta (query, api);
+      gst_query_add_allocation_meta (query, api, params);
     }
     ret = TRUE;
   }
@@ -1331,12 +1402,48 @@ gst_base_transform_default_propose_allocation (GstBaseTransform * trans,
 }
 
 static gboolean
+gst_base_transform_reconfigure (GstBaseTransform * trans)
+{
+  gboolean reconfigure, ret = TRUE;
+
+  reconfigure = gst_pad_check_reconfigure (trans->srcpad);
+
+  if (G_UNLIKELY (reconfigure)) {
+    GstCaps *incaps;
+
+    GST_DEBUG_OBJECT (trans, "we had a pending reconfigure");
+
+    incaps = gst_pad_get_current_caps (trans->sinkpad);
+    if (incaps == NULL)
+      goto done;
+
+    /* if we need to reconfigure we pretend new caps arrived. This
+     * will reconfigure the transform with the new output format. */
+    if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps)) {
+      GST_ELEMENT_WARNING (trans, STREAM, FORMAT,
+          ("not negotiated"), ("not negotiated"));
+      ret = FALSE;
+    }
+
+    gst_caps_unref (incaps);
+  }
+
+done:
+
+  if (!ret)
+    gst_pad_mark_reconfigure (trans->srcpad);
+
+  return ret;
+}
+
+static gboolean
 gst_base_transform_default_query (GstBaseTransform * trans,
     GstPadDirection direction, GstQuery * query)
 {
   gboolean ret = FALSE;
   GstPad *pad, *otherpad;
   GstBaseTransformClass *klass;
+  GstBaseTransformPrivate *priv = trans->priv;
 
   if (direction == GST_PAD_SRC) {
     pad = trans->srcpad;
@@ -1352,21 +1459,25 @@ gst_base_transform_default_query (GstBaseTransform * trans,
     case GST_QUERY_ALLOCATION:
     {
       GstQuery *decide_query = NULL;
-      gboolean negotiated;
 
       /* can only be done on the sinkpad */
       if (direction != GST_PAD_SINK)
         goto done;
 
+      ret = gst_base_transform_reconfigure (trans);
+      if (G_UNLIKELY (!ret))
+        goto done;
+
       GST_OBJECT_LOCK (trans);
-      if (G_UNLIKELY (!(negotiated = trans->negotiated))) {
+      if (!priv->negotiated && !priv->passthrough && (klass->set_caps != NULL)) {
         GST_DEBUG_OBJECT (trans,
-            "not negotiated yet, can't answer ALLOCATION query");
+            "not negotiated yet but need negotiation, can't answer ALLOCATION query");
         GST_OBJECT_UNLOCK (trans);
         goto done;
       }
-      if ((decide_query = trans->priv->query))
-        gst_query_ref (decide_query);
+
+      decide_query = trans->priv->query;
+      trans->priv->query = NULL;
       GST_OBJECT_UNLOCK (trans);
 
       GST_DEBUG_OBJECT (trans,
@@ -1379,8 +1490,16 @@ gst_base_transform_default_query (GstBaseTransform * trans,
       else
         ret = FALSE;
 
-      if (decide_query)
-        gst_query_unref (decide_query);
+      if (decide_query) {
+        GST_OBJECT_LOCK (trans);
+
+        if (trans->priv->query == NULL)
+          trans->priv->query = decide_query;
+        else
+          gst_query_unref (decide_query);
+
+        GST_OBJECT_UNLOCK (trans);
+      }
 
       GST_DEBUG_OBJECT (trans, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret,
           query);
@@ -1450,7 +1569,7 @@ gst_base_transform_query (GstPad * pad, GstObject * parent, GstQuery * query)
   GstBaseTransformClass *bclass;
   gboolean ret = FALSE;
 
-  trans = GST_BASE_TRANSFORM (parent);
+  trans = GST_BASE_TRANSFORM_CAST (parent);
   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
   if (bclass->query)
@@ -1466,7 +1585,7 @@ default_prepare_output_buffer (GstBaseTransform * trans,
     GstBuffer * inbuf, GstBuffer ** outbuf)
 {
   GstBaseTransformPrivate *priv;
-  GstFlowReturn ret = GST_FLOW_OK;
+  GstFlowReturn ret;
   GstBaseTransformClass *bclass;
   GstCaps *incaps, *outcaps;
   gsize insize, outsize;
@@ -1476,8 +1595,8 @@ default_prepare_output_buffer (GstBaseTransform * trans,
   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
   /* figure out how to allocate an output buffer */
-  if (trans->passthrough) {
-    /* passthrough, we will not modify the incomming buffer so we can just
+  if (priv->passthrough) {
+    /* passthrough, we will not modify the incoming buffer so we can just
      * reuse it */
     GST_DEBUG_OBJECT (trans, "passthrough: reusing input buffer");
     *outbuf = inbuf;
@@ -1494,11 +1613,14 @@ default_prepare_output_buffer (GstBaseTransform * trans,
     }
     GST_DEBUG_OBJECT (trans, "using pool alloc");
     ret = gst_buffer_pool_acquire_buffer (priv->pool, outbuf, NULL);
+    if (ret != GST_FLOW_OK)
+      goto alloc_failed;
+
     goto copy_meta;
   }
 
   /* no pool, we need to figure out the size of the output buffer first */
-  if ((bclass->transform_ip != NULL) && trans->always_in_place) {
+  if ((bclass->transform_ip != NULL) && priv->always_in_place) {
     /* we want to do an in-place alloc */
     if (gst_buffer_is_writable (inbuf)) {
       GST_DEBUG_OBJECT (trans, "inplace reuse writable input buffer");
@@ -1515,6 +1637,10 @@ default_prepare_output_buffer (GstBaseTransform * trans,
   incaps = gst_pad_get_current_caps (trans->sinkpad);
   outcaps = gst_pad_get_current_caps (trans->srcpad);
 
+  /* srcpad might be flushing already if we're being shut down */
+  if (outcaps == NULL)
+    goto no_outcaps;
+
   GST_DEBUG_OBJECT (trans, "getting output size for alloc");
   /* copy transform, figure out the output size */
   insize = gst_buffer_get_size (inbuf);
@@ -1528,7 +1654,11 @@ default_prepare_output_buffer (GstBaseTransform * trans,
     goto unknown_size;
 
   GST_DEBUG_OBJECT (trans, "doing alloc of size %" G_GSIZE_FORMAT, outsize);
-  *outbuf = gst_buffer_new_allocate (priv->allocator, outsize, priv->alignment);
+  *outbuf = gst_buffer_new_allocate (priv->allocator, outsize, &priv->params);
+  if (!*outbuf) {
+    ret = GST_FLOW_ERROR;
+    goto alloc_failed;
+  }
 
 copy_meta:
   /* copy the metadata */
@@ -1540,10 +1670,9 @@ copy_meta:
     }
 
 done:
-  return ret;
+  return GST_FLOW_OK;
 
   /* ERRORS */
-  /* ERRORS */
 activate_failed:
   {
     GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS,
@@ -1555,6 +1684,17 @@ unknown_size:
     GST_ERROR_OBJECT (trans, "unknown output size");
     return GST_FLOW_ERROR;
   }
+alloc_failed:
+  {
+    GST_DEBUG_OBJECT (trans, "could not allocate buffer from pool");
+    return ret;
+  }
+no_outcaps:
+  {
+    GST_DEBUG_OBJECT (trans, "no output caps, source pad has been deactivated");
+    gst_caps_unref (incaps);
+    return GST_FLOW_FLUSHING;
+  }
 }
 
 typedef struct
@@ -1571,16 +1711,11 @@ foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data)
   GstBaseTransformClass *klass;
   const GstMetaInfo *info = (*meta)->info;
   GstBuffer *outbuf = data->outbuf;
-  gboolean do_copy;
+  gboolean do_copy = FALSE;
 
   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
-  if (GST_META_FLAG_IS_SET (*meta, GST_META_FLAG_POOLED)) {
-    /* never call the transform_meta with pool private metadata */
-    GST_DEBUG_OBJECT (trans, "not copying pooled metadata %s",
-        g_type_name (info->api));
-    do_copy = FALSE;
-  } else if (gst_meta_api_type_has_tag (info->api, GST_META_TAG_MEMORY)) {
+  if (gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) {
     /* never call the transform_meta with memory specific metadata */
     GST_DEBUG_OBJECT (trans, "not copying memory specific metadata %s",
         g_type_name (info->api));
@@ -1589,20 +1724,21 @@ foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data)
     do_copy = klass->transform_meta (trans, outbuf, *meta, inbuf);
     GST_DEBUG_OBJECT (trans, "transformed metadata %s: copy: %d",
         g_type_name (info->api), do_copy);
-  } else {
-    do_copy = FALSE;
-    GST_DEBUG_OBJECT (trans, "not copying metadata %s",
-        g_type_name (info->api));
   }
 
   /* we only copy metadata when the subclass implemented a transform_meta
-   * function and when it returns TRUE */
+   * function and when it returns %TRUE */
   if (do_copy) {
     GstMetaTransformCopy copy_data = { FALSE, 0, -1 };
-    GST_DEBUG_OBJECT (trans, "copy metadata %s", g_type_name (info->api));
     /* simply copy then */
-    info->transform_func (outbuf, *meta, inbuf,
-        _gst_meta_transform_copy, &copy_data);
+    if (info->transform_func) {
+      GST_DEBUG_OBJECT (trans, "copy metadata %s", g_type_name (info->api));
+      info->transform_func (outbuf, *meta, inbuf,
+          _gst_meta_transform_copy, &copy_data);
+    } else {
+      GST_DEBUG_OBJECT (trans, "couldn't copy metadata %s",
+          g_type_name (info->api));
+    }
   }
   return TRUE;
 }
@@ -1646,7 +1782,7 @@ not_writable:
   }
 }
 
-/* Given @caps calcultate the size of one unit.
+/* Given @caps calculate the size of one unit.
  *
  * For video caps, this is the size of one frame (and thus one buffer).
  * For audio caps, this is the size of one sample.
@@ -1657,7 +1793,7 @@ not_writable:
  * We have two cache locations to store the size, one for the source caps
  * and one for the sink caps.
  *
- * this function returns FALSE if no size could be calculated.
+ * this function returns %FALSE if no size could be calculated.
  */
 static gboolean
 gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
@@ -1665,16 +1801,17 @@ gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
 {
   gboolean res = FALSE;
   GstBaseTransformClass *bclass;
+  GstBaseTransformPrivate *priv = trans->priv;
 
   /* see if we have the result cached */
-  if (trans->cache_caps1 == caps) {
-    *size = trans->cache_caps1_size;
+  if (priv->cache_caps1 == caps) {
+    *size = priv->cache_caps1_size;
     GST_DEBUG_OBJECT (trans,
         "returned %" G_GSIZE_FORMAT " from first cache", *size);
     return TRUE;
   }
-  if (trans->cache_caps2 == caps) {
-    *size = trans->cache_caps2_size;
+  if (priv->cache_caps2 == caps) {
+    *size = priv->cache_caps2_size;
     GST_DEBUG_OBJECT (trans,
         "returned %" G_GSIZE_FORMAT " from second cached", *size);
     return TRUE;
@@ -1683,19 +1820,19 @@ gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
   res = bclass->get_unit_size (trans, caps, size);
   GST_DEBUG_OBJECT (trans,
-      "caps %" GST_PTR_FORMAT ") has unit size %" G_GSIZE_FORMAT ", res %s",
+      "caps %" GST_PTR_FORMAT " has unit size %" G_GSIZE_FORMAT ", res %s",
       caps, *size, res ? "TRUE" : "FALSE");
 
   if (res) {
     /* and cache the values */
-    if (trans->cache_caps1 == NULL) {
-      gst_caps_replace (&trans->cache_caps1, caps);
-      trans->cache_caps1_size = *size;
+    if (priv->cache_caps1 == NULL) {
+      gst_caps_replace (&priv->cache_caps1, caps);
+      priv->cache_caps1_size = *size;
       GST_DEBUG_OBJECT (trans,
           "caching %" G_GSIZE_FORMAT " in first cache", *size);
-    } else if (trans->cache_caps2 == NULL) {
-      gst_caps_replace (&trans->cache_caps2, caps);
-      trans->cache_caps2_size = *size;
+    } else if (priv->cache_caps2 == NULL) {
+      gst_caps_replace (&priv->cache_caps2, caps);
+      priv->cache_caps2_size = *size;
       GST_DEBUG_OBJECT (trans,
           "caching %" G_GSIZE_FORMAT " in second cache", *size);
     } else {
@@ -1713,7 +1850,7 @@ gst_base_transform_sink_event (GstPad * pad, GstObject * parent,
   GstBaseTransformClass *bclass;
   gboolean ret = TRUE;
 
-  trans = GST_BASE_TRANSFORM (parent);
+  trans = GST_BASE_TRANSFORM_CAST (parent);
   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
   if (bclass->sink_event)
@@ -1728,6 +1865,7 @@ static gboolean
 gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event)
 {
   gboolean ret = TRUE, forward = TRUE;
+  GstBaseTransformPrivate *priv = trans->priv;
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_FLUSH_START:
@@ -1735,16 +1873,16 @@ gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event)
     case GST_EVENT_FLUSH_STOP:
       GST_OBJECT_LOCK (trans);
       /* reset QoS parameters */
-      trans->priv->proportion = 1.0;
-      trans->priv->earliest_time = -1;
-      trans->priv->discont = FALSE;
-      trans->priv->processed = 0;
-      trans->priv->dropped = 0;
+      priv->proportion = 1.0;
+      priv->earliest_time = -1;
+      priv->discont = FALSE;
+      priv->processed = 0;
+      priv->dropped = 0;
       GST_OBJECT_UNLOCK (trans);
       /* we need new segment info after the flush. */
       trans->have_segment = FALSE;
       gst_segment_init (&trans->segment, GST_FORMAT_UNDEFINED);
-      trans->priv->position_out = GST_CLOCK_TIME_NONE;
+      priv->position_out = GST_CLOCK_TIME_NONE;
       break;
     case GST_EVENT_EOS:
       break;
@@ -1755,7 +1893,11 @@ gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event)
       GstCaps *caps;
 
       gst_event_parse_caps (event, &caps);
+      /* clear any pending reconfigure flag */
+      gst_pad_check_reconfigure (trans->srcpad);
       ret = gst_base_transform_setcaps (trans, trans->sinkpad, caps);
+      if (!ret)
+        gst_pad_mark_reconfigure (trans->srcpad);
 
       forward = FALSE;
       break;
@@ -1789,7 +1931,7 @@ gst_base_transform_src_event (GstPad * pad, GstObject * parent,
   GstBaseTransformClass *bclass;
   gboolean ret = TRUE;
 
-  trans = GST_BASE_TRANSFORM (parent);
+  trans = GST_BASE_TRANSFORM_CAST (parent);
   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
   if (bclass->src_event)
@@ -1831,73 +1973,38 @@ gst_base_transform_src_eventfunc (GstBaseTransform * trans, GstEvent * event)
   return ret;
 }
 
-/* perform a transform on @inbuf and put the result in @outbuf.
- *
- * This function is common to the push and pull-based operations.
- *
- * This function takes ownership of @inbuf */
+/* Takes the input buffer */
 static GstFlowReturn
-gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf,
-    GstBuffer ** outbuf)
+default_submit_input_buffer (GstBaseTransform * trans, gboolean is_discont,
+    GstBuffer * inbuf)
 {
-  GstBaseTransformClass *bclass;
+  GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
+  GstBaseTransformPrivate *priv = trans->priv;
   GstFlowReturn ret = GST_FLOW_OK;
-  gboolean want_in_place;
   GstClockTime running_time;
   GstClockTime timestamp;
-  gboolean reconfigure;
-
-  bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
-
-  GST_OBJECT_LOCK (trans->sinkpad);
-  reconfigure = GST_PAD_NEEDS_RECONFIGURE (trans->srcpad)
-      || trans->priv->reconfigure;
-  GST_OBJECT_FLAG_UNSET (trans->srcpad, GST_PAD_FLAG_NEED_RECONFIGURE);
-  trans->priv->reconfigure = FALSE;
-  GST_OBJECT_UNLOCK (trans->sinkpad);
 
-  if (G_UNLIKELY (reconfigure)) {
-    GstCaps *incaps;
-
-    GST_DEBUG_OBJECT (trans, "we had a pending reconfigure");
-
-    incaps = gst_pad_get_current_caps (trans->sinkpad);
-    if (incaps == NULL)
-      goto no_reconfigure;
-
-    /* if we need to reconfigure we pretend new caps arrived. This
-     * will reconfigure the transform with the new output format. */
-    if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps)) {
-      gst_caps_unref (incaps);
-      goto not_negotiated;
-    }
-    gst_caps_unref (incaps);
-  }
+  if (G_UNLIKELY (!gst_base_transform_reconfigure (trans)))
+    goto not_negotiated;
 
-no_reconfigure:
   if (GST_BUFFER_OFFSET_IS_VALID (inbuf))
     GST_DEBUG_OBJECT (trans,
-        "handling buffer %p of size %" G_GSIZE_FORMAT " and offset %"
-        G_GUINT64_FORMAT, inbuf, gst_buffer_get_size (inbuf),
-        GST_BUFFER_OFFSET (inbuf));
+        "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT
+        " and offset %" G_GUINT64_FORMAT, inbuf, gst_buffer_get_size (inbuf),
+        GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)), GST_BUFFER_OFFSET (inbuf));
   else
     GST_DEBUG_OBJECT (trans,
-        "handling buffer %p of size %" G_GSIZE_FORMAT " and offset NONE", inbuf,
-        gst_buffer_get_size (inbuf));
+        "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT
+        " and offset NONE", inbuf, gst_buffer_get_size (inbuf),
+        GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)));
 
   /* Don't allow buffer handling before negotiation, except in passthrough mode
    * or if the class doesn't implement a set_caps function (in which case it doesn't
    * care about caps)
    */
-  if (!trans->negotiated && !trans->passthrough && (bclass->set_caps != NULL))
+  if (!priv->negotiated && !priv->passthrough && (bclass->set_caps != NULL))
     goto not_negotiated;
 
-  /* Set discont flag so we can mark the outgoing buffer */
-  if (GST_BUFFER_IS_DISCONT (inbuf)) {
-    GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", inbuf);
-    trans->priv->discont = TRUE;
-  }
-
   /* can only do QoS if the segment is in TIME */
   if (trans->segment.format != GST_FORMAT_TIME)
     goto no_qos;
@@ -1915,11 +2022,11 @@ no_reconfigure:
     /* lock for getting the QoS parameters that are set (in a different thread)
      * with the QOS events */
     GST_OBJECT_LOCK (trans);
-    earliest_time = trans->priv->earliest_time;
-    proportion = trans->priv->proportion;
+    earliest_time = priv->earliest_time;
+    proportion = priv->proportion;
     /* check for QoS, don't perform conversion for buffers
      * that are known to be late. */
-    need_skip = trans->priv->qos_enabled &&
+    need_skip = priv->qos_enabled &&
         earliest_time != -1 && running_time <= earliest_time;
     GST_OBJECT_UNLOCK (trans);
 
@@ -1933,7 +2040,7 @@ no_reconfigure:
           GST_TIME_FORMAT " <= %" GST_TIME_FORMAT,
           GST_TIME_ARGS (running_time), GST_TIME_ARGS (earliest_time));
 
-      trans->priv->dropped++;
+      priv->dropped++;
 
       duration = GST_BUFFER_DURATION (inbuf);
       stream_time =
@@ -1946,16 +2053,54 @@ no_reconfigure:
           stream_time, timestamp, duration);
       gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
       gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
-          trans->priv->processed, trans->priv->dropped);
+          priv->processed, priv->dropped);
       gst_element_post_message (GST_ELEMENT_CAST (trans), qos_msg);
 
       /* mark discont for next buffer */
-      trans->priv->discont = TRUE;
+      priv->discont = TRUE;
+      ret = GST_BASE_TRANSFORM_FLOW_DROPPED;
       goto skip;
     }
   }
 
 no_qos:
+  /* Stash input buffer where the default generate_output
+   * function can find it */
+  if (trans->queued_buf)
+    gst_buffer_unref (trans->queued_buf);
+  trans->queued_buf = inbuf;
+  return ret;
+skip:
+  gst_buffer_unref (inbuf);
+  return ret;
+
+not_negotiated:
+  {
+    gst_buffer_unref (inbuf);
+    if (GST_PAD_IS_FLUSHING (trans->srcpad))
+      return GST_FLOW_FLUSHING;
+    return GST_FLOW_NOT_NEGOTIATED;
+  }
+}
+
+static GstFlowReturn
+default_generate_output (GstBaseTransform * trans, GstBuffer ** outbuf)
+{
+  GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
+  GstBaseTransformPrivate *priv = trans->priv;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstBuffer *inbuf;
+  gboolean want_in_place;
+
+  /* Retrieve stashed input buffer, if the default submit_input_buffer
+   * was run. Takes ownership back from there */
+  inbuf = trans->queued_buf;
+  trans->queued_buf = NULL;
+
+  /* This default processing method needs one input buffer to feed to
+   * the transform functions, we can't do anything without it */
+  if (inbuf == NULL)
+    return GST_FLOW_OK;
 
   /* first try to allocate an output buffer based on the currently negotiated
    * format. outbuf will contain a buffer suitable for doing the configured
@@ -1973,18 +2118,18 @@ no_qos:
       *outbuf);
 
   /* now perform the needed transform */
-  if (trans->passthrough) {
+  if (priv->passthrough) {
     /* In passthrough mode, give transform_ip a look at the
      * buffer, without making it writable, or just push the
      * data through */
-    if (bclass->transform_ip) {
-      GST_DEBUG_OBJECT (trans, "doing passthrough transform");
+    if (bclass->transform_ip_on_passthrough && bclass->transform_ip) {
+      GST_DEBUG_OBJECT (trans, "doing passthrough transform_ip");
       ret = bclass->transform_ip (trans, *outbuf);
     } else {
       GST_DEBUG_OBJECT (trans, "element is in passthrough");
     }
   } else {
-    want_in_place = (bclass->transform_ip != NULL) && trans->always_in_place;
+    want_in_place = (bclass->transform_ip != NULL) && priv->always_in_place;
 
     if (want_in_place) {
       GST_DEBUG_OBJECT (trans, "doing inplace transform");
@@ -1999,7 +2144,6 @@ no_qos:
     }
   }
 
-skip:
   /* only unref input buffer if we allocated a new outbuf buffer. If we reused
    * the input buffer, no refcount is changed to keep the input buffer writable
    * when needed. */
@@ -2009,14 +2153,6 @@ skip:
   return ret;
 
   /* ERRORS */
-not_negotiated:
-  {
-    gst_buffer_unref (inbuf);
-    *outbuf = NULL;
-    GST_ELEMENT_ERROR (trans, STREAM, FORMAT,
-        ("not negotiated"), ("not negotiated"));
-    return GST_FLOW_NOT_NEGOTIATED;
-  }
 no_prepare:
   {
     gst_buffer_unref (inbuf);
@@ -2041,23 +2177,65 @@ static GstFlowReturn
 gst_base_transform_getrange (GstPad * pad, GstObject * parent, guint64 offset,
     guint length, GstBuffer ** buffer)
 {
-  GstBaseTransform *trans;
-  GstBaseTransformClass *klass;
+  GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (parent);
+  GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent);
+  GstBaseTransformPrivate *priv = trans->priv;
   GstFlowReturn ret;
-  GstBuffer *inbuf;
+  GstBuffer *inbuf = NULL;
+  GstBuffer *outbuf = NULL;
 
-  trans = GST_BASE_TRANSFORM (parent);
+  /* Try and generate a buffer, if the sub-class wants more data,
+   * pull some and repeat until a buffer (or error) is produced */
+  do {
+    ret = klass->generate_output (trans, &outbuf);
 
-  ret = gst_pad_pull_range (trans->sinkpad, offset, length, &inbuf);
-  if (G_UNLIKELY (ret != GST_FLOW_OK))
-    goto pull_error;
+    /* Consume the DROPPED return value and go get more data */
+    if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED)
+      ret = GST_FLOW_OK;
 
-  klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
-  if (klass->before_transform)
-    klass->before_transform (trans, inbuf);
+    if (ret != GST_FLOW_OK || outbuf != NULL)
+      break;
+
+    /* No buffer generated, try and pull data */
+    ret = gst_pad_pull_range (trans->sinkpad, offset, length, &inbuf);
+    if (G_UNLIKELY (ret != GST_FLOW_OK))
+      goto pull_error;
+
+    if (klass->before_transform)
+      klass->before_transform (trans, inbuf);
+
+    /* Set discont flag so we can mark the next outgoing buffer */
+    if (GST_BUFFER_IS_DISCONT (inbuf)) {
+      GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", inbuf);
+      priv->discont = TRUE;
+    }
 
-  ret = gst_base_transform_handle_buffer (trans, inbuf, buffer);
+    /* FIXME: Input offsets and lengths need to be translated, as per
+     * the FIXME above. For now, just advance somewhat */
+    offset += gst_buffer_get_size (inbuf);
 
+    ret = klass->submit_input_buffer (trans, priv->discont, inbuf);
+    if (ret != GST_FLOW_OK) {
+      if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED)
+        ret = GST_FLOW_OK;
+      goto done;
+    }
+  } while (ret == GST_FLOW_OK && outbuf == NULL);
+
+  *buffer = outbuf;
+  if (outbuf) {
+    /* apply DISCONT flag if the buffer is not yet marked as such */
+    if (priv->discont) {
+      GST_DEBUG_OBJECT (trans, "we have a pending DISCONT");
+      if (!GST_BUFFER_IS_DISCONT (outbuf)) {
+        GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer");
+        outbuf = gst_buffer_make_writable (outbuf);
+        GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+      }
+      priv->discont = FALSE;
+    }
+    priv->processed++;
+  }
 done:
   return ret;
 
@@ -2070,18 +2248,21 @@ pull_error:
   }
 }
 
+/* The flow of the chain function is the reverse of the
+ * getrange() function - we have data, feed it to the sub-class
+ * and then iterate, pushing buffers it generates until it either
+ * wants more data or returns an error */
 static GstFlowReturn
 gst_base_transform_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
 {
-  GstBaseTransform *trans;
-  GstBaseTransformClass *klass;
+  GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent);
+  GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
+  GstBaseTransformPrivate *priv = trans->priv;
   GstFlowReturn ret;
   GstClockTime position = GST_CLOCK_TIME_NONE;
   GstClockTime timestamp, duration;
   GstBuffer *outbuf = NULL;
 
-  trans = GST_BASE_TRANSFORM (parent);
-
   timestamp = GST_BUFFER_TIMESTAMP (buffer);
   duration = GST_BUFFER_DURATION (buffer);
 
@@ -2093,58 +2274,72 @@ gst_base_transform_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
       position = timestamp;
   }
 
-  klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
   if (klass->before_transform)
     klass->before_transform (trans, buffer);
 
-  /* protect transform method and concurrent buffer alloc */
-  ret = gst_base_transform_handle_buffer (trans, buffer, &outbuf);
-
-  /* outbuf can be NULL, this means a dropped buffer, if we have a buffer but
-   * GST_BASE_TRANSFORM_FLOW_DROPPED we will not push either. */
-  if (outbuf != NULL) {
-    if (ret == GST_FLOW_OK) {
-      GstClockTime position_out = GST_CLOCK_TIME_NONE;
-
-      /* Remember last stop position */
-      if (position != GST_CLOCK_TIME_NONE &&
-          trans->segment.format == GST_FORMAT_TIME)
-        trans->segment.position = position;
-
-      if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf)) {
-        position_out = GST_BUFFER_TIMESTAMP (outbuf);
-        if (GST_BUFFER_DURATION_IS_VALID (outbuf))
-          position_out += GST_BUFFER_DURATION (outbuf);
-      } else if (position != GST_CLOCK_TIME_NONE) {
-        position_out = position;
-      }
-      if (position_out != GST_CLOCK_TIME_NONE
-          && trans->segment.format == GST_FORMAT_TIME)
-        trans->priv->position_out = position_out;
-
-      /* apply DISCONT flag if the buffer is not yet marked as such */
-      if (trans->priv->discont) {
-        GST_DEBUG_OBJECT (trans, "we have a pending DISCONT");
-        if (!GST_BUFFER_IS_DISCONT (outbuf)) {
-          GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer");
-          outbuf = gst_buffer_make_writable (outbuf);
-          GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+  /* Set discont flag so we can mark the outgoing buffer */
+  if (GST_BUFFER_IS_DISCONT (buffer)) {
+    GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", buffer);
+    priv->discont = TRUE;
+  }
+
+  /* Takes ownership of input buffer */
+  ret = klass->submit_input_buffer (trans, priv->discont, buffer);
+  if (ret != GST_FLOW_OK)
+    goto done;
+
+  do {
+    outbuf = NULL;
+
+    ret = klass->generate_output (trans, &outbuf);
+
+    /* outbuf can be NULL, this means a dropped buffer, if we have a buffer but
+     * GST_BASE_TRANSFORM_FLOW_DROPPED we will not push either. */
+    if (outbuf != NULL) {
+      if (ret == GST_FLOW_OK) {
+        GstClockTime position_out = GST_CLOCK_TIME_NONE;
+
+        /* Remember last stop position */
+        if (position != GST_CLOCK_TIME_NONE &&
+            trans->segment.format == GST_FORMAT_TIME)
+          trans->segment.position = position;
+
+        if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf)) {
+          position_out = GST_BUFFER_TIMESTAMP (outbuf);
+          if (GST_BUFFER_DURATION_IS_VALID (outbuf))
+            position_out += GST_BUFFER_DURATION (outbuf);
+        } else if (position != GST_CLOCK_TIME_NONE) {
+          position_out = position;
         }
-        trans->priv->discont = FALSE;
-      }
-      trans->priv->processed++;
+        if (position_out != GST_CLOCK_TIME_NONE
+            && trans->segment.format == GST_FORMAT_TIME)
+          priv->position_out = position_out;
+
+        /* apply DISCONT flag if the buffer is not yet marked as such */
+        if (trans->priv->discont) {
+          GST_DEBUG_OBJECT (trans, "we have a pending DISCONT");
+          if (!GST_BUFFER_IS_DISCONT (outbuf)) {
+            GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer");
+            outbuf = gst_buffer_make_writable (outbuf);
+            GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+          }
+          priv->discont = FALSE;
+        }
+        priv->processed++;
 
-      ret = gst_pad_push (trans->srcpad, outbuf);
-    } else {
-      GST_DEBUG_OBJECT (trans, "we got return %s", gst_flow_get_name (ret));
-      gst_buffer_unref (outbuf);
+        ret = gst_pad_push (trans->srcpad, outbuf);
+      } else {
+        GST_DEBUG_OBJECT (trans, "we got return %s", gst_flow_get_name (ret));
+        gst_buffer_unref (outbuf);
+      }
     }
-  }
+  } while (ret == GST_FLOW_OK && outbuf != NULL);
 
+done:
   /* convert internal flow to OK and mark discont for the next buffer. */
   if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) {
     GST_DEBUG_OBJECT (trans, "dropped a buffer, marking DISCONT");
-    trans->priv->discont = TRUE;
+    priv->discont = TRUE;
     ret = GST_FLOW_OK;
   }
 
@@ -2157,7 +2352,7 @@ gst_base_transform_set_property (GObject * object, guint prop_id,
 {
   GstBaseTransform *trans;
 
-  trans = GST_BASE_TRANSFORM (object);
+  trans = GST_BASE_TRANSFORM_CAST (object);
 
   switch (prop_id) {
     case PROP_QOS:
@@ -2175,7 +2370,7 @@ gst_base_transform_get_property (GObject * object, guint prop_id,
 {
   GstBaseTransform *trans;
 
-  trans = GST_BASE_TRANSFORM (object);
+  trans = GST_BASE_TRANSFORM_CAST (object);
 
   switch (prop_id) {
     case PROP_QOS:
@@ -2192,6 +2387,7 @@ static gboolean
 gst_base_transform_activate (GstBaseTransform * trans, gboolean active)
 {
   GstBaseTransformClass *bclass;
+  GstBaseTransformPrivate *priv = trans->priv;
   gboolean result = TRUE;
 
   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
@@ -2199,7 +2395,7 @@ gst_base_transform_activate (GstBaseTransform * trans, gboolean active)
   if (active) {
     GstCaps *incaps, *outcaps;
 
-    if (trans->priv->pad_mode == GST_PAD_MODE_NONE && bclass->start)
+    if (priv->pad_mode == GST_PAD_MODE_NONE && bclass->start)
       result &= bclass->start (trans);
 
     incaps = gst_pad_get_current_caps (trans->sinkpad);
@@ -2207,20 +2403,20 @@ gst_base_transform_activate (GstBaseTransform * trans, gboolean active)
 
     GST_OBJECT_LOCK (trans);
     if (incaps && outcaps)
-      trans->have_same_caps =
-          gst_caps_is_equal (incaps, outcaps) || trans->passthrough;
+      priv->have_same_caps =
+          gst_caps_is_equal (incaps, outcaps) || priv->passthrough;
     else
-      trans->have_same_caps = trans->passthrough;
-    GST_DEBUG_OBJECT (trans, "have_same_caps %d", trans->have_same_caps);
-    trans->negotiated = FALSE;
+      priv->have_same_caps = priv->passthrough;
+    GST_DEBUG_OBJECT (trans, "have_same_caps %d", priv->have_same_caps);
+    priv->negotiated = FALSE;
     trans->have_segment = FALSE;
     gst_segment_init (&trans->segment, GST_FORMAT_UNDEFINED);
-    trans->priv->position_out = GST_CLOCK_TIME_NONE;
-    trans->priv->proportion = 1.0;
-    trans->priv->earliest_time = -1;
-    trans->priv->discont = FALSE;
-    trans->priv->processed = 0;
-    trans->priv->dropped = 0;
+    priv->position_out = GST_CLOCK_TIME_NONE;
+    priv->proportion = 1.0;
+    priv->earliest_time = -1;
+    priv->discont = FALSE;
+    priv->processed = 0;
+    priv->dropped = 0;
     GST_OBJECT_UNLOCK (trans);
 
     if (incaps)
@@ -2233,19 +2429,22 @@ gst_base_transform_activate (GstBaseTransform * trans, gboolean active)
     GST_PAD_STREAM_LOCK (trans->sinkpad);
     GST_PAD_STREAM_UNLOCK (trans->sinkpad);
 
-    trans->have_same_caps = FALSE;
-    /* We can only reset the passthrough mode if the instance told us to 
+    priv->have_same_caps = FALSE;
+    /* We can only reset the passthrough mode if the instance told us to
        handle it in configure_caps */
     if (bclass->passthrough_on_same_caps) {
       gst_base_transform_set_passthrough (trans, FALSE);
     }
-    gst_caps_replace (&trans->cache_caps1, NULL);
-    gst_caps_replace (&trans->cache_caps2, NULL);
+    gst_caps_replace (&priv->cache_caps1, NULL);
+    gst_caps_replace (&priv->cache_caps2, NULL);
+
+    /* Make sure any left over buffer is freed */
+    gst_buffer_replace (&trans->queued_buf, NULL);
 
-    if (trans->priv->pad_mode != GST_PAD_MODE_NONE && bclass->stop)
+    if (priv->pad_mode != GST_PAD_MODE_NONE && bclass->stop)
       result &= bclass->stop (trans);
 
-    gst_base_transform_set_allocation (trans, NULL, NULL, 0, 0, NULL);
+    gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL);
   }
 
   return result;
@@ -2258,7 +2457,7 @@ gst_base_transform_sink_activate_mode (GstPad * pad, GstObject * parent,
   gboolean result = FALSE;
   GstBaseTransform *trans;
 
-  trans = GST_BASE_TRANSFORM (parent);
+  trans = GST_BASE_TRANSFORM_CAST (parent);
 
   switch (mode) {
     case GST_PAD_MODE_PUSH:
@@ -2284,7 +2483,7 @@ gst_base_transform_src_activate_mode (GstPad * pad, GstObject * parent,
   gboolean result = FALSE;
   GstBaseTransform *trans;
 
-  trans = GST_BASE_TRANSFORM (parent);
+  trans = GST_BASE_TRANSFORM_CAST (parent);
 
   switch (mode) {
     case GST_PAD_MODE_PULL:
@@ -2315,7 +2514,7 @@ gst_base_transform_src_activate_mode (GstPad * pad, GstObject * parent,
  * Set passthrough mode for this filter by default. This is mostly
  * useful for filters that do not care about negotiation.
  *
- * Always TRUE for filters which don't implement either a transform
+ * Always %TRUE for filters which don't implement either a transform
  * or transform_ip method.
  *
  * MT safe.
@@ -2331,14 +2530,14 @@ gst_base_transform_set_passthrough (GstBaseTransform * trans,
   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
 
   GST_OBJECT_LOCK (trans);
-  if (passthrough == FALSE) {
+  if (!passthrough) {
     if (bclass->transform_ip || bclass->transform)
-      trans->passthrough = FALSE;
+      trans->priv->passthrough = FALSE;
   } else {
-    trans->passthrough = TRUE;
+    trans->priv->passthrough = TRUE;
   }
 
-  GST_DEBUG_OBJECT (trans, "set passthrough %d", trans->passthrough);
+  GST_DEBUG_OBJECT (trans, "set passthrough %d", trans->priv->passthrough);
   GST_OBJECT_UNLOCK (trans);
 }
 
@@ -2348,7 +2547,7 @@ gst_base_transform_set_passthrough (GstBaseTransform * trans,
  *
  * See if @trans is configured as a passthrough transform.
  *
- * Returns: TRUE is the transform is configured in passthrough mode.
+ * Returns: %TRUE is the transform is configured in passthrough mode.
  *
  * MT safe.
  */
@@ -2360,7 +2559,7 @@ gst_base_transform_is_passthrough (GstBaseTransform * trans)
   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
 
   GST_OBJECT_LOCK (trans);
-  result = trans->passthrough;
+  result = trans->priv->passthrough;
   GST_OBJECT_UNLOCK (trans);
 
   return result;
@@ -2374,10 +2573,9 @@ gst_base_transform_is_passthrough (GstBaseTransform * trans)
  *
  * Determines whether a non-writable buffer will be copied before passing
  * to the transform_ip function.
- * <itemizedlist>
- *   <listitem>Always TRUE if no transform function is implemented.</listitem>
- *   <listitem>Always FALSE if ONLY transform function is implemented.</listitem>
- * </itemizedlist>
+ *
+ *   * Always %TRUE if no transform function is implemented.
+ *   * Always %FALSE if ONLY transform function is implemented.
  *
  * MT safe.
  */
@@ -2395,12 +2593,12 @@ gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place)
   if (in_place) {
     if (bclass->transform_ip) {
       GST_DEBUG_OBJECT (trans, "setting in_place TRUE");
-      trans->always_in_place = TRUE;
+      trans->priv->always_in_place = TRUE;
     }
   } else {
     if (bclass->transform) {
       GST_DEBUG_OBJECT (trans, "setting in_place FALSE");
-      trans->always_in_place = FALSE;
+      trans->priv->always_in_place = FALSE;
     }
   }
 
@@ -2413,7 +2611,7 @@ gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place)
  *
  * See if @trans is configured as a in_place transform.
  *
- * Returns: TRUE is the transform is configured in in_place mode.
+ * Returns: %TRUE is the transform is configured in in_place mode.
  *
  * MT safe.
  */
@@ -2425,7 +2623,7 @@ gst_base_transform_is_in_place (GstBaseTransform * trans)
   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
 
   GST_OBJECT_LOCK (trans);
-  result = trans->always_in_place;
+  result = trans->priv->always_in_place;
   GST_OBJECT_UNLOCK (trans);
 
   return result;
@@ -2444,14 +2642,11 @@ gst_base_transform_is_in_place (GstBaseTransform * trans)
  * when needed.
  *
  * MT safe.
- *
- * Since: 0.10.5
  */
 void
 gst_base_transform_update_qos (GstBaseTransform * trans,
     gdouble proportion, GstClockTimeDiff diff, GstClockTime timestamp)
 {
-
   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans,
@@ -2472,8 +2667,6 @@ gst_base_transform_update_qos (GstBaseTransform * trans,
  * Enable or disable QoS handling in the transform.
  *
  * MT safe.
- *
- * Since: 0.10.5
  */
 void
 gst_base_transform_set_qos_enabled (GstBaseTransform * trans, gboolean enabled)
@@ -2493,11 +2686,9 @@ gst_base_transform_set_qos_enabled (GstBaseTransform * trans, gboolean enabled)
  *
  * Queries if the transform will handle QoS.
  *
- * Returns: TRUE if QoS is enabled.
+ * Returns: %TRUE if QoS is enabled.
  *
  * MT safe.
- *
- * Since: 0.10.5
  */
 gboolean
 gst_base_transform_is_qos_enabled (GstBaseTransform * trans)
@@ -2526,8 +2717,6 @@ gst_base_transform_is_qos_enabled (GstBaseTransform * trans)
  * unset the flag if the output is no neutral data.
  *
  * MT safe.
- *
- * Since: 0.10.16
  */
 void
 gst_base_transform_set_gap_aware (GstBaseTransform * trans, gboolean gap_aware)
@@ -2541,6 +2730,37 @@ gst_base_transform_set_gap_aware (GstBaseTransform * trans, gboolean gap_aware)
 }
 
 /**
+ * gst_base_transform_set_prefer_passthrough:
+ * @trans: a #GstBaseTransform
+ * @prefer_passthrough: New state
+ *
+ * If @prefer_passthrough is %TRUE (the default), @trans will check and
+ * prefer passthrough caps from the list of caps returned by the
+ * transform_caps vmethod.
+ *
+ * If set to %FALSE, the element must order the caps returned from the
+ * transform_caps function in such a way that the preferred format is
+ * first in the list. This can be interesting for transforms that can do
+ * passthrough transforms but prefer to do something else, like a
+ * capsfilter.
+ *
+ * MT safe.
+ *
+ * Since: 1.0.1
+ */
+void
+gst_base_transform_set_prefer_passthrough (GstBaseTransform * trans,
+    gboolean prefer_passthrough)
+{
+  g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
+
+  GST_OBJECT_LOCK (trans);
+  trans->priv->prefer_passthrough = prefer_passthrough;
+  GST_DEBUG_OBJECT (trans, "prefer passthrough %d", prefer_passthrough);
+  GST_OBJECT_UNLOCK (trans);
+}
+
+/**
  * gst_base_transform_reconfigure_sink:
  * @trans: a #GstBaseTransform
  *
@@ -2566,16 +2786,88 @@ gst_base_transform_reconfigure_sink (GstBaseTransform * trans)
  * Instructs @trans to renegotiate a new downstream transform on the next
  * buffer. This function is typically called after properties on the transform
  * were set that influence the output format.
- *
- * Since: 0.10.21
  */
 void
 gst_base_transform_reconfigure_src (GstBaseTransform * trans)
 {
   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
 
-  GST_OBJECT_LOCK (trans);
-  GST_DEBUG_OBJECT (trans, "marking reconfigure");
-  trans->priv->reconfigure = TRUE;
-  GST_OBJECT_UNLOCK (trans);
+  gst_pad_mark_reconfigure (trans->srcpad);
+}
+
+/**
+ * gst_base_transform_get_buffer_pool:
+ * @trans: a #GstBaseTransform
+ *
+ * Returns: (transfer full): the instance of the #GstBufferPool used
+ * by @trans; free it after use it
+ */
+GstBufferPool *
+gst_base_transform_get_buffer_pool (GstBaseTransform * trans)
+{
+  g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), NULL);
+
+  if (trans->priv->pool)
+    return gst_object_ref (trans->priv->pool);
+
+  return NULL;
+}
+
+/**
+ * gst_base_transform_get_allocator:
+ * @trans: a #GstBaseTransform
+ * @allocator: (out) (allow-none) (transfer full): the #GstAllocator
+ * used
+ * @params: (out) (allow-none) (transfer full): the
+ * #GstAllocationParams of @allocator
+ *
+ * Lets #GstBaseTransform sub-classes to know the memory @allocator
+ * used by the base class and its @params.
+ *
+ * Unref the @allocator after use it.
+ */
+void
+gst_base_transform_get_allocator (GstBaseTransform * trans,
+    GstAllocator ** allocator, GstAllocationParams * params)
+{
+  g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
+
+  if (allocator)
+    *allocator = trans->priv->allocator ?
+        gst_object_ref (trans->priv->allocator) : NULL;
+
+  if (params)
+    *params = trans->priv->params;
+}
+
+/**
+ * gst_base_transform_update_src_caps:
+ * @trans: a #GstBaseTransform
+ * @updated_caps: An updated version of the srcpad caps to be pushed
+ * downstream
+ *
+ * Updates the srcpad caps and send the caps downstream. This function
+ * can be used by subclasses when they have already negotiated their caps
+ * but found a change in them (or computed new information). This way,
+ * they can notify downstream about that change without losing any
+ * buffer.
+ *
+ * Returns: %TRUE if the caps could be send downstream %FALSE otherwise
+ *
+ * Since: 1.6
+ */
+gboolean
+gst_base_transform_update_src_caps (GstBaseTransform * trans,
+    GstCaps * updated_caps)
+{
+  g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
+
+  if (gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans),
+          gst_event_new_caps (updated_caps))) {
+    gst_pad_mark_reconfigure (trans->srcpad);
+
+    return TRUE;
+  }
+
+  return FALSE;
 }