pipeline: use reset_time message to reset the start time
[platform/upstream/gstreamer.git] / libs / gst / base / gstbasesink.c
index 338eeb5..7ddadb9 100644 (file)
  *
  * #GstBaseSink provides support for exactly one sink pad, which should be
  * named "sink". A sink implementation (subclass of #GstBaseSink) should
- * install a pad template in its base_init function, like so:
+ * install a pad template in its class_init function, like so:
  * |[
  * static void
- * my_element_base_init (gpointer g_class)
+ * my_element_class_init (GstMyElementClass *klass)
  * {
- *   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+ *   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
  *
  *   // sinktemplate should be a #GstStaticPadTemplate with direction
  *   // #GST_PAD_SINK and name "sink"
  * very specific elements (such as file sinks) which need to handle the
  * newsegment event specially.
  *
- * #GstBaseSink provides an overridable #GstBaseSinkClass.buffer_alloc()
- * function that can be used by sinks that want to do reverse negotiation or to
- * provide custom buffers (hardware buffers for example) to upstream elements.
- *
  * The #GstBaseSinkClass.unlock() method is called when the elements should
  * unblock any blocking operations they perform in the
  * #GstBaseSinkClass.render() method. This is mostly useful when the
 #  include "config.h"
 #endif
 
-/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
- * with newer GLib versions (>= 2.31.0) */
-#define GLIB_DISABLE_DEPRECATION_WARNINGS
 #include <gst/gst_private.h>
 
 #include "gstbasesink.h"
-#include <gst/gstmarshal.h>
 #include <gst/gst-i18n-lib.h>
 
 GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
@@ -181,9 +173,6 @@ typedef struct
   gboolean need_preroll;        /* if we need preroll after this step */
 } GstStepInfo;
 
-/* FIXME, some stuff in ABI.data and other in Private...
- * Make up your mind please.
- */
 struct _GstBaseSinkPrivate
 {
   gint qos_enabled;             /* ATOMIC */
@@ -244,11 +233,12 @@ struct _GstBaseSinkPrivate
   gboolean have_latency;
 
   /* the last buffer we prerolled or rendered. Useful for making snapshots */
-  gint enable_last_buffer;      /* atomic */
+  gint enable_last_sample;      /* atomic */
   GstBuffer *last_buffer;
+  GstCaps *last_caps;
 
-  /* caps for pull based scheduling */
-  GstCaps *pull_caps;
+  /* negotiated caps */
+  GstCaps *caps;
 
   /* blocksize for pulling */
   guint blocksize;
@@ -284,25 +274,11 @@ struct _GstBaseSinkPrivate
 #define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
 #define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
 
-enum
-{
-  _PR_IS_NOTHING = 1 << 0,
-  _PR_IS_BUFFER = 1 << 1,
-  _PR_IS_BUFFERLIST = 1 << 2,
-  _PR_IS_EVENT = 1 << 3
-} PrivateObjectType;
-
-#define OBJ_IS_BUFFER(a) ((a) & _PR_IS_BUFFER)
-#define OBJ_IS_BUFFERLIST(a) ((a) & _PR_IS_BUFFERLIST)
-#define OBJ_IS_EVENT(a) ((a) & _PR_IS_EVENT)
-#define OBJ_IS_BUFFERFULL(a) ((a) & (_PR_IS_BUFFER | _PR_IS_BUFFERLIST))
-
 /* BaseSink properties */
 
 #define DEFAULT_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */
 #define DEFAULT_CAN_ACTIVATE_PUSH TRUE
 
-#define DEFAULT_PREROLL_QUEUE_LEN   0
 #define DEFAULT_SYNC                TRUE
 #define DEFAULT_MAX_LATENESS        -1
 #define DEFAULT_QOS                 FALSE
@@ -310,20 +286,19 @@ enum
 #define DEFAULT_TS_OFFSET           0
 #define DEFAULT_BLOCKSIZE           4096
 #define DEFAULT_RENDER_DELAY        0
-#define DEFAULT_ENABLE_LAST_BUFFER  TRUE
+#define DEFAULT_ENABLE_LAST_SAMPLE  TRUE
 #define DEFAULT_THROTTLE_TIME       0
 
 enum
 {
   PROP_0,
-  PROP_PREROLL_QUEUE_LEN,
   PROP_SYNC,
   PROP_MAX_LATENESS,
   PROP_QOS,
   PROP_ASYNC,
   PROP_TS_OFFSET,
-  PROP_ENABLE_LAST_BUFFER,
-  PROP_LAST_BUFFER,
+  PROP_ENABLE_LAST_SAMPLE,
+  PROP_LAST_SAMPLE,
   PROP_BLOCKSIZE,
   PROP_RENDER_DELAY,
   PROP_THROTTLE_TIME,
@@ -370,14 +345,13 @@ static void gst_base_sink_get_property (GObject * object, guint prop_id,
 static gboolean gst_base_sink_send_event (GstElement * element,
     GstEvent * event);
 static gboolean default_element_query (GstElement * element, GstQuery * query);
-static const GstQueryType *gst_base_sink_get_query_types (GstElement * element);
-
-static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
-static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
-static GstFlowReturn gst_base_sink_buffer_alloc (GstBaseSink * sink,
-    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
-static void gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
-    GstClockTime * start, GstClockTime * end);
+
+static GstCaps *gst_base_sink_default_get_caps (GstBaseSink * sink,
+    GstCaps * caps);
+static gboolean gst_base_sink_default_set_caps (GstBaseSink * sink,
+    GstCaps * caps);
+static void gst_base_sink_default_get_times (GstBaseSink * basesink,
+    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
 static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink,
     GstPad * pad, gboolean flushing);
 static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
@@ -390,33 +364,36 @@ static gboolean gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
 static GstStateChangeReturn gst_base_sink_change_state (GstElement * element,
     GstStateChange transition);
 
-static gboolean gst_base_sink_sink_query (GstPad * pad, GstQuery * query);
-static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstBuffer * buffer);
-static GstFlowReturn gst_base_sink_chain_list (GstPad * pad,
+static gboolean gst_base_sink_sink_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
+static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstObject * parent,
+    GstBuffer * buffer);
+static GstFlowReturn gst_base_sink_chain_list (GstPad * pad, GstObject * parent,
     GstBufferList * list);
 
 static void gst_base_sink_loop (GstPad * pad);
-static gboolean gst_base_sink_pad_activate (GstPad * pad);
-static gboolean gst_base_sink_pad_activate_push (GstPad * pad, gboolean active);
-static gboolean gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active);
-static gboolean gst_base_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_base_sink_pad_activate (GstPad * pad, GstObject * parent);
+static gboolean gst_base_sink_pad_activate_mode (GstPad * pad,
+    GstObject * parent, GstPadMode mode, gboolean active);
+static gboolean gst_base_sink_default_event (GstBaseSink * basesink,
+    GstEvent * event);
+static GstFlowReturn gst_base_sink_default_wait_eos (GstBaseSink * basesink,
+    GstEvent * event);
+static gboolean gst_base_sink_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
 
-static gboolean default_sink_query (GstBaseSink * sink, GstQuery * query);
+static gboolean gst_base_sink_default_query (GstBaseSink * sink,
+    GstQuery * query);
 
 static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink);
-static GstCaps *gst_base_sink_pad_getcaps (GstPad * pad);
-static gboolean gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps);
-static void gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps);
-static GstFlowReturn gst_base_sink_pad_buffer_alloc (GstPad * pad,
-    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
-
+static GstCaps *gst_base_sink_default_fixate (GstBaseSink * bsink,
+    GstCaps * caps);
+static GstCaps *gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
 
 /* check if an object was too late */
 static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink,
     GstMiniObject * obj, GstClockTime rstart, GstClockTime rstop,
     GstClockReturn status, GstClockTimeDiff jitter);
-static GstFlowReturn gst_base_sink_preroll_object (GstBaseSink * basesink,
-    guint8 obj_type, GstMiniObject * obj);
 
 static void
 gst_base_sink_class_init (GstBaseSinkClass * klass)
@@ -438,14 +415,6 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
   gobject_class->set_property = gst_base_sink_set_property;
   gobject_class->get_property = gst_base_sink_get_property;
 
-  /* FIXME, this next value should be configured using an event from the
-   * upstream element, ie, the BUFFER_SIZE event. */
-  g_object_class_install_property (gobject_class, PROP_PREROLL_QUEUE_LEN,
-      g_param_spec_uint ("preroll-queue-len", "Preroll queue length",
-          "Number of buffers to queue during preroll", 0, G_MAXUINT,
-          DEFAULT_PREROLL_QUEUE_LEN,
-          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
-
   g_object_class_install_property (gobject_class, PROP_SYNC,
       g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
@@ -489,22 +458,22 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
           DEFAULT_TS_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
-   * GstBaseSink:enable-last-buffer
+   * GstBaseSink:enable-last-sample
    *
-   * Enable the last-buffer property. If FALSE, basesink doesn't keep a
-   * reference to the last buffer arrived and the last-buffer property is always
+   * Enable the last-sample property. If FALSE, basesink doesn't keep a
+   * reference to the last buffer arrived and the last-sample property is always
    * set to NULL. This can be useful if you need buffers to be released as soon
    * as possible, eg. if you're using a buffer pool.
    *
    * Since: 0.10.30
    */
-  g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_BUFFER,
-      g_param_spec_boolean ("enable-last-buffer", "Enable Last Buffer",
-          "Enable the last-buffer property", DEFAULT_ENABLE_LAST_BUFFER,
+  g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_SAMPLE,
+      g_param_spec_boolean ("enable-last-sample", "Enable Last Buffer",
+          "Enable the last-sample property", DEFAULT_ENABLE_LAST_SAMPLE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
-   * GstBaseSink:last-buffer
+   * GstBaseSink:last-sample
    *
    * The last buffer that arrived in the sink and was used for preroll or for
    * rendering. This property can be used to generate thumbnails. This property
@@ -512,9 +481,9 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    *
    * Since: 0.10.15
    */
-  g_object_class_install_property (gobject_class, PROP_LAST_BUFFER,
-      gst_param_spec_mini_object ("last-buffer", "Last Buffer",
-          "The last buffer received in the sink", GST_TYPE_BUFFER,
+  g_object_class_install_property (gobject_class, PROP_LAST_SAMPLE,
+      g_param_spec_boxed ("last-sample", "Last Sample",
+          "The last sample received in the sink", GST_TYPE_SAMPLE,
           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
   /**
    * GstBaseSink:blocksize
@@ -552,32 +521,28 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    */
   g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME,
       g_param_spec_uint64 ("throttle-time", "Throttle time",
-          "The time to keep between rendered buffers (unused)", 0, G_MAXUINT64,
+          "The time to keep between rendered buffers", 0, G_MAXUINT64,
           DEFAULT_THROTTLE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
   gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
   gstelement_class->query = GST_DEBUG_FUNCPTR (default_element_query);
-  gstelement_class->get_query_types =
-      GST_DEBUG_FUNCPTR (gst_base_sink_get_query_types);
 
-  klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
-  klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
-  klass->buffer_alloc = GST_DEBUG_FUNCPTR (gst_base_sink_buffer_alloc);
-  klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times);
+  klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_caps);
+  klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_set_caps);
+  klass->fixate = GST_DEBUG_FUNCPTR (gst_base_sink_default_fixate);
   klass->activate_pull =
       GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull);
-  klass->query = GST_DEBUG_FUNCPTR (default_sink_query);
+  klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_times);
+  klass->query = GST_DEBUG_FUNCPTR (gst_base_sink_default_query);
+  klass->event = GST_DEBUG_FUNCPTR (gst_base_sink_default_event);
+  klass->wait_eos = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_eos);
 
   /* Registering debug symbols for function pointers */
-  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_getcaps);
-  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_setcaps);
-  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_fixate);
-  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_buffer_alloc);
+  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_fixate);
   GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate);
-  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_push);
-  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_pull);
+  GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_mode);
   GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_event);
   GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain);
   GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain_list);
@@ -585,25 +550,23 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
 }
 
 static GstCaps *
-gst_base_sink_pad_getcaps (GstPad * pad)
+gst_base_sink_query_caps (GstBaseSink * bsink, GstPad * pad, GstCaps * filter)
 {
   GstBaseSinkClass *bclass;
-  GstBaseSink *bsink;
   GstCaps *caps = NULL;
+  gboolean fixed;
 
-  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
   bclass = GST_BASE_SINK_GET_CLASS (bsink);
+  fixed = GST_PAD_IS_FIXED_CAPS (pad);
 
-  if (bsink->pad_mode == GST_ACTIVATE_PULL) {
-    /* if we are operating in pull mode we only accept the negotiated caps */
-    GST_OBJECT_LOCK (pad);
-    if ((caps = GST_PAD_CAPS (pad)))
-      gst_caps_ref (caps);
-    GST_OBJECT_UNLOCK (pad);
+  if (fixed || bsink->pad_mode == GST_PAD_MODE_PULL) {
+    /* if we are operating in pull mode or fixed caps, we only accept the
+     * currently negotiated caps */
+    caps = gst_pad_get_current_caps (pad);
   }
   if (caps == NULL) {
     if (bclass->get_caps)
-      caps = bclass->get_caps (bsink);
+      caps = bclass->get_caps (bsink, filter);
 
     if (caps == NULL) {
       GstPadTemplate *pad_template;
@@ -612,69 +575,41 @@ gst_base_sink_pad_getcaps (GstPad * pad)
           gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass),
           "sink");
       if (pad_template != NULL) {
-        caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
+        caps = gst_pad_template_get_caps (pad_template);
+
+        if (filter) {
+          GstCaps *intersection;
+
+          intersection =
+              gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
+          gst_caps_unref (caps);
+          caps = intersection;
+        }
       }
     }
   }
-  gst_object_unref (bsink);
 
   return caps;
 }
 
-static gboolean
-gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps)
+static GstCaps *
+gst_base_sink_default_fixate (GstBaseSink * bsink, GstCaps * caps)
 {
-  GstBaseSinkClass *bclass;
-  GstBaseSink *bsink;
-  gboolean res = TRUE;
-
-  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
-  bclass = GST_BASE_SINK_GET_CLASS (bsink);
-
-  if (res && bclass->set_caps)
-    res = bclass->set_caps (bsink, caps);
-
-  gst_object_unref (bsink);
-
-  return res;
+  GST_DEBUG_OBJECT (bsink, "using default caps fixate function");
+  return gst_caps_fixate (caps);
 }
 
-static void
-gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps)
+static GstCaps *
+gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
 {
   GstBaseSinkClass *bclass;
-  GstBaseSink *bsink;
 
-  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
   bclass = GST_BASE_SINK_GET_CLASS (bsink);
 
   if (bclass->fixate)
-    bclass->fixate (bsink, caps);
-
-  gst_object_unref (bsink);
-}
+    caps = bclass->fixate (bsink, caps);
 
-static GstFlowReturn
-gst_base_sink_pad_buffer_alloc (GstPad * pad, guint64 offset, guint size,
-    GstCaps * caps, GstBuffer ** buf)
-{
-  GstBaseSinkClass *bclass;
-  GstBaseSink *bsink;
-  GstFlowReturn result = GST_FLOW_OK;
-
-  bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
-  if (G_UNLIKELY (bsink == NULL))
-    return GST_FLOW_WRONG_STATE;
-  bclass = GST_BASE_SINK_GET_CLASS (bsink);
-
-  if (bclass->buffer_alloc)
-    result = bclass->buffer_alloc (bsink, offset, size, caps, buf);
-  else
-    *buf = NULL;                /* fallback in gstpad.c will allocate generic buffer */
-
-  gst_object_unref (bsink);
-
-  return result;
+  return caps;
 }
 
 static void
@@ -691,42 +626,35 @@ gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
 
   basesink->sinkpad = gst_pad_new_from_template (pad_template, "sink");
 
