pipeline: use reset_time message to reset the start time
[platform/upstream/gstreamer.git] / libs / gst / base / gstbasesink.c
index 198cbe7..7ddadb9 100644 (file)
 #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);
@@ -262,8 +261,6 @@ struct _GstBaseSinkPrivate
   /* for throttling and QoS */
   GstClockTime earliest_in_time;
   GstClockTime throttle_time;
-
-  gboolean reset_time;
 };
 
 #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
@@ -277,19 +274,6 @@ 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 */
@@ -362,10 +346,12 @@ static gboolean gst_base_sink_send_event (GstElement * element,
     GstEvent * event);
 static gboolean default_element_query (GstElement * element, GstQuery * query);
 
-static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink, GstCaps * caps);
-static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
-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,
@@ -396,18 +382,18 @@ static GstFlowReturn gst_base_sink_default_wait_eos (GstBaseSink * basesink,
 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 void gst_base_sink_default_fixate (GstBaseSink * bsink, GstCaps * caps);
-static void gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
+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)
@@ -535,7 +521,7 @@ 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 =
@@ -543,13 +529,13 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
   gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
   gstelement_class->query = GST_DEBUG_FUNCPTR (default_element_query);
 
-  klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
-  klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
+  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->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times);
-  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);
 
@@ -606,14 +592,14 @@ gst_base_sink_query_caps (GstBaseSink * bsink, GstPad * pad, GstCaps * filter)
   return caps;
 }
 
-static void
+static GstCaps *
 gst_base_sink_default_fixate (GstBaseSink * bsink, GstCaps * caps)
 {
   GST_DEBUG_OBJECT (bsink, "using default caps fixate function");
-  gst_caps_fixate (caps);
+  return gst_caps_fixate (caps);
 }
 
-static void
+static GstCaps *
 gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
 {
   GstBaseSinkClass *bclass;
@@ -621,7 +607,9 @@ gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
   bclass = GST_BASE_SINK_GET_CLASS (bsink);
 
   if (bclass->fixate)
-    bclass->fixate (bsink, caps);
+    caps = bclass->fixate (bsink, caps);
+
+  return caps;
 }
 
 static void
@@ -648,8 +636,8 @@ gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
   gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad);
 
   basesink->pad_mode = GST_PAD_MODE_NONE;
-  basesink->preroll_lock = g_mutex_new ();
-  basesink->preroll_cond = g_cond_new ();
+  g_mutex_init (&basesink->preroll_lock);
+  g_cond_init (&basesink->preroll_cond);
   priv->have_latency = FALSE;
 
   basesink->can_activate_push = DEFAULT_CAN_ACTIVATE_PUSH;
@@ -676,8 +664,8 @@ gst_base_sink_finalize (GObject * object)
 
   basesink = GST_BASE_SINK (object);
 
-  g_mutex_free (basesink->preroll_lock);
-  g_cond_free (basesink->preroll_cond);
+  g_mutex_clear (&basesink->preroll_lock);
+  g_cond_clear (&basesink->preroll_cond);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -1398,13 +1386,13 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
 
 
 static GstCaps *
-gst_base_sink_get_caps (GstBaseSink * sink, GstCaps * filter)
+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;
 }
@@ -1418,7 +1406,6 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
   gboolean post_paused = FALSE;
   gboolean post_async_done = FALSE;
   gboolean post_playing = FALSE;
-  gboolean reset_time;
 
   /* we are certainly not playing async anymore now */
   basesink->playing_async = FALSE;
@@ -1428,8 +1415,6 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
   next = GST_STATE_NEXT (basesink);
   pending = GST_STATE_PENDING (basesink);
   post_pending = pending;
-  reset_time = basesink->priv->reset_time;
-  basesink->priv->reset_time = FALSE;
 
   switch (pending) {
     case GST_STATE_PLAYING:
@@ -1480,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), reset_time));
+        gst_message_new_async_done (GST_OBJECT_CAST (basesink), FALSE));
   }
   if (post_playing) {
     GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
@@ -1756,8 +1741,7 @@ 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, GstStepInfo * step,
-    gboolean * step_end, guint8 obj_type)
+    gboolean * stepped, GstStepInfo * step, gboolean * step_end)
 {
   GstBaseSinkClass *bclass;
   GstBuffer *buffer;
@@ -1776,7 +1760,7 @@ gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
   /* 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)) {
@@ -1830,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;
@@ -2074,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.
@@ -2104,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:
   {
@@ -2114,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
@@ -2154,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)));
 
-    ret = gst_base_sink_preroll_object (sink, obj_type, obj);
-    if (ret != GST_FLOW_OK)
-      goto preroll_failed;
+        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;
+      }
+    }
+
+    /* 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 */
@@ -2175,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));
@@ -2225,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
@@ -2255,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;
   }
 }
 
@@ -2283,7 +2298,7 @@ flushing:
  */
 static GstFlowReturn
 gst_base_sink_do_sync (GstBaseSink * basesink,
-    GstMiniObject * obj, gboolean * late, gboolean * step_end, guint8 obj_type)
+    GstMiniObject * obj, gboolean * late, gboolean * step_end)
 {
   GstClockTimeDiff jitter = 0;
   gboolean syncable;
@@ -2310,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,
-      current, step_end, obj_type);
+      &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, current, step_end);
 
   if (G_UNLIKELY (stepped))
     goto step_skipped;
@@ -2377,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,
@@ -2444,7 +2450,7 @@ qos_dropped:
 flushing:
   {
     GST_DEBUG_OBJECT (basesink, "we are flushing");
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 preroll_failed:
   {
@@ -2763,83 +2769,6 @@ 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.
- */
-
-/* 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;
-
-    if (OBJ_IS_BUFFERLIST (obj_type)) {
-      buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
-      g_assert (NULL != buf);
-    } else {
-      buf = GST_BUFFER_CAST (obj);
-    }
-
-    GST_DEBUG_OBJECT (basesink, "preroll buffer %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
-
-    /*
-     * 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;
-  }
-}
-
 static void
 gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
 {
@@ -2890,8 +2819,13 @@ gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad,
       gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
     }
   }
-  basesink->priv->reset_time = reset_time;
   GST_OBJECT_UNLOCK (basesink);
+
+  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));
+  }
 }
 
 static GstFlowReturn
@@ -2901,7 +2835,7 @@ gst_base_sink_default_wait_eos (GstBaseSink * basesink, GstEvent * event)
   gboolean late, step_end;
 
   ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (event),
-      &late, &step_end, _PR_IS_EVENT);
+      &late, &step_end);
 
   return ret;
 }
@@ -3096,14 +3030,17 @@ after_eos:
  * 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)) {
@@ -3142,7 +3079,7 @@ gst_base_sink_needs_preroll (GstBaseSink * basesink)
  */
 static GstFlowReturn
 gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
-    guint8 obj_type, gpointer obj)
+    gpointer obj, gboolean is_list)
 {
   GstBaseSinkClass *bclass;
   GstBaseSinkPrivate *priv = basesink->priv;
@@ -3159,7 +3096,7 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
   if (G_UNLIKELY (priv->received_eos))
     goto was_eos;
 
-  if (OBJ_IS_BUFFERLIST (obj_type)) {
+  if (is_list) {
     sync_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
     g_assert (NULL != sync_buf);
   } else {
@@ -3199,7 +3136,7 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
   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, sync_buf, &start, &end);
+    gst_base_sink_default_get_times (basesink, sync_buf, &start, &end);
   }
 
   GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
@@ -3212,6 +3149,20 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
       goto out_of_segment;
   }
 
+  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;
@@ -3219,7 +3170,7 @@ again:
   /* 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, obj_type);
+      &late, &step_end);
   if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto sync_failed;
 
@@ -3236,10 +3187,8 @@ again:
   if (do_qos)
     gst_base_sink_do_render_stats (basesink, TRUE);
 
-  if (!OBJ_IS_BUFFERLIST (obj_type)) {
-    /* 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 (!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)
@@ -3281,7 +3230,7 @@ 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:
   {
@@ -3295,6 +3244,13 @@ 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));
@@ -3338,8 +3294,8 @@ dropped:
 /* 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;
 
@@ -3347,7 +3303,7 @@ gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,
     goto wrong_mode;
 
   GST_BASE_SINK_PREROLL_LOCK (basesink);
-  result = gst_base_sink_chain_unlocked (basesink, pad, obj_type, obj);
+  result = gst_base_sink_chain_unlocked (basesink, pad, obj, is_list);
   GST_BASE_SINK_PREROLL_UNLOCK (basesink);
 
 done:
@@ -3376,7 +3332,7 @@ gst_base_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
 
   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
@@ -3391,7 +3347,7 @@ gst_base_sink_chain_list (GstPad * pad, GstObject * 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 {
     guint i, len;
     GstBuffer *buffer;
@@ -3402,9 +3358,9 @@ gst_base_sink_chain_list (GstPad * pad, GstObject * parent,
 
     result = GST_FLOW_OK;
     for (i = 0; i < len; i++) {
-      buffer = gst_buffer_list_get (list, 0);
-      result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER,
-          gst_buffer_ref (buffer));
+      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;
     }
@@ -3749,7 +3705,7 @@ gst_base_sink_loop (GstPad * pad)
   basesink->segment.position = offset;
 
   GST_BASE_SINK_PREROLL_LOCK (basesink);
-  result = gst_base_sink_chain_unlocked (basesink, pad, _PR_IS_BUFFER, buf);
+  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;
@@ -4002,14 +3958,12 @@ gst_base_sink_negotiate_pull (GstBaseSink * basesink)
        pull() without setcaps() */
     result = TRUE;
   } else {
-    caps = gst_caps_make_writable (caps);
     /* try to fixate */
-    gst_base_sink_fixate (basesink, caps);
+    caps = gst_base_sink_fixate (basesink, caps);
     GST_DEBUG_OBJECT (basesink, "fixated to: %" GST_PTR_FORMAT, caps);
 
     if (gst_caps_is_fixed (caps)) {
-      if (!gst_pad_send_event (GST_BASE_SINK_PAD (basesink),
-              gst_event_new_caps (caps)))
+      if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps))
         goto could_not_set_caps;
 
       result = TRUE;
@@ -4588,7 +4542,7 @@ default_element_query (GstElement * element, GstQuery * query)
 
 
 static gboolean
-default_sink_query (GstBaseSink * basesink, GstQuery * query)
+gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
 {
   gboolean res;
   GstBaseSinkClass *bclass;
@@ -4615,6 +4569,23 @@ default_sink_query (GstBaseSink * basesink, GstQuery * query)
       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),
@@ -4672,7 +4643,6 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       priv->step_unlock = FALSE;
       basesink->need_preroll = TRUE;
       basesink->playing_async = TRUE;
-      basesink->priv->reset_time = FALSE;
       priv->current_sstart = GST_CLOCK_TIME_NONE;
       priv->current_sstop = GST_CLOCK_TIME_NONE;
       priv->eos_rtime = GST_CLOCK_TIME_NONE;