-  gst_pad_set_getcaps_function (basesink->sinkpad, gst_base_sink_pad_getcaps);
-  gst_pad_set_setcaps_function (basesink->sinkpad, gst_base_sink_pad_setcaps);
-  gst_pad_set_fixatecaps_function (basesink->sinkpad, gst_base_sink_pad_fixate);
-  gst_pad_set_bufferalloc_function (basesink->sinkpad,
-      gst_base_sink_pad_buffer_alloc);
   gst_pad_set_activate_function (basesink->sinkpad, gst_base_sink_pad_activate);
-  gst_pad_set_activatepush_function (basesink->sinkpad,
-      gst_base_sink_pad_activate_push);
-  gst_pad_set_activatepull_function (basesink->sinkpad,
-      gst_base_sink_pad_activate_pull);
+  gst_pad_set_activatemode_function (basesink->sinkpad,
+      gst_base_sink_pad_activate_mode);
   gst_pad_set_query_function (basesink->sinkpad, gst_base_sink_sink_query);
   gst_pad_set_event_function (basesink->sinkpad, gst_base_sink_event);
   gst_pad_set_chain_function (basesink->sinkpad, gst_base_sink_chain);
   gst_pad_set_chain_list_function (basesink->sinkpad, gst_base_sink_chain_list);
   gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad);
 
-  basesink->pad_mode = GST_ACTIVATE_NONE;
-  basesink->preroll_queue = g_queue_new ();
-  basesink->abidata.ABI.clip_segment = gst_segment_new ();
+  basesink->pad_mode = GST_PAD_MODE_NONE;
+  g_mutex_init (&basesink->preroll_lock);
+  g_cond_init (&basesink->preroll_cond);
   priv->have_latency = FALSE;
 
   basesink->can_activate_push = DEFAULT_CAN_ACTIVATE_PUSH;
   basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL;
 
   basesink->sync = DEFAULT_SYNC;
-  basesink->abidata.ABI.max_lateness = DEFAULT_MAX_LATENESS;
+  basesink->max_lateness = DEFAULT_MAX_LATENESS;
   g_atomic_int_set (&priv->qos_enabled, DEFAULT_QOS);
   priv->async_enabled = DEFAULT_ASYNC;
   priv->ts_offset = DEFAULT_TS_OFFSET;
   priv->render_delay = DEFAULT_RENDER_DELAY;
   priv->blocksize = DEFAULT_BLOCKSIZE;
   priv->cached_clock_id = NULL;
-  g_atomic_int_set (&priv->enable_last_buffer, DEFAULT_ENABLE_LAST_BUFFER);
+  g_atomic_int_set (&priv->enable_last_sample, DEFAULT_ENABLE_LAST_SAMPLE);
   priv->throttle_time = DEFAULT_THROTTLE_TIME;
 
-  GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_IS_SINK);
+  GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_SINK);
 }
 
 static void
@@ -736,8 +664,8 @@ gst_base_sink_finalize (GObject * object)
 
   basesink = GST_BASE_SINK (object);
 
-  g_queue_free (basesink->preroll_queue);
-  gst_segment_free (basesink->abidata.ABI.clip_segment);
+  g_mutex_clear (&basesink->preroll_lock);
+  g_cond_clear (&basesink->preroll_cond);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -808,7 +736,7 @@ gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
   g_return_if_fail (GST_IS_BASE_SINK (sink));
 
   GST_OBJECT_LOCK (sink);
-  sink->abidata.ABI.max_lateness = max_lateness;
+  sink->max_lateness = max_lateness;
   GST_OBJECT_UNLOCK (sink);
 }
 
@@ -833,7 +761,7 @@ gst_base_sink_get_max_lateness (GstBaseSink * sink)
   g_return_val_if_fail (GST_IS_BASE_SINK (sink), -1);
 
   GST_OBJECT_LOCK (sink);
-  res = sink->abidata.ABI.max_lateness;
+  res = sink->max_lateness;
   GST_OBJECT_UNLOCK (sink);
 
   return res;
@@ -896,10 +824,10 @@ gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled)
 {
   g_return_if_fail (GST_IS_BASE_SINK (sink));
 
-  GST_PAD_PREROLL_LOCK (sink->sinkpad);
+  GST_BASE_SINK_PREROLL_LOCK (sink);
   g_atomic_int_set (&sink->priv->async_enabled, enabled);
   GST_LOG_OBJECT (sink, "set async enabled to %d", enabled);
-  GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
+  GST_BASE_SINK_PREROLL_UNLOCK (sink);
 }
 
 /**
@@ -974,32 +902,34 @@ gst_base_sink_get_ts_offset (GstBaseSink * sink)
 }
 
 /**
- * gst_base_sink_get_last_buffer:
+ * gst_base_sink_get_last_sample:
  * @sink: the sink
  *
- * Get the last buffer that arrived in the sink and was used for preroll or for
+ * Get the last sample that arrived in the sink and was used for preroll or for
  * rendering. This property can be used to generate thumbnails.
  *
- * The #GstCaps on the buffer can be used to determine the type of the buffer.
+ * The #GstCaps on the sample can be used to determine the type of the buffer.
  *
- * Free-function: gst_buffer_unref
+ * Free-function: gst_sample_unref
  *
- * Returns: (transfer full): a #GstBuffer. gst_buffer_unref() after usage.
+ * Returns: (transfer full): a #GstSample. gst_sample_unref() after usage.
  *     This function returns NULL when no buffer has arrived in the sink yet
  *     or when the sink is not in PAUSED or PLAYING.
  *
  * Since: 0.10.15
  */
-GstBuffer *
-gst_base_sink_get_last_buffer (GstBaseSink * sink)
+GstSample *
+gst_base_sink_get_last_sample (GstBaseSink * sink)
 {
-  GstBuffer *res;
+  GstSample *res = NULL;
 
   g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL);
 
   GST_OBJECT_LOCK (sink);
-  if ((res = sink->priv->last_buffer))
-    gst_buffer_ref (res);
+  if (sink->priv->last_buffer) {
+    res = gst_sample_new (sink->priv->last_buffer,
+        sink->priv->last_caps, &sink->segment, NULL);
+  }
   GST_OBJECT_UNLOCK (sink);
 
   return res;
@@ -1017,6 +947,11 @@ gst_base_sink_set_last_buffer_unlocked (GstBaseSink * sink, GstBuffer * buffer)
     if (G_LIKELY (buffer))
       gst_buffer_ref (buffer);
     sink->priv->last_buffer = buffer;
+    if (buffer)
+      /* copy over the caps */
+      gst_caps_replace (&sink->priv->last_caps, sink->priv->caps);
+    else
+      gst_caps_replace (&sink->priv->last_caps, NULL);
   } else {
     old = NULL;
   }
@@ -1032,7 +967,7 @@ gst_base_sink_set_last_buffer_unlocked (GstBaseSink * sink, GstBuffer * buffer)
 static void
 gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
 {
-  if (!g_atomic_int_get (&sink->priv->enable_last_buffer))
+  if (!g_atomic_int_get (&sink->priv->enable_last_sample))
     return;
 
   GST_OBJECT_LOCK (sink);
@@ -1041,22 +976,22 @@ gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
 }
 
 /**
- * gst_base_sink_set_last_buffer_enabled:
+ * gst_base_sink_set_last_sample_enabled:
  * @sink: the sink
- * @enabled: the new enable-last-buffer value.
+ * @enabled: the new enable-last-sample value.
  *
- * Configures @sink to store the last received buffer in the last-buffer
+ * Configures @sink to store the last received sample in the last-sample
  * property.
  *
  * Since: 0.10.30
  */
 void
-gst_base_sink_set_last_buffer_enabled (GstBaseSink * sink, gboolean enabled)
+gst_base_sink_set_last_sample_enabled (GstBaseSink * sink, gboolean enabled)
 {
   g_return_if_fail (GST_IS_BASE_SINK (sink));
 
   /* Only take lock if we change the value */
-  if (g_atomic_int_compare_and_exchange (&sink->priv->enable_last_buffer,
+  if (g_atomic_int_compare_and_exchange (&sink->priv->enable_last_sample,
           !enabled, enabled) && !enabled) {
     GST_OBJECT_LOCK (sink);
     gst_base_sink_set_last_buffer_unlocked (sink, NULL);
@@ -1065,22 +1000,22 @@ gst_base_sink_set_last_buffer_enabled (GstBaseSink * sink, gboolean enabled)
 }
 
 /**
- * gst_base_sink_is_last_buffer_enabled:
+ * gst_base_sink_is_last_sample_enabled:
  * @sink: the sink
  *
- * Checks if @sink is currently configured to store the last received buffer in
- * the last-buffer property.
+ * Checks if @sink is currently configured to store the last received sample in
+ * the last-sample property.
  *
- * Returns: TRUE if the sink is configured to store the last received buffer.
+ * Returns: TRUE if the sink is configured to store the last received sample.
  *
  * Since: 0.10.30
  */
 gboolean
-gst_base_sink_is_last_buffer_enabled (GstBaseSink * sink)
+gst_base_sink_is_last_sample_enabled (GstBaseSink * sink)
 {
   g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
 
-  return g_atomic_int_get (&sink->priv->enable_last_buffer);
+  return g_atomic_int_get (&sink->priv->enable_last_sample);
 }
 
 /**
@@ -1373,12 +1308,6 @@ gst_base_sink_set_property (GObject * object, guint prop_id,
   GstBaseSink *sink = GST_BASE_SINK (object);
 
   switch (prop_id) {
-    case PROP_PREROLL_QUEUE_LEN:
-      /* preroll lock necessary to serialize with finish_preroll */
-      GST_PAD_PREROLL_LOCK (sink->sinkpad);
-      g_atomic_int_set (&sink->preroll_queue_max_len, g_value_get_uint (value));
-      GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
-      break;
     case PROP_SYNC:
       gst_base_sink_set_sync (sink, g_value_get_boolean (value));
       break;
@@ -1400,8 +1329,8 @@ gst_base_sink_set_property (GObject * object, guint prop_id,
     case PROP_RENDER_DELAY:
       gst_base_sink_set_render_delay (sink, g_value_get_uint64 (value));
       break;
-    case PROP_ENABLE_LAST_BUFFER:
-      gst_base_sink_set_last_buffer_enabled (sink, g_value_get_boolean (value));
+    case PROP_ENABLE_LAST_SAMPLE:
+      gst_base_sink_set_last_sample_enabled (sink, g_value_get_boolean (value));
       break;
     case PROP_THROTTLE_TIME:
       gst_base_sink_set_throttle_time (sink, g_value_get_uint64 (value));
@@ -1419,9 +1348,6 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
   GstBaseSink *sink = GST_BASE_SINK (object);
 
   switch (prop_id) {
-    case PROP_PREROLL_QUEUE_LEN:
-      g_value_set_uint (value, g_atomic_int_get (&sink->preroll_queue_max_len));
-      break;
     case PROP_SYNC:
       g_value_set_boolean (value, gst_base_sink_get_sync (sink));
       break;
@@ -1437,11 +1363,11 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_TS_OFFSET:
       g_value_set_int64 (value, gst_base_sink_get_ts_offset (sink));
       break;
-    case PROP_LAST_BUFFER:
-      gst_value_take_buffer (value, gst_base_sink_get_last_buffer (sink));
+    case PROP_LAST_SAMPLE:
+      gst_value_take_buffer (value, gst_base_sink_get_last_sample (sink));
       break;
-    case PROP_ENABLE_LAST_BUFFER:
-      g_value_set_boolean (value, gst_base_sink_is_last_buffer_enabled (sink));
+    case PROP_ENABLE_LAST_SAMPLE:
+      g_value_set_boolean (value, gst_base_sink_is_last_sample_enabled (sink));
       break;
     case PROP_BLOCKSIZE:
       g_value_set_uint (value, gst_base_sink_get_blocksize (sink));
@@ -1460,100 +1386,17 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
 
 
 static GstCaps *
-gst_base_sink_get_caps (GstBaseSink * sink)
+gst_base_sink_default_get_caps (GstBaseSink * sink, GstCaps * filter)
 {
   return NULL;
 }
 
 static gboolean
-gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
+gst_base_sink_default_set_caps (GstBaseSink * sink, GstCaps * caps)
 {
   return TRUE;
 }
 
-static GstFlowReturn
-gst_base_sink_buffer_alloc (GstBaseSink * sink, guint64 offset, guint size,
-    GstCaps * caps, GstBuffer ** buf)
-{
-  *buf = NULL;
-  return GST_FLOW_OK;
-}
-
-/* with PREROLL_LOCK, STREAM_LOCK */
-static void
-gst_base_sink_preroll_queue_flush (GstBaseSink * basesink, GstPad * pad)
-{
-  GstMiniObject *obj;
-
-  GST_DEBUG_OBJECT (basesink, "flushing queue %p", basesink);
-  while ((obj = g_queue_pop_head (basesink->preroll_queue))) {
-    GST_DEBUG_OBJECT (basesink, "popped %p", obj);
-    gst_mini_object_unref (obj);
-  }
-  /* we can't have EOS anymore now */
-  basesink->eos = FALSE;
-  basesink->priv->received_eos = FALSE;
-  basesink->have_preroll = FALSE;
-  basesink->priv->step_unlock = FALSE;
-  basesink->eos_queued = FALSE;
-  basesink->preroll_queued = 0;
-  basesink->buffers_queued = 0;
-  basesink->events_queued = 0;
-  /* can't report latency anymore until we preroll again */
-  if (basesink->priv->async_enabled) {
-    GST_OBJECT_LOCK (basesink);
-    basesink->priv->have_latency = FALSE;
-    GST_OBJECT_UNLOCK (basesink);
-  }
-  /* and signal any waiters now */
-  GST_PAD_PREROLL_SIGNAL (pad);
-}
-
-/* with STREAM_LOCK, configures given segment with the event information. */
-static void
-gst_base_sink_configure_segment (GstBaseSink * basesink, GstPad * pad,
-    GstEvent * event, GstSegment * segment)
-{
-  gboolean update;
-  gdouble rate, arate;
-  GstFormat format;
-  gint64 start;
-  gint64 stop;
-  gint64 time;
-
-  /* the newsegment event is needed to bring the buffer timestamps to the
-   * stream time and to drop samples outside of the playback segment. */
-  gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
-      &start, &stop, &time);
-
-  /* The segment is protected with both the STREAM_LOCK and the OBJECT_LOCK.
-   * We protect with the OBJECT_LOCK so that we can use the values to
-   * safely answer a POSITION query. */
-  GST_OBJECT_LOCK (basesink);
-  gst_segment_set_newsegment_full (segment, update, rate, arate, format, start,
-      stop, time);
-
-  if (format == GST_FORMAT_TIME) {
-    GST_DEBUG_OBJECT (basesink,
-        "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
-        "format GST_FORMAT_TIME, "
-        "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
-        ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
-        update, rate, arate, GST_TIME_ARGS (segment->start),
-        GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
-        GST_TIME_ARGS (segment->accum));
-  } else {
-    GST_DEBUG_OBJECT (basesink,
-        "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
-        "format %d, "
-        "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
-        G_GINT64_FORMAT ", accum %" G_GINT64_FORMAT, update, rate, arate,
-        segment->format, segment->start, segment->stop, segment->time,
-        segment->accum);
-  }
-  GST_OBJECT_UNLOCK (basesink);
-}
-
 /* with PREROLL_LOCK, STREAM_LOCK */
 static gboolean
 gst_base_sink_commit_state (GstBaseSink * basesink)
@@ -1576,11 +1419,6 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
   switch (pending) {
     case GST_STATE_PLAYING:
     {
-      GstBaseSinkClass *bclass;
-      GstStateChangeReturn ret;
-
-      bclass = GST_BASE_SINK_GET_CLASS (basesink);
-
       GST_DEBUG_OBJECT (basesink, "commiting state to PLAYING");
 
       basesink->need_preroll = FALSE;
@@ -1591,14 +1429,6 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
       if (current == GST_STATE_READY) {
         post_paused = TRUE;
       }
-
-      /* make sure we notify the subclass of async playing */
-      if (bclass->async_play) {
-        GST_WARNING_OBJECT (basesink, "deprecated async_play");
-        ret = bclass->async_play (basesink);
-        if (ret == GST_STATE_CHANGE_FAILURE)
-          goto async_failed;
-      }
       break;
     }
     case GST_STATE_PAUSED:
@@ -1635,7 +1465,7 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
   if (post_async_done) {
     GST_DEBUG_OBJECT (basesink, "posting async-done message");
     gst_element_post_message (GST_ELEMENT_CAST (basesink),
-        gst_message_new_async_done (GST_OBJECT_CAST (basesink)));
+        gst_message_new_async_done (GST_OBJECT_CAST (basesink), FALSE));
   }
   if (post_playing) {
     GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
@@ -1683,13 +1513,6 @@ stopping:
     GST_OBJECT_UNLOCK (basesink);
     return FALSE;
   }
-async_failed:
-  {
-    GST_DEBUG_OBJECT (basesink, "async commit failed");
-    GST_STATE_RETURN (basesink) = GST_STATE_CHANGE_FAILURE;
-    GST_OBJECT_UNLOCK (basesink);
-    return FALSE;
-  }
 }
 
 static void
@@ -1720,7 +1543,6 @@ start_stepping (GstBaseSink * sink, GstSegment * segment,
   /* set the new rate for the remainder of the segment */
   current->start_rate = segment->rate;
   segment->rate *= current->rate;
-  segment->abs_rate = ABS (segment->rate);
 
   /* save values */
   if (segment->rate > 0.0)
@@ -1734,27 +1556,19 @@ start_stepping (GstBaseSink * sink, GstSegment * segment,
       /* update the segment clipping regions for non-flushing seeks */
       if (segment->rate > 0.0) {
         segment->stop = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
-        segment->last_stop = segment->stop;
+        segment->position = segment->stop;
       } else {
         gint64 position;
 
         position = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
         segment->time = position;
         segment->start = position;
-        segment->last_stop = position;
+        segment->position = position;
       }
     }
   }
 
-  GST_DEBUG_OBJECT (sink,
-      "segment now rate %lf, applied rate %lf, "
-      "format GST_FORMAT_TIME, "
-      "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
-      ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
-      segment->rate, segment->applied_rate, GST_TIME_ARGS (segment->start),
-      GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
-      GST_TIME_ARGS (segment->accum));
-
+  GST_DEBUG_OBJECT (sink, "segment now %" GST_SEGMENT_FORMAT, segment);
   GST_DEBUG_OBJECT (sink, "step started at running_time %" GST_TIME_FORMAT,
       GST_TIME_ARGS (current->start));
 
@@ -1799,8 +1613,8 @@ stop_stepping (GstBaseSink * sink, GstSegment * segment,
   gst_segment_set_running_time (segment, GST_FORMAT_TIME, position);
 
   if (current->flush) {
-    /* and remove the accumulated time we flushed, start time did not change */
-    segment->accum = current->start;
+    /* and remove the time we flushed, start time did not change */
+    segment->base = current->start;
   } else {
     /* start time is now the stepped position */
     gst_element_set_start_time (GST_ELEMENT_CAST (sink), position);
@@ -1808,16 +1622,12 @@ stop_stepping (GstBaseSink * sink, GstSegment * segment,
 
   /* restore the previous rate */
   segment->rate = current->start_rate;
-  segment->abs_rate = ABS (segment->rate);
 
   if (segment->rate > 0.0)
     segment->stop = current->start_stop;
   else
     segment->start = current->start_start;
 
-  /* the clip segment is used for position report in paused... */
-  memcpy (sink->abidata.ABI.clip_segment, segment, sizeof (GstSegment));
-
   /* post the step done when we know the stepped duration in TIME */
   message =
       gst_message_new_step_done (GST_OBJECT_CAST (sink), current->format,
@@ -1835,8 +1645,8 @@ stop_stepping (GstBaseSink * sink, GstSegment * segment,
 
 static gboolean
 handle_stepping (GstBaseSink * sink, GstSegment * segment,
-    GstStepInfo * current, gint64 * cstart, gint64 * cstop, gint64 * rstart,
-    gint64 * rstop)
+    GstStepInfo * current, guint64 * cstart, guint64 * cstop, guint64 * rstart,
+    guint64 * rstop)
 {
   gboolean step_end = FALSE;
 
@@ -1845,7 +1655,8 @@ handle_stepping (GstBaseSink * sink, GstSegment * segment,
     case GST_FORMAT_TIME:
     {
       guint64 end;
-      gint64 first, last;
+      guint64 first, last;
+      gdouble abs_rate;
 
       if (segment->rate > 0.0) {
         if (segment->stop == *cstop)
@@ -1864,8 +1675,9 @@ handle_stepping (GstBaseSink * sink, GstSegment * segment,
       end = current->start + current->amount;
       current->position = first - current->start;
 
-      if (G_UNLIKELY (segment->abs_rate != 1.0))
-        current->position /= segment->abs_rate;
+      abs_rate = ABS (segment->rate);
+      if (G_UNLIKELY (abs_rate != 1.0))
+        current->position /= abs_rate;
 
       GST_DEBUG_OBJECT (sink,
           "buffer: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
@@ -1929,44 +1741,45 @@ static gboolean
 gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
     GstClockTime * rsstart, GstClockTime * rsstop,
     GstClockTime * rrstart, GstClockTime * rrstop, gboolean * do_sync,
-    gboolean * stepped, GstSegment * segment, GstStepInfo * step,
-    gboolean * step_end, guint8 obj_type)
+    gboolean * stepped, GstStepInfo * step, gboolean * step_end)
 {
   GstBaseSinkClass *bclass;
   GstBuffer *buffer;
   GstClockTime start, stop;     /* raw start/stop timestamps */
-  gint64 cstart, cstop;         /* clipped raw timestamps */
-  gint64 rstart, rstop;         /* clipped timestamps converted to running time */
+  guint64 cstart, cstop;        /* clipped raw timestamps */
+  guint64 rstart, rstop;        /* clipped timestamps converted to running time */
   GstClockTime sstart, sstop;   /* clipped timestamps converted to stream time */
   GstFormat format;
   GstBaseSinkPrivate *priv;
+  GstSegment *segment;
   gboolean eos;
 
   priv = basesink->priv;
+  segment = &basesink->segment;
 
   /* start with nothing */
   start = stop = GST_CLOCK_TIME_NONE;
 
-  if (G_UNLIKELY (OBJ_IS_EVENT (obj_type))) {
+  if (G_UNLIKELY (GST_IS_EVENT (obj))) {
     GstEvent *event = GST_EVENT_CAST (obj);
 
     switch (GST_EVENT_TYPE (event)) {
         /* EOS event needs syncing */
       case GST_EVENT_EOS:
       {
-        if (basesink->segment.rate >= 0.0) {
+        if (segment->rate >= 0.0) {
           sstart = sstop = priv->current_sstop;
           if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
             /* we have not seen a buffer yet, use the segment values */
-            sstart = sstop = gst_segment_to_stream_time (&basesink->segment,
-                basesink->segment.format, basesink->segment.stop);
+            sstart = sstop = gst_segment_to_stream_time (segment,
+                segment->format, segment->stop);
           }
         } else {
           sstart = sstop = priv->current_sstart;
           if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
             /* we have not seen a buffer yet, use the segment values */
-            sstart = sstop = gst_segment_to_stream_time (&basesink->segment,
-                basesink->segment.format, basesink->segment.start);
+            sstart = sstop = gst_segment_to_stream_time (segment,
+                segment->format, segment->start);
           }
         }
 
@@ -1981,11 +1794,6 @@ gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
       }
       default:
         /* other events do not need syncing */
-        /* FIXME, maybe NEWSEGMENT might need synchronisation
-         * since the POSITION query depends on accumulated times and
-         * we cannot accumulate the current segment before the previous
-         * one completed.
-         */
         return FALSE;
     }
   }
@@ -2006,7 +1814,7 @@ again:
   if (!GST_CLOCK_TIME_IS_VALID (start)) {
     /* we don't need to sync but we still want to get the timestamps for
      * tracking the position */
-    gst_base_sink_get_times (basesink, buffer, &start, &stop);
+    gst_base_sink_default_get_times (basesink, buffer, &start, &stop);
     *do_sync = FALSE;
   } else {
     *do_sync = TRUE;
@@ -2019,19 +1827,9 @@ again:
   /* collect segment and format for code clarity */
   format = segment->format;
 
-  /* no timestamp clipping if we did not get a TIME segment format */
-  if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
-    cstart = start;
-    cstop = stop;
-    /* do running and stream time in TIME format */
-    format = GST_FORMAT_TIME;
-    GST_LOG_OBJECT (basesink, "not time format, don't clip");
-    goto do_times;
-  }
-
-  /* clip, only when we know about time */
-  if (G_UNLIKELY (!gst_segment_clip (segment, GST_FORMAT_TIME,
-              (gint64) start, (gint64) stop, &cstart, &cstop))) {
+  /* clip */
+  if (G_UNLIKELY (!gst_segment_clip (segment, format,
+              start, stop, &cstart, &cstop))) {
     if (step->valid) {
       GST_DEBUG_OBJECT (basesink, "step out of segment");
       /* when we are stepping, pretend we're at the end of the segment */
@@ -2055,9 +1853,9 @@ again:
 
   /* set last stop position */
   if (G_LIKELY (stop != GST_CLOCK_TIME_NONE && cstop != GST_CLOCK_TIME_NONE))
-    gst_segment_set_last_stop (segment, GST_FORMAT_TIME, cstop);
+    segment->position = cstop;
   else
-    gst_segment_set_last_stop (segment, GST_FORMAT_TIME, cstart);
+    segment->position = cstart;
 
 do_times:
   rstart = gst_segment_to_running_time (segment, format, cstart);
@@ -2220,11 +2018,11 @@ gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,
    * entry. */
   sink->clock_id = sink->priv->cached_clock_id;
   /* release the preroll lock while waiting */
-  GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
+  GST_BASE_SINK_PREROLL_UNLOCK (sink);
 
   ret = gst_clock_id_wait (sink->priv->cached_clock_id, jitter);
 
-  GST_PAD_PREROLL_LOCK (sink->sinkpad);
+  GST_BASE_SINK_PREROLL_LOCK (sink);
   sink->clock_id = NULL;
 
   return ret;
@@ -2260,7 +2058,7 @@ no_clock:
  * This function will block until a state change to PLAYING happens (in which
  * case this function returns #GST_FLOW_OK) or the processing must be stopped due
  * to a state change to READY or a FLUSH event (in which case this function
- * returns #GST_FLOW_WRONG_STATE).
+ * returns #GST_FLOW_FLUSHING).
  *
  * This function should only be called with the PREROLL_LOCK held, like in the
  * render function.
@@ -2276,7 +2074,7 @@ gst_base_sink_wait_preroll (GstBaseSink * sink)
   sink->have_preroll = TRUE;
   GST_DEBUG_OBJECT (sink, "waiting in preroll for flush or PLAYING");
   /* block until the state changes, or we get a flush, or something */
-  GST_PAD_PREROLL_WAIT (sink->sinkpad);
+  GST_BASE_SINK_PREROLL_WAIT (sink);
   sink->have_preroll = FALSE;
   if (G_UNLIKELY (sink->flushing))
     goto stopping;
@@ -2290,7 +2088,7 @@ gst_base_sink_wait_preroll (GstBaseSink * sink)
 stopping:
   {
     GST_DEBUG_OBJECT (sink, "preroll interrupted because of flush");
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 step_unlocked:
   {
@@ -2300,23 +2098,6 @@ step_unlocked:
   }
 }
 
-static inline guint8
-get_object_type (GstMiniObject * obj)
-{
-  guint8 obj_type;
-
-  if (G_LIKELY (GST_IS_BUFFER (obj)))
-    obj_type = _PR_IS_BUFFER;
-  else if (GST_IS_EVENT (obj))
-    obj_type = _PR_IS_EVENT;
-  else if (GST_IS_BUFFER_LIST (obj))
-    obj_type = _PR_IS_BUFFERLIST;
-  else
-    obj_type = _PR_IS_NOTHING;
-
-  return obj_type;
-}
-
 /**
  * gst_base_sink_do_preroll:
  * @sink: the sink
@@ -2340,14 +2121,46 @@ gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
   GstFlowReturn ret;
 
   while (G_UNLIKELY (sink->need_preroll)) {
-    guint8 obj_type;
     GST_DEBUG_OBJECT (sink, "prerolling object %p", obj);
 
-    obj_type = get_object_type (obj);
+    /* if it's a buffer, we need to call the preroll method */
+    if (sink->priv->call_preroll) {
+      GstBaseSinkClass *bclass;
+      GstBuffer *buf;
+
+      if (GST_IS_BUFFER_LIST (obj)) {
+        buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+        g_assert (NULL != buf);
+      } else if (GST_IS_BUFFER (obj)) {
+        buf = GST_BUFFER_CAST (obj);
+        /* For buffer lists do not set last buffer for now */
+        gst_base_sink_set_last_buffer (sink, buf);
+      } else
+        buf = NULL;
+
+      if (buf) {
+        GST_DEBUG_OBJECT (sink, "preroll buffer %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+        bclass = GST_BASE_SINK_GET_CLASS (sink);
+
+        if (bclass->prepare)
+          if ((ret = bclass->prepare (sink, buf)) != GST_FLOW_OK)
+            goto prepare_canceled;
+
+        if (bclass->preroll)
+          if ((ret = bclass->preroll (sink, buf)) != GST_FLOW_OK)
+            goto preroll_canceled;
+
+        sink->priv->call_preroll = FALSE;
+      }
+    }
 
-    ret = gst_base_sink_preroll_object (sink, obj_type, obj);
-    if (ret != GST_FLOW_OK)
-      goto preroll_failed;
+    /* commit state */
+    if (G_LIKELY (sink->playing_async)) {
+      if (G_UNLIKELY (!gst_base_sink_commit_state (sink)))
+        goto stopping;
+    }
 
     /* need to recheck here because the commit state could have
      * made us not need the preroll anymore */
@@ -2361,6 +2174,23 @@ gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
   return GST_FLOW_OK;
 
   /* ERRORS */
+prepare_canceled:
+  {
+    GST_DEBUG_OBJECT (sink, "prepare failed, abort state");
+    gst_element_abort_state (GST_ELEMENT_CAST (sink));
+    return ret;
+  }
+preroll_canceled:
+  {
+    GST_DEBUG_OBJECT (sink, "preroll failed, abort state");
+    gst_element_abort_state (GST_ELEMENT_CAST (sink));
+    return ret;
+  }
+stopping:
+  {
+    GST_DEBUG_OBJECT (sink, "stopping while commiting state");
+    return GST_FLOW_FLUSHING;
+  }
 preroll_failed:
   {
     GST_DEBUG_OBJECT (sink, "preroll failed: %s", gst_flow_get_name (ret));
@@ -2411,8 +2241,7 @@ gst_base_sink_wait_eos (GstBaseSink * sink, GstClockTime time,
     GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %"
         GST_TIME_FORMAT, GST_TIME_ARGS (time));
 
-    /* compensate for latency and ts_offset. We don't adjust for render delay
-     * because we don't interact with the device on EOS normally. */
+    /* compensate for latency, ts_offset and render delay */
     stime = gst_base_sink_adjust_time (sink, time);
 
     /* wait for the clock, this can be interrupted because we got shut down or
@@ -2441,7 +2270,7 @@ gst_base_sink_wait_eos (GstBaseSink * sink, GstClockTime time,
 flushing:
   {
     GST_DEBUG_OBJECT (sink, "we are flushing");
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 }
 
@@ -2468,8 +2297,8 @@ flushing:
  * does not take ownership of obj.
  */
 static GstFlowReturn
-gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,
-    GstMiniObject * obj, gboolean * late, gboolean * step_end, guint8 obj_type)
+gst_base_sink_do_sync (GstBaseSink * basesink,
+    GstMiniObject * obj, gboolean * late, gboolean * step_end)
 {
   GstClockTimeDiff jitter = 0;
   gboolean syncable;
@@ -2496,8 +2325,7 @@ do_step:
 
   /* get timing information for this object against the render segment */
   syncable = gst_base_sink_get_sync_times (basesink, obj,
-      &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, &basesink->segment,
-      current, step_end, obj_type);
+      &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, current, step_end);
 
   if (G_UNLIKELY (stepped))
     goto step_skipped;
@@ -2563,14 +2391,6 @@ again:
   /* adjust for latency */
   stime = gst_base_sink_adjust_time (basesink, rstart);
 
-  /* adjust for render-delay, avoid underflows */
-  if (GST_CLOCK_TIME_IS_VALID (stime)) {
-    if (stime > priv->render_delay)
-      stime -= priv->render_delay;
-    else
-      stime = 0;
-  }
-
   /* preroll done, we can sync since we are in PLAYING now. */
   GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %"
       GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT,
@@ -2630,7 +2450,7 @@ qos_dropped:
 flushing:
   {
     GST_DEBUG_OBJECT (basesink, "we are flushing");
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 preroll_failed:
   {
@@ -2652,7 +2472,7 @@ gst_base_sink_send_qos (GstBaseSink * basesink, GstQOSType type,
       "qos: type %d, proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %"
       GST_TIME_FORMAT, type, proportion, diff, GST_TIME_ARGS (time));
 
-  event = gst_event_new_qos_full (type, proportion, diff, time);
+  event = gst_event_new_qos (type, proportion, diff, time);
 
   /* send upstream */
   res = gst_pad_push_event (basesink->sinkpad, event);
@@ -2833,7 +2653,7 @@ gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj,
     GstClockReturn status, GstClockTimeDiff jitter)
 {
   gboolean late;
-  gint64 max_lateness;
+  guint64 max_lateness;
   GstBaseSinkPrivate *priv;
 
   priv = basesink->priv;
@@ -2844,7 +2664,7 @@ gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj,
   if (G_LIKELY (status != GST_CLOCK_EARLY))
     goto in_time;
 
-  max_lateness = basesink->abidata.ABI.max_lateness;
+  max_lateness = basesink->max_lateness;
 
   /* check if frame dropping is enabled */
   if (max_lateness == -1)
@@ -2949,616 +2769,259 @@ gst_base_sink_do_render_stats (GstBaseSink * basesink, gboolean start)
   }
 }
 
-/* with STREAM_LOCK, PREROLL_LOCK,
- *
- * Synchronize the object on the clock and then render it.
- *
- * takes ownership of obj.
- */
-static GstFlowReturn
-gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad,
-    guint8 obj_type, gpointer obj)
+static void
+gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
 {
-  GstFlowReturn ret;
-  GstBaseSinkClass *bclass;
-  gboolean late, step_end;
-  gpointer sync_obj;
-  GstBaseSinkPrivate *priv;
-
-  priv = basesink->priv;
+  /* make sure we are not blocked on the clock also clear any pending
+   * eos state. */
+  gst_base_sink_set_flushing (basesink, pad, TRUE);
 
-  if (OBJ_IS_BUFFERLIST (obj_type)) {
-    /*
-     * If buffer list, use the first group buffer within the list
-     * for syncing
-     */
-    sync_obj = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
-    g_assert (NULL != sync_obj);
+  /* we grab the stream lock but that is not needed since setting the
+   * sink to flushing would make sure no state commit is being done
+   * anymore */
+  GST_PAD_STREAM_LOCK (pad);
+  gst_base_sink_reset_qos (basesink);
+  /* and we need to commit our state again on the next
+   * prerolled buffer */
+  basesink->playing_async = TRUE;
+  if (basesink->priv->async_enabled) {
+    gst_element_lost_state (GST_ELEMENT_CAST (basesink));
   } else {
-    sync_obj = obj;
+    /* start time reset in above case as well;
+     * arranges for a.o. proper position reporting when flushing in PAUSED */
+    gst_element_set_start_time (GST_ELEMENT_CAST (basesink), 0);
+    basesink->priv->have_latency = TRUE;
   }
+  gst_base_sink_set_last_buffer (basesink, NULL);
+  GST_PAD_STREAM_UNLOCK (pad);
+}
 
-again:
-  late = FALSE;
-  step_end = FALSE;
+static void
+gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad,
+    gboolean reset_time)
+{
+  /* unset flushing so we can accept new data, this also flushes out any EOS
+   * event. */
+  gst_base_sink_set_flushing (basesink, pad, FALSE);
 
-  /* synchronize this object, non syncable objects return OK
-   * immediately. */
-  ret =
-      gst_base_sink_do_sync (basesink, pad, sync_obj, &late, &step_end,
-      obj_type);
-  if (G_UNLIKELY (ret != GST_FLOW_OK))
-    goto sync_failed;
+  /* for position reporting */
+  GST_OBJECT_LOCK (basesink);
+  basesink->priv->current_sstart = GST_CLOCK_TIME_NONE;
+  basesink->priv->current_sstop = GST_CLOCK_TIME_NONE;
+  basesink->priv->eos_rtime = GST_CLOCK_TIME_NONE;
+  basesink->priv->call_preroll = TRUE;
+  basesink->priv->current_step.valid = FALSE;
+  basesink->priv->pending_step.valid = FALSE;
+  if (basesink->pad_mode == GST_PAD_MODE_PUSH) {
+    /* we need new segment info after the flush. */
+    basesink->have_newsegment = FALSE;
+    if (reset_time) {
+      gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
+    }
+  }
+  GST_OBJECT_UNLOCK (basesink);
 
-  /* and now render, event or buffer/buffer list. */
-  if (G_LIKELY (OBJ_IS_BUFFERFULL (obj_type))) {
-    /* drop late buffers unconditionally, let's hope it's unlikely */
-    if (G_UNLIKELY (late))
-      goto dropped;
+  if (reset_time) {
+    GST_DEBUG_OBJECT (basesink, "posting reset-time message");
+    gst_element_post_message (GST_ELEMENT_CAST (basesink),
+        gst_message_new_reset_time (GST_OBJECT_CAST (basesink), 0));
+  }
+}
 
-    bclass = GST_BASE_SINK_GET_CLASS (basesink);
+static GstFlowReturn
+gst_base_sink_default_wait_eos (GstBaseSink * basesink, GstEvent * event)
+{
+  GstFlowReturn ret;
+  gboolean late, step_end;
 
-    if (G_LIKELY ((OBJ_IS_BUFFERLIST (obj_type) && bclass->render_list) ||
-            (!OBJ_IS_BUFFERLIST (obj_type) && bclass->render))) {
-      gint do_qos;
+  ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (event),
+      &late, &step_end);
 
-      /* read once, to get same value before and after */
-      do_qos = g_atomic_int_get (&priv->qos_enabled);
+  return ret;
+}
 
-      GST_DEBUG_OBJECT (basesink, "rendering object %p", obj);
+static gboolean
+gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
+{
+  gboolean result = TRUE;
+  GstBaseSinkClass *bclass;
 
-      /* record rendering time for QoS and stats */
-      if (do_qos)
-        gst_base_sink_do_render_stats (basesink, TRUE);
+  bclass = GST_BASE_SINK_GET_CLASS (basesink);
 
-      if (!OBJ_IS_BUFFERLIST (obj_type)) {
-        GstBuffer *buf;
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_START:
+    {
+      GST_DEBUG_OBJECT (basesink, "flush-start %p", event);
+      gst_base_sink_flush_start (basesink, basesink->sinkpad);
+      break;
+    }
+    case GST_EVENT_FLUSH_STOP:
+    {
+      gboolean reset_time;
 
-        /* For buffer lists do not set last buffer. Creating buffer
-         * with meaningful data can be done only with memcpy which will
-         * significantly affect performance */
-        buf = GST_BUFFER_CAST (obj);
-        gst_base_sink_set_last_buffer (basesink, buf);
+      gst_event_parse_flush_stop (event, &reset_time);
+      GST_DEBUG_OBJECT (basesink, "flush-stop %p, reset_time: %d", event,
+          reset_time);
+      gst_base_sink_flush_stop (basesink, basesink->sinkpad, reset_time);
+      break;
+    }
+    case GST_EVENT_EOS:
+    {
+      GstMessage *message;
+      guint32 seqnum;
 
-        ret = bclass->render (basesink, buf);
-      } else {
-        GstBufferList *buflist;
+      /* we set the received EOS flag here so that we can use it when testing if
+       * we are prerolled and to refuse more buffers. */
+      basesink->priv->received_eos = TRUE;
 
-        buflist = GST_BUFFER_LIST_CAST (obj);
+      /* wait for EOS */
+      if (G_LIKELY (bclass->wait_eos)) {
+        GstFlowReturn ret;
 
-        ret = bclass->render_list (basesink, buflist);
+        ret = bclass->wait_eos (basesink, event);
+        if (G_UNLIKELY (ret != GST_FLOW_OK)) {
+          result = FALSE;
+          goto done;
+        }
       }
 
-      if (do_qos)
-        gst_base_sink_do_render_stats (basesink, FALSE);
+      /* the EOS event is completely handled so we mark
+       * ourselves as being in the EOS state. eos is also
+       * protected by the object lock so we can read it when
+       * answering the POSITION query. */
+      GST_OBJECT_LOCK (basesink);
+      basesink->eos = TRUE;
+      GST_OBJECT_UNLOCK (basesink);
 
-      if (ret == GST_FLOW_STEP)
-        goto again;
+      /* ok, now we can post the message */
+      GST_DEBUG_OBJECT (basesink, "Now posting EOS");
 
-      if (G_UNLIKELY (basesink->flushing))
-        goto flushing;
+      seqnum = basesink->priv->seqnum = gst_event_get_seqnum (event);
+      GST_DEBUG_OBJECT (basesink, "Got seqnum #%" G_GUINT32_FORMAT, seqnum);
 
-      priv->rendered++;
+      message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
+      gst_message_set_seqnum (message, seqnum);
+      gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
+      break;
     }
-  } else if (G_LIKELY (OBJ_IS_EVENT (obj_type))) {
-    GstEvent *event = GST_EVENT_CAST (obj);
-    gboolean event_res = TRUE;
-    GstEventType type;
-
-    bclass = GST_BASE_SINK_GET_CLASS (basesink);
-
-    type = GST_EVENT_TYPE (event);
-
-    GST_DEBUG_OBJECT (basesink, "rendering event %p, type %s", obj,
-        gst_event_type_get_name (type));
-
-    if (bclass->event)
-      event_res = bclass->event (basesink, event);
-
-    /* when we get here we could be flushing again when the event handler calls
-     * _wait_eos(). We have to ignore this object in that case. */
-    if (G_UNLIKELY (basesink->flushing))
-      goto flushing;
+    case GST_EVENT_CAPS:
+    {
+      GstCaps *caps;
 
-    if (G_LIKELY (event_res)) {
-      guint32 seqnum;
+      GST_DEBUG_OBJECT (basesink, "caps %p", event);
 
-      seqnum = basesink->priv->seqnum = gst_event_get_seqnum (event);
-      GST_DEBUG_OBJECT (basesink, "Got seqnum #%" G_GUINT32_FORMAT, seqnum);
+      gst_event_parse_caps (event, &caps);
+      if (bclass->set_caps)
+        result = bclass->set_caps (basesink, caps);
 
-      switch (type) {
-        case GST_EVENT_EOS:
-        {
-          GstMessage *message;
+      if (result) {
+        GST_OBJECT_LOCK (basesink);
+        gst_caps_replace (&basesink->priv->caps, caps);
+        GST_OBJECT_UNLOCK (basesink);
+      }
+      break;
+    }
+    case GST_EVENT_SEGMENT:
+      /* configure the segment */
+      /* The segment is protected with both the STREAM_LOCK and the OBJECT_LOCK.
+       * We protect with the OBJECT_LOCK so that we can use the values to
+       * safely answer a POSITION query. */
+      GST_OBJECT_LOCK (basesink);
+      /* the newsegment event is needed to bring the buffer timestamps to the
+       * stream time and to drop samples outside of the playback segment. */
+      gst_event_copy_segment (event, &basesink->segment);
+      GST_DEBUG_OBJECT (basesink, "configured SEGMENT %" GST_SEGMENT_FORMAT,
+          &basesink->segment);
+      basesink->have_newsegment = TRUE;
+      GST_OBJECT_UNLOCK (basesink);
+      break;
+    case GST_EVENT_TAG:
+    {
+      GstTagList *taglist;
 
-          /* the EOS event is completely handled so we mark
-           * ourselves as being in the EOS state. eos is also
-           * protected by the object lock so we can read it when
-           * answering the POSITION query. */
-          GST_OBJECT_LOCK (basesink);
-          basesink->eos = TRUE;
-          GST_OBJECT_UNLOCK (basesink);
+      gst_event_parse_tag (event, &taglist);
 
-          /* ok, now we can post the message */
-          GST_DEBUG_OBJECT (basesink, "Now posting EOS");
+      gst_element_post_message (GST_ELEMENT_CAST (basesink),
+          gst_message_new_tag (GST_OBJECT_CAST (basesink),
+              gst_tag_list_copy (taglist)));
+      break;
+    }
+    case GST_EVENT_SINK_MESSAGE:
+    {
+      GstMessage *msg = NULL;
 
-          message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
-          gst_message_set_seqnum (message, seqnum);
-          gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
-          break;
-        }
-        case GST_EVENT_NEWSEGMENT:
-          /* configure the segment */
-          gst_base_sink_configure_segment (basesink, pad, event,
-              &basesink->segment);
-          break;
-        case GST_EVENT_SINK_MESSAGE:{
-          GstMessage *msg = NULL;
-
-          gst_event_parse_sink_message (event, &msg);
-
-          if (msg)
-            gst_element_post_message (GST_ELEMENT_CAST (basesink), msg);
-        }
-        default:
-          break;
-      }
+      gst_event_parse_sink_message (event, &msg);
+      if (msg)
+        gst_element_post_message (GST_ELEMENT_CAST (basesink), msg);
+      break;
     }
-  } else {
-    g_return_val_if_reached (GST_FLOW_ERROR);
+    default:
+      break;
   }
-
 done:
-  if (step_end) {
-    /* the step ended, check if we need to activate a new step */
-    GST_DEBUG_OBJECT (basesink, "step ended");
-    stop_stepping (basesink, &basesink->segment, &priv->current_step,
-        priv->current_rstart, priv->current_rstop, basesink->eos);
-    goto again;
-  }
+  gst_event_unref (event);
 
-  gst_base_sink_perform_qos (basesink, late);
-
-  GST_DEBUG_OBJECT (basesink, "object unref after render %p", obj);
-  gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
-  return ret;
-
-  /* ERRORS */
-sync_failed:
-  {
-    GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret));
-    goto done;
-  }
-dropped:
-  {
-    priv->dropped++;
-    GST_DEBUG_OBJECT (basesink, "buffer late, dropping");
-
-    if (g_atomic_int_get (&priv->qos_enabled)) {
-      GstMessage *qos_msg;
-      GstClockTime timestamp, duration;
-
-      timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (sync_obj));
-      duration = GST_BUFFER_DURATION (GST_BUFFER_CAST (sync_obj));
-
-      GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
-          "qos: dropped buffer rt %" GST_TIME_FORMAT ", st %" GST_TIME_FORMAT
-          ", ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (priv->current_rstart),
-          GST_TIME_ARGS (priv->current_sstart), GST_TIME_ARGS (timestamp),
-          GST_TIME_ARGS (duration));
-      GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
-          "qos: rendered %" G_GUINT64_FORMAT ", dropped %" G_GUINT64_FORMAT,
-          priv->rendered, priv->dropped);
-
-      qos_msg =
-          gst_message_new_qos (GST_OBJECT_CAST (basesink), basesink->sync,
-          priv->current_rstart, priv->current_sstart, timestamp, duration);
-      gst_message_set_qos_values (qos_msg, priv->current_jitter, priv->avg_rate,
-          1000000);
-      gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, priv->rendered,
-          priv->dropped);
-      gst_element_post_message (GST_ELEMENT_CAST (basesink), qos_msg);
-    }
-    goto done;
-  }
-flushing:
-  {
-    GST_DEBUG_OBJECT (basesink, "we are flushing, ignore object");
-    gst_mini_object_unref (obj);
-    return GST_FLOW_WRONG_STATE;
-  }
-}
-
-/* with STREAM_LOCK, PREROLL_LOCK
- *
- * Perform preroll on the given object. For buffers this means
- * calling the preroll subclass method.
- * If that succeeds, the state will be commited.
- *
- * function does not take ownership of obj.
- */
-static GstFlowReturn
-gst_base_sink_preroll_object (GstBaseSink * basesink, guint8 obj_type,
-    GstMiniObject * obj)
-{
-  GstFlowReturn ret;
-
-  GST_DEBUG_OBJECT (basesink, "prerolling object %p", obj);
-
-  /* if it's a buffer, we need to call the preroll method */
-  if (G_LIKELY (OBJ_IS_BUFFERFULL (obj_type) && basesink->priv->call_preroll)) {
-    GstBaseSinkClass *bclass;
-    GstBuffer *buf;
-    GstClockTime timestamp;
-
-    if (OBJ_IS_BUFFERLIST (obj_type)) {
-      buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
-      g_assert (NULL != buf);
-    } else {
-      buf = GST_BUFFER_CAST (obj);
-    }
-
-    timestamp = GST_BUFFER_TIMESTAMP (buf);
-
-    GST_DEBUG_OBJECT (basesink, "preroll buffer %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (timestamp));
-
-    /*
-     * For buffer lists do not set last buffer. Creating buffer
-     * with meaningful data can be done only with memcpy which will
-     * significantly affect performance
-     */
-    if (!OBJ_IS_BUFFERLIST (obj_type)) {
-      gst_base_sink_set_last_buffer (basesink, buf);
-    }
-
-    bclass = GST_BASE_SINK_GET_CLASS (basesink);
-    if (bclass->preroll)
-      if ((ret = bclass->preroll (basesink, buf)) != GST_FLOW_OK)
-        goto preroll_failed;
-
-    basesink->priv->call_preroll = FALSE;
-  }
-
-  /* commit state */
-  if (G_LIKELY (basesink->playing_async)) {
-    if (G_UNLIKELY (!gst_base_sink_commit_state (basesink)))
-      goto stopping;
-  }
-
-  return GST_FLOW_OK;
-
-  /* ERRORS */
-preroll_failed:
-  {
-    GST_DEBUG_OBJECT (basesink, "preroll failed, abort state");
-    gst_element_abort_state (GST_ELEMENT_CAST (basesink));
-    return ret;
-  }
-stopping:
-  {
-    GST_DEBUG_OBJECT (basesink, "stopping while commiting state");
-    return GST_FLOW_WRONG_STATE;
-  }
-}
-
-/* with STREAM_LOCK, PREROLL_LOCK
- *
- * Queue an object for rendering.
- * The first prerollable object queued will complete the preroll. If the
- * preroll queue is filled, we render all the objects in the queue.
- *
- * This function takes ownership of the object.
- */
-static GstFlowReturn
-gst_base_sink_queue_object_unlocked (GstBaseSink * basesink, GstPad * pad,
-    guint8 obj_type, gpointer obj, gboolean prerollable)
-{
-  GstFlowReturn ret = GST_FLOW_OK;
-  gint length;
-  GQueue *q;
-
-  if (G_UNLIKELY (basesink->need_preroll)) {
-    if (G_LIKELY (prerollable))
-      basesink->preroll_queued++;
-
-    length = basesink->preroll_queued;
-
-    GST_DEBUG_OBJECT (basesink, "now %d prerolled items", length);
-
-    /* first prerollable item needs to finish the preroll */
-    if (length == 1) {
-      ret = gst_base_sink_preroll_object (basesink, obj_type, obj);
-      if (G_UNLIKELY (ret != GST_FLOW_OK))
-        goto preroll_failed;
-    }
-    /* need to recheck if we need preroll, commit state during preroll
-     * could have made us not need more preroll. */
-    if (G_UNLIKELY (basesink->need_preroll)) {
-      /* see if we can render now, if we can't add the object to the preroll
-       * queue. */
-      if (G_UNLIKELY (length <= basesink->preroll_queue_max_len))
-        goto more_preroll;
-    }
-  }
-  /* we can start rendering (or blocking) the queued object
-   * if any. */
-  q = basesink->preroll_queue;
-  while (G_UNLIKELY (!g_queue_is_empty (q))) {
-    GstMiniObject *o;
-    guint8 ot;
-
-    o = g_queue_pop_head (q);
-    GST_DEBUG_OBJECT (basesink, "rendering queued object %p", o);
-
-    ot = get_object_type (o);
-
-    /* do something with the return value */
-    ret = gst_base_sink_render_object (basesink, pad, ot, o);
-    if (ret != GST_FLOW_OK)
-      goto dequeue_failed;
-  }
-
-  /* now render the object */
-  ret = gst_base_sink_render_object (basesink, pad, obj_type, obj);
-  basesink->preroll_queued = 0;
-
-  return ret;
-
-  /* special cases */
-preroll_failed:
-  {
-    GST_DEBUG_OBJECT (basesink, "preroll failed, reason %s",
-        gst_flow_get_name (ret));
-    gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
-    return ret;
-  }
-more_preroll:
-  {
-    /* add object to the queue and return */
-    GST_DEBUG_OBJECT (basesink, "need more preroll data %d <= %d",
-        length, basesink->preroll_queue_max_len);
-    g_queue_push_tail (basesink->preroll_queue, obj);
-    return GST_FLOW_OK;
-  }
-dequeue_failed:
-  {
-    GST_DEBUG_OBJECT (basesink, "rendering queued objects failed, reason %s",
-        gst_flow_get_name (ret));
-    gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
-    return ret;
-  }
-}
-
-/* with STREAM_LOCK
- *
- * This function grabs the PREROLL_LOCK and adds the object to
- * the queue.
- *
- * This function takes ownership of obj.
- *
- * Note: Only GstEvent seem to be passed to this private method
- */
-static GstFlowReturn
-gst_base_sink_queue_object (GstBaseSink * basesink, GstPad * pad,
-    GstMiniObject * obj, gboolean prerollable)
-{
-  GstFlowReturn ret;
-
-  GST_PAD_PREROLL_LOCK (pad);
-  if (G_UNLIKELY (basesink->flushing))
-    goto flushing;
-
-  if (G_UNLIKELY (basesink->priv->received_eos))
-    goto was_eos;
-
-  ret =
-      gst_base_sink_queue_object_unlocked (basesink, pad, _PR_IS_EVENT, obj,
-      prerollable);
-  GST_PAD_PREROLL_UNLOCK (pad);
-
-  return ret;
-
-  /* ERRORS */
-flushing:
-  {
-    GST_DEBUG_OBJECT (basesink, "sink is flushing");
-    GST_PAD_PREROLL_UNLOCK (pad);
-    gst_mini_object_unref (obj);
-    return GST_FLOW_WRONG_STATE;
-  }
-was_eos:
-  {
-    GST_DEBUG_OBJECT (basesink,
-        "we are EOS, dropping object, return UNEXPECTED");
-    GST_PAD_PREROLL_UNLOCK (pad);
-    gst_mini_object_unref (obj);
-    return GST_FLOW_UNEXPECTED;
-  }
-}
-
-static void
-gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
-{
-  /* make sure we are not blocked on the clock also clear any pending
-   * eos state. */
-  gst_base_sink_set_flushing (basesink, pad, TRUE);
-
-  /* we grab the stream lock but that is not needed since setting the
-   * sink to flushing would make sure no state commit is being done
-   * anymore */
-  GST_PAD_STREAM_LOCK (pad);
-  gst_base_sink_reset_qos (basesink);
-  /* and we need to commit our state again on the next
-   * prerolled buffer */
-  basesink->playing_async = TRUE;
-  if (basesink->priv->async_enabled) {
-    gst_element_lost_state (GST_ELEMENT_CAST (basesink));
-  } else {
-    /* start time reset in above case as well;
-     * arranges for a.o. proper position reporting when flushing in PAUSED */
-    gst_element_set_start_time (GST_ELEMENT_CAST (basesink), 0);
-    basesink->priv->have_latency = TRUE;
-  }
-  gst_base_sink_set_last_buffer (basesink, NULL);
-  GST_PAD_STREAM_UNLOCK (pad);
-}
-
-static void
-gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad)
-{
-  /* unset flushing so we can accept new data, this also flushes out any EOS
-   * event. */
-  gst_base_sink_set_flushing (basesink, pad, FALSE);
-
-  /* for position reporting */
-  GST_OBJECT_LOCK (basesink);
-  basesink->priv->current_sstart = GST_CLOCK_TIME_NONE;
-  basesink->priv->current_sstop = GST_CLOCK_TIME_NONE;
-  basesink->priv->eos_rtime = GST_CLOCK_TIME_NONE;
-  basesink->priv->call_preroll = TRUE;
-  basesink->priv->current_step.valid = FALSE;
-  basesink->priv->pending_step.valid = FALSE;
-  if (basesink->pad_mode == GST_ACTIVATE_PUSH) {
-    /* we need new segment info after the flush. */
-    basesink->have_newsegment = FALSE;
-    gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
-    gst_segment_init (basesink->abidata.ABI.clip_segment, GST_FORMAT_UNDEFINED);
-  }
-  GST_OBJECT_UNLOCK (basesink);
-}
+  return result;
+}
 
 static gboolean
-gst_base_sink_event (GstPad * pad, GstEvent * event)
+gst_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   GstBaseSink *basesink;
   gboolean result = TRUE;
   GstBaseSinkClass *bclass;
 
-  basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
-  if (G_UNLIKELY (basesink == NULL)) {
-    gst_event_unref (event);
-    return FALSE;
-  }
-
+  basesink = GST_BASE_SINK_CAST (parent);
   bclass = GST_BASE_SINK_GET_CLASS (basesink);
 
   GST_DEBUG_OBJECT (basesink, "received event %p %" GST_PTR_FORMAT, event,
       event);
 
   switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_EOS:
-    {
-      GstFlowReturn ret;
-
-      GST_PAD_PREROLL_LOCK (pad);
-      if (G_UNLIKELY (basesink->flushing))
-        goto flushing;
-
-      if (G_UNLIKELY (basesink->priv->received_eos)) {
-        /* we can't accept anything when we are EOS */
-        result = FALSE;
-        gst_event_unref (event);
-      } else {
-        /* we set the received EOS flag here so that we can use it when testing if
-         * we are prerolled and to refuse more buffers. */
-        basesink->priv->received_eos = TRUE;
-
-        /* EOS is a prerollable object, we call the unlocked version because it
-         * does not check the received_eos flag. */
-        ret = gst_base_sink_queue_object_unlocked (basesink, pad,
-            _PR_IS_EVENT, GST_MINI_OBJECT_CAST (event), TRUE);
-        if (G_UNLIKELY (ret != GST_FLOW_OK))
-          result = FALSE;
-      }
-      GST_PAD_PREROLL_UNLOCK (pad);
-      break;
-    }
-    case GST_EVENT_NEWSEGMENT:
-    {
-      GstFlowReturn ret;
-      gboolean update;
-
-      GST_DEBUG_OBJECT (basesink, "newsegment %p", event);
-
-      GST_PAD_PREROLL_LOCK (pad);
-      if (G_UNLIKELY (basesink->flushing))
-        goto flushing;
-
-      gst_event_parse_new_segment_full (event, &update, NULL, NULL, NULL, NULL,
-          NULL, NULL);
-
-      if (G_UNLIKELY (basesink->priv->received_eos && !update)) {
-        /* we can't accept anything when we are EOS */
-        result = FALSE;
-        gst_event_unref (event);
-      } else {
-        /* the new segment is a non prerollable item and does not block anything,
-         * we need to configure the current clipping segment and insert the event
-         * in the queue to serialize it with the buffers for rendering. */
-        gst_base_sink_configure_segment (basesink, pad, event,
-            basesink->abidata.ABI.clip_segment);
-
-        ret =
-            gst_base_sink_queue_object_unlocked (basesink, pad,
-            _PR_IS_EVENT, GST_MINI_OBJECT_CAST (event), FALSE);
-        if (G_UNLIKELY (ret != GST_FLOW_OK))
-          result = FALSE;
-        else {
-          GST_OBJECT_LOCK (basesink);
-          basesink->have_newsegment = TRUE;
-          GST_OBJECT_UNLOCK (basesink);
-        }
-      }
-      GST_PAD_PREROLL_UNLOCK (pad);
-      break;
-    }
-    case GST_EVENT_FLUSH_START:
-      if (bclass->event)
-        bclass->event (basesink, event);
-
-      GST_DEBUG_OBJECT (basesink, "flush-start %p", event);
-
-      gst_base_sink_flush_start (basesink, pad);
-
-      gst_event_unref (event);
-      break;
     case GST_EVENT_FLUSH_STOP:
+      /* special case for this serialized event because we don't want to grab
+       * the PREROLL lock or check if we were flushing */
       if (bclass->event)
-        bclass->event (basesink, event);
-
-      GST_DEBUG_OBJECT (basesink, "flush-stop %p", event);
-
-      gst_base_sink_flush_stop (basesink, pad);
-
-      gst_event_unref (event);
+        result = bclass->event (basesink, event);
       break;
     default:
-      /* other events are sent to queue or subclass depending on if they
-       * are serialized. */
       if (GST_EVENT_IS_SERIALIZED (event)) {
-        gst_base_sink_queue_object (basesink, pad,
-            GST_MINI_OBJECT_CAST (event), FALSE);
+        GST_BASE_SINK_PREROLL_LOCK (basesink);
+        if (G_UNLIKELY (basesink->flushing))
+          goto flushing;
+
+        if (G_UNLIKELY (basesink->priv->received_eos))
+          goto after_eos;
+
+        if (bclass->event)
+          result = bclass->event (basesink, event);
+
+        GST_BASE_SINK_PREROLL_UNLOCK (basesink);
       } else {
         if (bclass->event)
-          bclass->event (basesink, event);
-        gst_event_unref (event);
+          result = bclass->event (basesink, event);
       }
       break;
   }
 done:
-  gst_object_unref (basesink);
-
   return result;
 
   /* ERRORS */
 flushing:
   {
     GST_DEBUG_OBJECT (basesink, "we are flushing");
-    GST_PAD_PREROLL_UNLOCK (pad);
+    GST_BASE_SINK_PREROLL_UNLOCK (basesink);
+    gst_event_unref (event);
     result = FALSE;
+    goto done;
+  }
+
+after_eos:
+  {
+    GST_DEBUG_OBJECT (basesink, "Event received after EOS, dropping");
+    GST_BASE_SINK_PREROLL_UNLOCK (basesink);
     gst_event_unref (event);
+    result = FALSE;
     goto done;
   }
 }
@@ -3567,14 +3030,17 @@ flushing:
  * timestamps on a buffer, subclasses can override
  */
 static void
-gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
+gst_base_sink_default_get_times (GstBaseSink * basesink, GstBuffer * buffer,
     GstClockTime * start, GstClockTime * end)
 {
   GstClockTime timestamp, duration;
 
-  timestamp = GST_BUFFER_TIMESTAMP (buffer);
-  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+  /* first sync on DTS, else use PTS */
+  timestamp = GST_BUFFER_DTS (buffer);
+  if (!GST_CLOCK_TIME_IS_VALID (timestamp))
+    timestamp = GST_BUFFER_PTS (buffer);
 
+  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
     /* get duration to calculate end time */
     duration = GST_BUFFER_DURATION (buffer);
     if (GST_CLOCK_TIME_IS_VALID (duration)) {
@@ -3607,35 +3073,38 @@ gst_base_sink_needs_preroll (GstBaseSink * basesink)
  *
  * Takes a buffer and compare the timestamps with the last segment.
  * If the buffer falls outside of the segment boundaries, drop it.
- * Else queue the buffer for preroll and rendering.
+ * Else send the buffer for preroll and rendering.
  *
  * This function takes ownership of the buffer.
  */
 static GstFlowReturn
 gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
-    guint8 obj_type, gpointer obj)
+    gpointer obj, gboolean is_list)
 {
   GstBaseSinkClass *bclass;
-  GstFlowReturn result;
+  GstBaseSinkPrivate *priv = basesink->priv;
+  GstFlowReturn ret;
   GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
-  GstSegment *clip_segment;
-  GstBuffer *time_buf;
+  GstSegment *segment;
+  GstBuffer *sync_buf;
+  gint do_qos;
+  gboolean late, step_end;
 
   if (G_UNLIKELY (basesink->flushing))
     goto flushing;
 
-  if (G_UNLIKELY (basesink->priv->received_eos))
+  if (G_UNLIKELY (priv->received_eos))
     goto was_eos;
 
-  if (OBJ_IS_BUFFERLIST (obj_type)) {
-    time_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
-    g_assert (NULL != time_buf);
+  if (is_list) {
+    sync_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+    g_assert (NULL != sync_buf);
   } else {
-    time_buf = GST_BUFFER_CAST (obj);
+    sync_buf = GST_BUFFER_CAST (obj);
   }
 
   /* for code clarity */
-  clip_segment = basesink->abidata.ABI.clip_segment;
+  segment = &basesink->segment;
 
   if (G_UNLIKELY (!basesink->have_newsegment)) {
     gboolean sync;
@@ -3649,8 +3118,8 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
 
     /* this means this sink will assume timestamps start from 0 */
     GST_OBJECT_LOCK (basesink);
-    clip_segment->start = 0;
-    clip_segment->stop = -1;
+    segment->start = 0;
+    segment->stop = -1;
     basesink->segment.start = 0;
     basesink->segment.stop = -1;
     basesink->have_newsegment = TRUE;
@@ -3662,44 +3131,112 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
   /* check if the buffer needs to be dropped, we first ask the subclass for the
    * start and end */
   if (bclass->get_times)
-    bclass->get_times (basesink, time_buf, &start, &end);
+    bclass->get_times (basesink, sync_buf, &start, &end);
 
   if (!GST_CLOCK_TIME_IS_VALID (start)) {
     /* if the subclass does not want sync, we use our own values so that we at
      * least clip the buffer to the segment */
-    gst_base_sink_get_times (basesink, time_buf, &start, &end);
+    gst_base_sink_default_get_times (basesink, sync_buf, &start, &end);
   }
 
   GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
       ", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
 
   /* a dropped buffer does not participate in anything */
-  if (GST_CLOCK_TIME_IS_VALID (start) &&
-      (clip_segment->format == GST_FORMAT_TIME)) {
-    if (G_UNLIKELY (!gst_segment_clip (clip_segment,
-                GST_FORMAT_TIME, (gint64) start, (gint64) end, NULL, NULL)))
+  if (GST_CLOCK_TIME_IS_VALID (start) && (segment->format == GST_FORMAT_TIME)) {
+    if (G_UNLIKELY (!gst_segment_clip (segment,
+                GST_FORMAT_TIME, start, end, NULL, NULL)))
       goto out_of_segment;
   }
 
-  /* now we can process the buffer in the queue, this function takes ownership
-   * of the buffer */
-  result = gst_base_sink_queue_object_unlocked (basesink, pad,
-      obj_type, obj, TRUE);
-  return result;
+  if (!is_list) {
+    if (bclass->prepare) {
+      ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj));
+      if (G_UNLIKELY (ret != GST_FLOW_OK))
+        goto prepare_failed;
+    }
+  } else {
+    if (bclass->prepare_list) {
+      ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj));
+      if (G_UNLIKELY (ret != GST_FLOW_OK))
+        goto prepare_failed;
+    }
+  }
+
+again:
+  late = FALSE;
+  step_end = FALSE;
+
+  /* synchronize this object, non syncable objects return OK
+   * immediately. */
+  ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (sync_buf),
+      &late, &step_end);
+  if (G_UNLIKELY (ret != GST_FLOW_OK))
+    goto sync_failed;
+
+  /* drop late buffers unconditionally, let's hope it's unlikely */
+  if (G_UNLIKELY (late))
+    goto dropped;
+
+  /* read once, to get same value before and after */
+  do_qos = g_atomic_int_get (&priv->qos_enabled);
+
+  GST_DEBUG_OBJECT (basesink, "rendering object %p", obj);
+
+  /* record rendering time for QoS and stats */
+  if (do_qos)
+    gst_base_sink_do_render_stats (basesink, TRUE);
+
+  if (!is_list) {
+    /* For buffer lists do not set last buffer for now. */
+    gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj));
+
+    if (bclass->render)
+      ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
+  } else {
+    if (bclass->render_list)
+      ret = bclass->render_list (basesink, GST_BUFFER_LIST_CAST (obj));
+  }
+
+  if (do_qos)
+    gst_base_sink_do_render_stats (basesink, FALSE);
+
+  if (ret == GST_FLOW_STEP)
+    goto again;
+
+  if (G_UNLIKELY (basesink->flushing))
+    goto flushing;
+
+  priv->rendered++;
+
+done:
+  if (step_end) {
+    /* the step ended, check if we need to activate a new step */
+    GST_DEBUG_OBJECT (basesink, "step ended");
+    stop_stepping (basesink, &basesink->segment, &priv->current_step,
+        priv->current_rstart, priv->current_rstop, basesink->eos);
+    goto again;
+  }
+
+  gst_base_sink_perform_qos (basesink, late);
+
+  GST_DEBUG_OBJECT (basesink, "object unref after render %p", obj);
+  gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
+
+  return ret;
 
   /* ERRORS */
 flushing:
   {
     GST_DEBUG_OBJECT (basesink, "sink is flushing");
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 was_eos:
   {
-    GST_DEBUG_OBJECT (basesink,
-        "we are EOS, dropping object, return UNEXPECTED");
+    GST_DEBUG_OBJECT (basesink, "we are EOS, dropping object, return EOS");
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
-    return GST_FLOW_UNEXPECTED;
+    return GST_FLOW_EOS;
   }
 out_of_segment:
   {
@@ -3707,22 +3244,67 @@ out_of_segment:
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
     return GST_FLOW_OK;
   }
+prepare_failed:
+  {
+    GST_DEBUG_OBJECT (basesink, "prepare buffer failed %s",
+        gst_flow_get_name (ret));
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
+    return ret;
+  }
+sync_failed:
+  {
+    GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret));
+    goto done;
+  }
+dropped:
+  {
+    priv->dropped++;
+    GST_DEBUG_OBJECT (basesink, "buffer late, dropping");
+
+    if (g_atomic_int_get (&priv->qos_enabled)) {
+      GstMessage *qos_msg;
+      GstClockTime timestamp, duration;
+
+      timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (sync_buf));
+      duration = GST_BUFFER_DURATION (GST_BUFFER_CAST (sync_buf));
+
+      GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
+          "qos: dropped buffer rt %" GST_TIME_FORMAT ", st %" GST_TIME_FORMAT
+          ", ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (priv->current_rstart),
+          GST_TIME_ARGS (priv->current_sstart), GST_TIME_ARGS (timestamp),
+          GST_TIME_ARGS (duration));
+      GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
+          "qos: rendered %" G_GUINT64_FORMAT ", dropped %" G_GUINT64_FORMAT,
+          priv->rendered, priv->dropped);
+
+      qos_msg =
+          gst_message_new_qos (GST_OBJECT_CAST (basesink), basesink->sync,
+          priv->current_rstart, priv->current_sstart, timestamp, duration);
+      gst_message_set_qos_values (qos_msg, priv->current_jitter, priv->avg_rate,
+          1000000);
+      gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, priv->rendered,
+          priv->dropped);
+      gst_element_post_message (GST_ELEMENT_CAST (basesink), qos_msg);
+    }
+    goto done;
+  }
 }
 
 /* with STREAM_LOCK
  */
 static GstFlowReturn
-gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,
-    guint8 obj_type, gpointer obj)
+gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad, gpointer obj,
+    gboolean is_list)
 {
   GstFlowReturn result;
 
-  if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PUSH))
+  if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PUSH))
     goto wrong_mode;
 
-  GST_PAD_PREROLL_LOCK (pad);
-  result = gst_base_sink_chain_unlocked (basesink, pad, obj_type, obj);
-  GST_PAD_PREROLL_UNLOCK (pad);
+  GST_BASE_SINK_PREROLL_LOCK (basesink);
+  result = gst_base_sink_chain_unlocked (basesink, pad, obj, is_list);
+  GST_BASE_SINK_PREROLL_UNLOCK (basesink);
 
 done:
   return result;
@@ -3738,60 +3320,50 @@ wrong_mode:
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
     /* we don't post an error message this will signal to the peer
      * pushing that EOS is reached. */
-    result = GST_FLOW_UNEXPECTED;
+    result = GST_FLOW_EOS;
     goto done;
   }
 }
 
 static GstFlowReturn
-gst_base_sink_chain (GstPad * pad, GstBuffer * buf)
+gst_base_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
 {
   GstBaseSink *basesink;
 
-  basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
+  basesink = GST_BASE_SINK (parent);
 
-  return gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, buf);
+  return gst_base_sink_chain_main (basesink, pad, buf, FALSE);
 }
 
 static GstFlowReturn
-gst_base_sink_chain_list (GstPad * pad, GstBufferList * list)
+gst_base_sink_chain_list (GstPad * pad, GstObject * parent,
+    GstBufferList * list)
 {
   GstBaseSink *basesink;
   GstBaseSinkClass *bclass;
   GstFlowReturn result;
 
-  basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
+  basesink = GST_BASE_SINK (parent);
   bclass = GST_BASE_SINK_GET_CLASS (basesink);
 
   if (G_LIKELY (bclass->render_list)) {
-    result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFERLIST, list);
+    result = gst_base_sink_chain_main (basesink, pad, list, TRUE);
   } else {
-    GstBufferListIterator *it;
-    GstBuffer *group;
+    guint i, len;
+    GstBuffer *buffer;
 
     GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
 
-    it = gst_buffer_list_iterate (list);
+    len = gst_buffer_list_length (list);
 
-    if (gst_buffer_list_iterator_next_group (it)) {
-      do {
-        group = gst_buffer_list_iterator_merge_group (it);
-        if (group == NULL) {
-          group = gst_buffer_new ();
-          GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
-        } else {
-          GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining group");
-        }
-        result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, group);
-      } while (result == GST_FLOW_OK
-          && gst_buffer_list_iterator_next_group (it));
-    } else {
-      GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
-      result =
-          gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER,
-          gst_buffer_new ());
+    result = GST_FLOW_OK;
+    for (i = 0; i < len; i++) {
+      buffer = gst_buffer_list_get (list, i);
+      result = gst_base_sink_chain_main (basesink, pad,
+          gst_buffer_ref (buffer), FALSE);
+      if (result != GST_FLOW_OK)
+        break;
     }
-    gst_buffer_list_iterator_free (it);
     gst_buffer_list_unref (list);
   }
   return result;
@@ -3833,17 +3405,16 @@ gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
   GstSeekType cur_type, stop_type;
   gint64 cur, stop;
   GstSeekFlags flags;
-  GstFormat seek_format, dest_format;
+  GstFormat seek_format;
   gdouble rate;
   gboolean update;
   gboolean res = TRUE;
 
   gst_event_parse_seek (event, &rate, &seek_format, &flags,
       &cur_type, &cur, &stop_type, &stop);
-  dest_format = segment->format;
 
-  if (seek_format == dest_format) {
-    gst_segment_set_seek (segment, rate, seek_format, flags,
+  if (seek_format == segment->format) {
+    gst_segment_do_seek (segment, rate, seek_format, flags,
         cur_type, cur, stop_type, stop, &update);
     return TRUE;
   }
@@ -3851,7 +3422,7 @@ gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
   if (cur_type != GST_SEEK_TYPE_NONE) {
     /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
     res =
-        gst_pad_query_convert (sink->sinkpad, seek_format, cur, &dest_format,
+        gst_pad_query_convert (sink->sinkpad, seek_format, cur, segment->format,
         &cur);
     cur_type = GST_SEEK_TYPE_SET;
   }
@@ -3859,13 +3430,13 @@ gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
   if (res && stop_type != GST_SEEK_TYPE_NONE) {
     /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
     res =
-        gst_pad_query_convert (sink->sinkpad, seek_format, stop, &dest_format,
-        &stop);
+        gst_pad_query_convert (sink->sinkpad, seek_format, stop,
+        segment->format, &stop);
     stop_type = GST_SEEK_TYPE_SET;
   }
 
   /* And finally, configure our output segment in the desired format */
-  gst_segment_set_seek (segment, rate, dest_format, flags, cur_type, cur,
+  gst_segment_do_seek (segment, rate, segment->format, flags, cur_type, cur,
       stop_type, stop, &update);
 
   if (!res)
@@ -3938,7 +3509,7 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
       } else {
         /* The seek format matches our processing format, no need to ask the
          * the subclass to configure the segment. */
-        gst_segment_set_seek (&seeksegment, rate, seek_format, flags,
+        gst_segment_do_seek (&seeksegment, rate, seek_format, flags,
             cur_type, cur, stop_type, stop, &update);
       }
     }
@@ -3949,22 +3520,22 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
   if (res) {
     GST_DEBUG_OBJECT (sink, "segment configured from %" G_GINT64_FORMAT
         " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
-        seeksegment.start, seeksegment.stop, seeksegment.last_stop);
+        seeksegment.start, seeksegment.stop, seeksegment.position);
 
-    /* do the seek, segment.last_stop contains the new position. */
+    /* do the seek, segment.position contains the new position. */
     res = gst_base_sink_default_do_seek (sink, &seeksegment);
   }
 
 
   if (flush) {
     GST_DEBUG_OBJECT (sink, "stop flushing upstream");
-    gst_pad_push_event (pad, gst_event_new_flush_stop ());
-    gst_base_sink_flush_stop (sink, pad);
-  } else if (res && sink->abidata.ABI.running) {
+    gst_pad_push_event (pad, gst_event_new_flush_stop (TRUE));
+    gst_base_sink_flush_stop (sink, pad, TRUE);
+  } else if (res && sink->running) {
     /* we are running the current segment and doing a non-flushing seek,
-     * close the segment first based on the last_stop. */
+     * close the segment first based on the position. */
     GST_DEBUG_OBJECT (sink, "closing running segment %" G_GINT64_FORMAT
-        " to %" G_GINT64_FORMAT, sink->segment.start, sink->segment.last_stop);
+        " to %" G_GINT64_FORMAT, sink->segment.start, sink->segment.position);
   }
 
   /* The subclass must have converted the segment to the processing format
@@ -3978,17 +3549,17 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
   /* if successful seek, we update our real segment and push
    * out the new segment. */
   if (res) {
-    memcpy (&sink->segment, &seeksegment, sizeof (GstSegment));
+    gst_segment_copy_into (&seeksegment, &sink->segment);
 
     if (sink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
       gst_element_post_message (GST_ELEMENT (sink),
           gst_message_new_segment_start (GST_OBJECT (sink),
-              sink->segment.format, sink->segment.last_stop));
+              sink->segment.format, sink->segment.position));
     }
   }
 
   sink->priv->discont = TRUE;
-  sink->abidata.ABI.running = TRUE;
+  sink->running = TRUE;
 
   GST_PAD_STREAM_UNLOCK (pad);
 
@@ -4051,7 +3622,7 @@ gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
     if (bclass->unlock)
       bclass->unlock (sink);
 
-    GST_PAD_PREROLL_LOCK (sink->sinkpad);
+    GST_BASE_SINK_PREROLL_LOCK (sink);
     /* now that we have the PREROLL lock, clear our unlock request */
     if (bclass->unlock_stop)
       bclass->unlock_stop (sink);
@@ -4066,7 +3637,7 @@ gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
       sink->playing_async = TRUE;
       priv->pending_step.need_preroll = TRUE;
       sink->need_preroll = FALSE;
-      gst_element_lost_state_full (GST_ELEMENT_CAST (sink), FALSE);
+      gst_element_lost_state (GST_ELEMENT_CAST (sink));
     } else {
       sink->priv->have_latency = TRUE;
       sink->need_preroll = FALSE;
@@ -4085,9 +3656,9 @@ gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
     if (sink->have_preroll) {
       GST_DEBUG_OBJECT (sink, "signal waiter");
       priv->step_unlock = TRUE;
-      GST_PAD_PREROLL_SIGNAL (sink->sinkpad);
+      GST_BASE_SINK_PREROLL_SIGNAL (sink);
     }
-    GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
+    GST_BASE_SINK_PREROLL_UNLOCK (sink);
   } else {
     /* update the stepinfo and make it valid */
     set_step_info (sink, current, pending, seqnum, format, amount, rate, flush,
@@ -4102,20 +3673,22 @@ gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
 static void
 gst_base_sink_loop (GstPad * pad)
 {
+  GstObject *parent;
   GstBaseSink *basesink;
   GstBuffer *buf = NULL;
   GstFlowReturn result;
   guint blocksize;
   guint64 offset;
 
-  basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
+  parent = GST_OBJECT_PARENT (pad);
+  basesink = GST_BASE_SINK (parent);
 
-  g_assert (basesink->pad_mode == GST_ACTIVATE_PULL);
+  g_assert (basesink->pad_mode == GST_PAD_MODE_PULL);
 
   if ((blocksize = basesink->priv->blocksize) == 0)
     blocksize = -1;
 
-  offset = basesink->segment.last_stop;
+  offset = basesink->segment.position;
 
   GST_DEBUG_OBJECT (basesink, "pulling %" G_GUINT64_FORMAT ", %u",
       offset, blocksize);
@@ -4127,13 +3700,13 @@ gst_base_sink_loop (GstPad * pad)
   if (G_UNLIKELY (buf == NULL))
     goto no_buffer;
 
-  offset += GST_BUFFER_SIZE (buf);
+  offset += gst_buffer_get_size (buf);
 
-  gst_segment_set_last_stop (&basesink->segment, GST_FORMAT_BYTES, offset);
+  basesink->segment.position = offset;
 
-  GST_PAD_PREROLL_LOCK (pad);
-  result = gst_base_sink_chain_unlocked (basesink, pad, _PR_IS_BUFFER, buf);
-  GST_PAD_PREROLL_UNLOCK (pad);
+  GST_BASE_SINK_PREROLL_LOCK (basesink);
+  result = gst_base_sink_chain_unlocked (basesink, pad, buf, FALSE);
+  GST_BASE_SINK_PREROLL_UNLOCK (basesink);
   if (G_UNLIKELY (result != GST_FLOW_OK))
     goto paused;
 
@@ -4145,16 +3718,16 @@ paused:
     GST_LOG_OBJECT (basesink, "pausing task, reason %s",
         gst_flow_get_name (result));
     gst_pad_pause_task (pad);
-    if (result == GST_FLOW_UNEXPECTED) {
+    if (result == GST_FLOW_EOS) {
       /* perform EOS logic */
       if (basesink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
         gst_element_post_message (GST_ELEMENT_CAST (basesink),
             gst_message_new_segment_done (GST_OBJECT_CAST (basesink),
-                basesink->segment.format, basesink->segment.last_stop));
+                basesink->segment.format, basesink->segment.position));
       } else {
-        gst_base_sink_event (pad, gst_event_new_eos ());
+        gst_base_sink_event (pad, parent, gst_event_new_eos ());
       }
-    } else if (result == GST_FLOW_NOT_LINKED || result <= GST_FLOW_UNEXPECTED) {
+    } else if (result == GST_FLOW_NOT_LINKED || result <= GST_FLOW_EOS) {
       /* for fatal errors we post an error message, post the error
        * first so the app knows about the error first. 
        * wrong-state is not a fatal error because it happens due to
@@ -4164,7 +3737,7 @@ paused:
       GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
           (_("Internal data stream error.")),
           ("stream stopped, reason %s", gst_flow_get_name (result)));
-      gst_base_sink_event (pad, gst_event_new_eos ());
+      gst_base_sink_event (pad, parent, gst_event_new_eos ());
     }
     return;
   }
@@ -4193,7 +3766,7 @@ gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad,
       bclass->unlock (basesink);
   }
 
-  GST_PAD_PREROLL_LOCK (pad);
+  GST_BASE_SINK_PREROLL_LOCK (basesink);
   basesink->flushing = flushing;
   if (flushing) {
     /* step 1, now that we have the PREROLL lock, clear our unlock request */
@@ -4213,9 +3786,22 @@ gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad,
      * also flush out the EOS state */
     GST_DEBUG_OBJECT (basesink,
         "flushing out data thread, need preroll to TRUE");
-    gst_base_sink_preroll_queue_flush (basesink, pad);
+
+    /* we can't have EOS anymore now */
+    basesink->eos = FALSE;
+    basesink->priv->received_eos = FALSE;
+    basesink->have_preroll = FALSE;
+    basesink->priv->step_unlock = FALSE;
+    /* can't report latency anymore until we preroll again */
+    if (basesink->priv->async_enabled) {
+      GST_OBJECT_LOCK (basesink);
+      basesink->priv->have_latency = FALSE;
+      GST_OBJECT_UNLOCK (basesink);
+    }
+    /* and signal any waiters now */
+    GST_BASE_SINK_PREROLL_SIGNAL (basesink);
   }
-  GST_PAD_PREROLL_UNLOCK (pad);
+  GST_BASE_SINK_PREROLL_UNLOCK (basesink);
 
   return TRUE;
 }
@@ -4238,12 +3824,14 @@ gst_base_sink_default_activate_pull (GstBaseSink * basesink, gboolean active)
 }
 
 static gboolean
-gst_base_sink_pad_activate (GstPad * pad)
+gst_base_sink_pad_activate (GstPad * pad, GstObject * parent)
 {
   gboolean result = FALSE;
   GstBaseSink *basesink;
+  GstQuery *query;
+  gboolean pull_mode;
 
-  basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
+  basesink = GST_BASE_SINK (parent);
 
   GST_DEBUG_OBJECT (basesink, "Trying pull mode first");
 
@@ -4256,7 +3844,19 @@ gst_base_sink_pad_activate (GstPad * pad)
   }
 
   /* check if downstreams supports pull mode at all */
-  if (!gst_pad_check_pull_range (pad)) {
+  query = gst_query_new_scheduling ();
+
+  if (!gst_pad_peer_query (pad, query)) {
+    gst_query_unref (query);
+    GST_DEBUG_OBJECT (basesink, "peer query faild, no pull mode");
+    goto fallback;
+  }
+
+  /* parse result of the query */
+  pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
+  gst_query_unref (query);
+
+  if (!pull_mode) {
     GST_DEBUG_OBJECT (basesink, "pull mode not supported");
     goto fallback;
   }
@@ -4264,7 +3864,7 @@ gst_base_sink_pad_activate (GstPad * pad)
   /* set the pad mode before starting the task so that it's in the
    * correct state for the new thread. also the sink set_caps and get_caps
    * function checks this */
-  basesink->pad_mode = GST_ACTIVATE_PULL;
+  basesink->pad_mode = GST_PAD_MODE_PULL;
 
   /* we first try to negotiate a format so that when we try to activate
    * downstream, it knows about our format */
@@ -4274,10 +3874,10 @@ gst_base_sink_pad_activate (GstPad * pad)
   }
 
   /* ok activate now */
-  if (!gst_pad_activate_pull (pad, TRUE)) {
+  if (!gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE)) {
     /* clear any pending caps */
     GST_OBJECT_LOCK (basesink);
-    gst_caps_replace (&basesink->priv->pull_caps, NULL);
+    gst_caps_replace (&basesink->priv->caps, NULL);
     GST_OBJECT_UNLOCK (basesink);
     GST_DEBUG_OBJECT (basesink, "failed to activate in pull mode");
     goto fallback;
@@ -4290,7 +3890,7 @@ gst_base_sink_pad_activate (GstPad * pad)
   /* push mode fallback */
 fallback:
   GST_DEBUG_OBJECT (basesink, "Falling back to push mode");
-  if ((result = gst_pad_activate_push (pad, TRUE))) {
+  if ((result = gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE))) {
     GST_DEBUG_OBJECT (basesink, "Success activating push mode");
   }
 
@@ -4300,40 +3900,37 @@ done:
     gst_base_sink_set_flushing (basesink, pad, TRUE);
   }
 
-  gst_object_unref (basesink);
-
   return result;
 }
 
 static gboolean
-gst_base_sink_pad_activate_push (GstPad * pad, gboolean active)
+gst_base_sink_pad_activate_push (GstPad * pad, GstObject * parent,
+    gboolean active)
 {
   gboolean result;
   GstBaseSink *basesink;
 
-  basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
+  basesink = GST_BASE_SINK (parent);
 
   if (active) {
     if (!basesink->can_activate_push) {
       result = FALSE;
-      basesink->pad_mode = GST_ACTIVATE_NONE;
+      basesink->pad_mode = GST_PAD_MODE_NONE;
     } else {
       result = TRUE;
-      basesink->pad_mode = GST_ACTIVATE_PUSH;
+      basesink->pad_mode = GST_PAD_MODE_PUSH;
     }
   } else {
-    if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PUSH)) {
+    if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PUSH)) {
       g_warning ("Internal GStreamer activation error!!!");
       result = FALSE;
     } else {
       gst_base_sink_set_flushing (basesink, pad, TRUE);
       result = TRUE;
-      basesink->pad_mode = GST_ACTIVATE_NONE;
+      basesink->pad_mode = GST_PAD_MODE_NONE;
     }
   }
 
-  gst_object_unref (basesink);
-
   return result;
 }
 
@@ -4354,29 +3951,23 @@ gst_base_sink_negotiate_pull (GstBaseSink * basesink)
 
   GST_DEBUG_OBJECT (basesink, "allowed caps: %" GST_PTR_FORMAT, caps);
 
-  caps = gst_caps_make_writable (caps);
-  /* get the first (preferred) format */
-  gst_caps_truncate (caps);
-  /* try to fixate */
-  gst_pad_fixate_caps (GST_BASE_SINK_PAD (basesink), caps);
-
-  GST_DEBUG_OBJECT (basesink, "fixated to: %" GST_PTR_FORMAT, caps);
-
   if (gst_caps_is_any (caps)) {
     GST_DEBUG_OBJECT (basesink, "caps were ANY after fixating, "
         "allowing pull()");
     /* neither side has template caps in this case, so they are prepared for
        pull() without setcaps() */
     result = TRUE;
-  } else if (gst_caps_is_fixed (caps)) {
-    if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps))
-      goto could_not_set_caps;
+  } else {
+    /* try to fixate */
+    caps = gst_base_sink_fixate (basesink, caps);
+    GST_DEBUG_OBJECT (basesink, "fixated to: %" GST_PTR_FORMAT, caps);
 
-    GST_OBJECT_LOCK (basesink);
-    gst_caps_replace (&basesink->priv->pull_caps, caps);
-    GST_OBJECT_UNLOCK (basesink);
+    if (gst_caps_is_fixed (caps)) {
+      if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps))
+        goto could_not_set_caps;
 
-    result = TRUE;
+      result = TRUE;
+    }
   }
 
   gst_caps_unref (caps);
@@ -4401,38 +3992,33 @@ could_not_set_caps:
 
 /* this won't get called until we implement an activate function */
 static gboolean
-gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active)
+gst_base_sink_pad_activate_pull (GstPad * pad, GstObject * parent,
+    gboolean active)
 {
   gboolean result = FALSE;
   GstBaseSink *basesink;
   GstBaseSinkClass *bclass;
 
-  basesink = GST_BASE_SINK (gst_pad_get_parent (pad));
+  basesink = GST_BASE_SINK (parent);
   bclass = GST_BASE_SINK_GET_CLASS (basesink);
 
   if (active) {
-    GstFormat format;
     gint64 duration;
 
     /* we mark we have a newsegment here because pull based
      * mode works just fine without having a newsegment before the
      * first buffer */
-    format = GST_FORMAT_BYTES;
-
-    gst_segment_init (&basesink->segment, format);
-    gst_segment_init (basesink->abidata.ABI.clip_segment, format);
+    gst_segment_init (&basesink->segment, GST_FORMAT_BYTES);
     GST_OBJECT_LOCK (basesink);
     basesink->have_newsegment = TRUE;
     GST_OBJECT_UNLOCK (basesink);
 
     /* get the peer duration in bytes */
-    result = gst_pad_query_peer_duration (pad, &format, &duration);
+    result = gst_pad_peer_query_duration (pad, GST_FORMAT_BYTES, &duration);
     if (result) {
       GST_DEBUG_OBJECT (basesink,
           "setting duration in bytes to %" G_GINT64_FORMAT, duration);
-      gst_segment_set_duration (basesink->abidata.ABI.clip_segment, format,
-          duration);
-      gst_segment_set_duration (&basesink->segment, format, duration);
+      basesink->segment.duration = duration;
     } else {
       GST_DEBUG_OBJECT (basesink, "unknown duration");
     }
@@ -4446,21 +4032,16 @@ gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active)
       goto activate_failed;
 
   } else {
-    if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PULL)) {
+    if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PULL)) {
       g_warning ("Internal GStreamer activation error!!!");
       result = FALSE;
     } else {
       result = gst_base_sink_set_flushing (basesink, pad, TRUE);
       if (bclass->activate_pull)
         result &= bclass->activate_pull (basesink, FALSE);
-      basesink->pad_mode = GST_ACTIVATE_NONE;
-      /* clear any pending caps */
-      GST_OBJECT_LOCK (basesink);
-      gst_caps_replace (&basesink->priv->pull_caps, NULL);
-      GST_OBJECT_UNLOCK (basesink);
+      basesink->pad_mode = GST_PAD_MODE_NONE;
     }
   }
-  gst_object_unref (basesink);
 
   return result;
 
@@ -4468,13 +4049,34 @@ gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active)
 activate_failed:
   {
     /* reset, as starting the thread failed */
-    basesink->pad_mode = GST_ACTIVATE_NONE;
+    basesink->pad_mode = GST_PAD_MODE_NONE;
 
     GST_ERROR_OBJECT (basesink, "subclass failed to activate in pull mode");
     return FALSE;
   }
 }
 
+static gboolean
+gst_base_sink_pad_activate_mode (GstPad * pad, GstObject * parent,
+    GstPadMode mode, gboolean active)
+{
+  gboolean res;
+
+  switch (mode) {
+    case GST_PAD_MODE_PULL:
+      res = gst_base_sink_pad_activate_pull (pad, parent, active);
+      break;
+    case GST_PAD_MODE_PUSH:
+      res = gst_base_sink_pad_activate_push (pad, parent, active);
+      break;
+    default:
+      GST_LOG_OBJECT (pad, "unknown activation mode %d", mode);
+      res = FALSE;
+      break;
+  }
+  return res;
+}
+
 /* send an event to our sinkpad peer. */
 static gboolean
 gst_base_sink_send_event (GstElement * element, GstEvent * event)
@@ -4482,7 +4084,7 @@ gst_base_sink_send_event (GstElement * element, GstEvent * event)
   GstPad *pad;
   GstBaseSink *basesink = GST_BASE_SINK (element);
   gboolean forward, result = TRUE;
-  GstActivateMode mode;
+  GstPadMode mode;
 
   GST_OBJECT_LOCK (element);
   /* get the pad and the scheduling mode */
@@ -4520,7 +4122,7 @@ gst_base_sink_send_event (GstElement * element, GstEvent * event)
     }
     case GST_EVENT_SEEK:
       /* in pull mode we will execute the seek */
-      if (mode == GST_ACTIVATE_PULL)
+      if (mode == GST_PAD_MODE_PULL)
         result = gst_base_sink_perform_seek (basesink, pad, event);
       break;
     case GST_EVENT_STEP:
@@ -4552,11 +4154,11 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
 {
   GstClock *clock = NULL;
   gboolean res = FALSE;
-  GstFormat oformat, tformat;
+  GstFormat oformat;
   GstSegment *segment;
   GstClockTime now, latency;
-  GstClockTimeDiff base;
-  gint64 time, accum, duration;
+  GstClockTimeDiff base_time;
+  gint64 time, base, duration;
   gdouble rate;
   gint64 last;
   gboolean last_seen, with_clock, in_paused;
@@ -4575,16 +4177,8 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
     in_paused = TRUE;
   }
 
-  /* we don't use the clip segment in pull mode, when seeking we update the
-   * main segment directly with the new segment values without it having to be
-   * activated by the rendering after preroll */
-  if (basesink->pad_mode == GST_ACTIVATE_PUSH)
-    segment = basesink->abidata.ABI.clip_segment;
-  else
-    segment = &basesink->segment;
+  segment = &basesink->segment;
 
-  /* our intermediate time format */
-  tformat = GST_FORMAT_TIME;
   /* get the format in the segment */
   oformat = segment->format;
 
@@ -4621,7 +4215,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
   else
     duration = 0;
 
-  accum = segment->accum;
+  base = segment->base;
   rate = segment->rate * segment->applied_rate;
   latency = basesink->priv->latency;
 
@@ -4646,28 +4240,28 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
     }
   } else {
     /* convert last stop to stream time */
-    last = gst_segment_to_stream_time (segment, oformat, segment->last_stop);
+    last = gst_segment_to_stream_time (segment, oformat, segment->position);
   }
 
   if (in_paused) {
     /* in paused, use start_time */
-    base = GST_ELEMENT_START_TIME (basesink);
+    base_time = GST_ELEMENT_START_TIME (basesink);
     GST_DEBUG_OBJECT (basesink, "in paused, using start time %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (base));
+        GST_TIME_ARGS (base_time));
   } else if (with_clock) {
     /* else use clock when needed */
-    base = GST_ELEMENT_CAST (basesink)->base_time;
+    base_time = GST_ELEMENT_CAST (basesink)->base_time;
     GST_DEBUG_OBJECT (basesink, "using clock and base time %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (base));
+        GST_TIME_ARGS (base_time));
   } else {
     /* else, no sync or clock -> no base time */
     GST_DEBUG_OBJECT (basesink, "no sync or no clock");
-    base = -1;
+    base_time = -1;
   }
 
-  /* no base, we can't calculate running_time, use last seem timestamp to report
+  /* no base_time, we can't calculate running_time, use last seem timestamp to report
    * time */
-  if (base == -1)
+  if (base_time == -1)
     last_seen = TRUE;
 
   /* need to release the object lock before we can get the time,
@@ -4689,47 +4283,47 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
         GST_TIME_ARGS (last));
     *cur = last;
   } else {
-    if (oformat != tformat) {
-      /* convert accum, time and duration to time */
-      if (!gst_pad_query_convert (basesink->sinkpad, oformat, accum, &tformat,
-              &accum))
+    if (oformat != GST_FORMAT_TIME) {
+      /* convert base, time and duration to time */
+      if (!gst_pad_query_convert (basesink->sinkpad, oformat, base,
+              GST_FORMAT_TIME, &base))
         goto convert_failed;
       if (!gst_pad_query_convert (basesink->sinkpad, oformat, duration,
-              &tformat, &duration))
+              GST_FORMAT_TIME, &duration))
         goto convert_failed;
-      if (!gst_pad_query_convert (basesink->sinkpad, oformat, time, &tformat,
-              &time))
+      if (!gst_pad_query_convert (basesink->sinkpad, oformat, time,
+              GST_FORMAT_TIME, &time))
         goto convert_failed;
-      if (!gst_pad_query_convert (basesink->sinkpad, oformat, last, &tformat,
-              &last))
+      if (!gst_pad_query_convert (basesink->sinkpad, oformat, last,
+              GST_FORMAT_TIME, &last))
         goto convert_failed;
 
       /* assume time format from now on */
-      oformat = tformat;
+      oformat = GST_FORMAT_TIME;
     }
 
     if (!in_paused && with_clock) {
       now = gst_clock_get_time (clock);
     } else {
-      now = base;
-      base = 0;
+      now = base_time;
+      base_time = 0;
     }
 
-    /* subtract base time and accumulated time from the clock time.
+    /* subtract base time and base time from the clock time.
      * Make sure we don't go negative. This is the current time in
      * the segment which we need to scale with the combined
      * rate and applied rate. */
-    base += accum;
-    base += latency;
-    if (GST_CLOCK_DIFF (base, now) < 0)
-      base = now;
+    base_time += base;
+    base_time += latency;
+    if (GST_CLOCK_DIFF (base_time, now) < 0)
+      base_time = now;
 
     /* for negative rates we need to count back from the segment
      * duration. */
     if (rate < 0.0)
       time += duration;
 
-    *cur = time + gst_guint64_to_gdouble (now - base) * rate;
+    *cur = time + gst_guint64_to_gdouble (now - base_time) * rate;
 
     if (in_paused) {
       /* never report less than segment values in paused */
@@ -4742,15 +4336,15 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
     }
 
     GST_DEBUG_OBJECT (basesink,
-        "now %" GST_TIME_FORMAT " - base %" GST_TIME_FORMAT " - accum %"
+        "now %" GST_TIME_FORMAT " - base_time %" GST_TIME_FORMAT " - base %"
         GST_TIME_FORMAT " + time %" GST_TIME_FORMAT "  last %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (now), GST_TIME_ARGS (base), GST_TIME_ARGS (accum),
+        GST_TIME_ARGS (now), GST_TIME_ARGS (base_time), GST_TIME_ARGS (base),
         GST_TIME_ARGS (time), GST_TIME_ARGS (last));
   }
 
   if (oformat != format) {
     /* convert to final format */
-    if (!gst_pad_query_convert (basesink->sinkpad, oformat, *cur, &format, cur))
+    if (!gst_pad_query_convert (basesink->sinkpad, oformat, *cur, format, cur))
       goto convert_failed;
   }
 
@@ -4790,21 +4384,23 @@ gst_base_sink_get_duration (GstBaseSink * basesink, GstFormat format,
 {
   gboolean res = FALSE;
 
-  if (basesink->pad_mode == GST_ACTIVATE_PULL) {
-    GstFormat uformat = GST_FORMAT_BYTES;
+  if (basesink->pad_mode == GST_PAD_MODE_PULL) {
     gint64 uduration;
 
     /* get the duration in bytes, in pull mode that's all we are sure to
      * know. We have to explicitly get this value from upstream instead of
      * using our cached value because it might change. Duration caching
      * should be done at a higher level. */
-    res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat, &uduration);
+    res =
+        gst_pad_peer_query_duration (basesink->sinkpad, GST_FORMAT_BYTES,
+        &uduration);
     if (res) {
-      gst_segment_set_duration (&basesink->segment, uformat, uduration);
-      if (format != uformat) {
+      basesink->segment.duration = uduration;
+      if (format != GST_FORMAT_BYTES) {
         /* convert to the requested format */
-        res = gst_pad_query_convert (basesink->sinkpad, uformat, uduration,
-            &format, dur);
+        res =
+            gst_pad_query_convert (basesink->sinkpad, GST_FORMAT_BYTES,
+            uduration, format, dur);
       } else {
         *dur = uduration;
       }
@@ -4817,20 +4413,6 @@ gst_base_sink_get_duration (GstBaseSink * basesink, GstFormat format,
   return res;
 }
 
-static const GstQueryType *
-gst_base_sink_get_query_types (GstElement * element)
-{
-  static const GstQueryType query_types[] = {
-    GST_QUERY_DURATION,
-    GST_QUERY_POSITION,
-    GST_QUERY_SEGMENT,
-    GST_QUERY_LATENCY,
-    0
-  };
-
-  return query_types;
-}
-
 static gboolean
 default_element_query (GstElement * element, GstQuery * query)
 {
@@ -4862,20 +4444,21 @@ default_element_query (GstElement * element, GstQuery * query)
         /* we can handle a few things if upstream failed */
         if (format == GST_FORMAT_PERCENT) {
           gint64 dur = 0;
-          GstFormat uformat = GST_FORMAT_TIME;
 
           res = gst_base_sink_get_position (basesink, GST_FORMAT_TIME, &cur,
               &upstream);
           if (!res && upstream) {
-            res = gst_pad_query_peer_position (basesink->sinkpad, &uformat,
+            res =
+                gst_pad_peer_query_position (basesink->sinkpad, GST_FORMAT_TIME,
                 &cur);
           }
           if (res) {
             res = gst_base_sink_get_duration (basesink, GST_FORMAT_TIME, &dur,
                 &upstream);
             if (!res && upstream) {
-              res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat,
-                  &dur);
+              res =
+                  gst_pad_peer_query_duration (basesink->sinkpad,
+                  GST_FORMAT_TIME, &dur);
             }
           }
           if (res) {
@@ -4936,7 +4519,7 @@ default_element_query (GstElement * element, GstQuery * query)
       break;
     case GST_QUERY_SEGMENT:
     {
-      if (basesink->pad_mode == GST_ACTIVATE_PULL) {
+      if (basesink->pad_mode == GST_PAD_MODE_PULL) {
         gst_query_set_segment (query, basesink->segment.rate,
             GST_FORMAT_TIME, basesink->segment.start, basesink->segment.stop);
         res = TRUE;
@@ -4957,25 +4540,69 @@ default_element_query (GstElement * element, GstQuery * query)
   return res;
 }
 
+
 static gboolean
-default_sink_query (GstBaseSink * basesink, GstQuery * query)
+gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
 {
-  return gst_pad_query_default (basesink->sinkpad, query);
+  gboolean res;
+  GstBaseSinkClass *bclass;
+
+  bclass = GST_BASE_SINK_GET_CLASS (basesink);
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_ALLOCATION:
+    {
+      if (bclass->propose_allocation)
+        res = bclass->propose_allocation (basesink, query);
+      else
+        res = FALSE;
+      break;
+    }
+    case GST_QUERY_CAPS:
+    {
+      GstCaps *caps, *filter;
+
+      gst_query_parse_caps (query, &filter);
+      caps = gst_base_sink_query_caps (basesink, basesink->sinkpad, filter);
+      gst_query_set_caps_result (query, caps);
+      gst_caps_unref (caps);
+      res = TRUE;
+      break;
+    }
+    case GST_QUERY_ACCEPT_CAPS:
+    {
+      GstCaps *caps, *allowed;
+      gboolean subset;
+
+      /* slightly faster than the default implementation */
+      gst_query_parse_accept_caps (query, &caps);
+      allowed = gst_base_sink_query_caps (basesink, basesink->sinkpad, NULL);
+      subset = gst_caps_is_subset (caps, allowed);
+      gst_caps_unref (allowed);
+      gst_query_set_accept_caps_result (query, subset);
+      res = TRUE;
+      break;
+    }
+    case GST_QUERY_DRAIN:
+      res = TRUE;
+      break;
+    default:
+      res =
+          gst_pad_query_default (basesink->sinkpad, GST_OBJECT_CAST (basesink),
+          query);
+      break;
+  }
+  return res;
 }
 
 static gboolean
-gst_base_sink_sink_query (GstPad * pad, GstQuery * query)
+gst_base_sink_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
 {
   GstBaseSink *basesink;
   GstBaseSinkClass *bclass;
   gboolean res;
 
-  basesink = GST_BASE_SINK_CAST (gst_pad_get_parent (pad));
-  if (G_UNLIKELY (basesink == NULL)) {
-    gst_query_unref (query);
-    return FALSE;
-  }
-
+  basesink = GST_BASE_SINK_CAST (parent);
   bclass = GST_BASE_SINK_GET_CLASS (basesink);
 
   if (bclass->query)
@@ -4983,8 +4610,6 @@ gst_base_sink_sink_query (GstPad * pad, GstQuery * query)
   else
     res = FALSE;
 
-  gst_object_unref (basesink);
-
   return res;
 }
 
@@ -5009,12 +4634,10 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
     case GST_STATE_CHANGE_READY_TO_PAUSED:
       /* need to complete preroll before this state change completes, there
        * is no data flow in READY so we can safely assume we need to preroll. */
-      GST_PAD_PREROLL_LOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_LOCK (basesink);
       GST_DEBUG_OBJECT (basesink, "READY to PAUSED");
       basesink->have_newsegment = FALSE;
       gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
-      gst_segment_init (basesink->abidata.ABI.clip_segment,
-          GST_FORMAT_UNDEFINED);
       basesink->offset = 0;
       basesink->have_preroll = FALSE;
       priv->step_unlock = FALSE;
@@ -5037,14 +4660,14 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
          * the state change function */
         ret = GST_STATE_CHANGE_ASYNC;
         gst_element_post_message (GST_ELEMENT_CAST (basesink),
-            gst_message_new_async_start (GST_OBJECT_CAST (basesink), FALSE));
+            gst_message_new_async_start (GST_OBJECT_CAST (basesink)));
       } else {
         priv->have_latency = TRUE;
       }
-      GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_UNLOCK (basesink);
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-      GST_PAD_PREROLL_LOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_LOCK (basesink);
       g_atomic_int_set (&basesink->priv->to_playing, TRUE);
       if (!gst_base_sink_needs_preroll (basesink)) {
         GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, don't need preroll");
@@ -5061,7 +4684,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
           gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
         } else {
           GST_DEBUG_OBJECT (basesink, "signal preroll");
-          GST_PAD_PREROLL_SIGNAL (basesink->sinkpad);
+          GST_BASE_SINK_PREROLL_SIGNAL (basesink);
         }
       } else {
         GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, we are not prerolled");
@@ -5073,10 +4696,10 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
           GST_DEBUG_OBJECT (basesink, "doing async state change");
           ret = GST_STATE_CHANGE_ASYNC;
           gst_element_post_message (GST_ELEMENT_CAST (basesink),
-              gst_message_new_async_start (GST_OBJECT_CAST (basesink), FALSE));
+              gst_message_new_async_start (GST_OBJECT_CAST (basesink)));
         }
       }
-      GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_UNLOCK (basesink);
       break;
     default:
       break;
@@ -5107,7 +4730,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       if (bclass->unlock)
         bclass->unlock (basesink);
 
-      GST_PAD_PREROLL_LOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_LOCK (basesink);
       GST_DEBUG_OBJECT (basesink, "got preroll lock");
       /* now that we have the PREROLL lock, clear our unlock request */
       if (bclass->unlock_stop)
@@ -5142,8 +4765,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
             GST_DEBUG_OBJECT (basesink, "doing async state change");
             ret = GST_STATE_CHANGE_ASYNC;
             gst_element_post_message (GST_ELEMENT_CAST (basesink),
-                gst_message_new_async_start (GST_OBJECT_CAST (basesink),
-                    FALSE));
+                gst_message_new_async_start (GST_OBJECT_CAST (basesink)));
           }
         }
       }
@@ -5151,10 +4773,10 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
           ", dropped: %" G_GUINT64_FORMAT, priv->rendered, priv->dropped);
 
       gst_base_sink_reset_qos (basesink);
-      GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_UNLOCK (basesink);
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
-      GST_PAD_PREROLL_LOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_LOCK (basesink);
       /* start by resetting our position state with the object lock so that the
        * position query gets the right idea. We do this before we post the
        * messages so that the message handlers pick this up. */
@@ -5167,6 +4789,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
         gst_clock_id_unref (priv->cached_clock_id);
         priv->cached_clock_id = NULL;
       }
+      gst_caps_replace (&basesink->priv->caps, NULL);
       GST_OBJECT_UNLOCK (basesink);
 
       gst_base_sink_set_last_buffer (basesink, NULL);
@@ -5181,13 +4804,13 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
                   GST_STATE_PLAYING, GST_STATE_PAUSED, GST_STATE_READY));
 
           gst_element_post_message (GST_ELEMENT_CAST (basesink),
-              gst_message_new_async_done (GST_OBJECT_CAST (basesink)));
+              gst_message_new_async_done (GST_OBJECT_CAST (basesink), FALSE));
         }
         priv->commited = TRUE;
       } else {
         GST_DEBUG_OBJECT (basesink, "PAUSED to READY, don't need_preroll");
       }
-      GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
+      GST_BASE_SINK_PREROLL_UNLOCK (basesink);
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
       if (bclass->stop) {