aggregator: Assert if the sink/src pad type that is to be used is not a GstAggregator...
[platform/upstream/gstreamer.git] / libs / gst / base / gstbasesink.c
index b6fc1fc..4368c1a 100644 (file)
@@ -21,6 +21,7 @@
 
 /**
  * SECTION:gstbasesink
+ * @title: GstBaseSink
  * @short_description: Base class for sink elements
  * @see_also: #GstBaseTransform, #GstBaseSrc
  *
@@ -37,7 +38,7 @@
  * #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 class_init function, like so:
- * |[
+ * |[<!-- language="C" -->
  * static void
  * my_element_class_init (GstMyElementClass *klass)
  * {
  *
  *   // sinktemplate should be a #GstStaticPadTemplate with direction
  *   // %GST_PAD_SINK and name "sink"
- *   gst_element_class_add_pad_template (gstelement_class,
- *       gst_static_pad_template_get (&amp;sinktemplate));
+ *   gst_element_class_add_static_pad_template (gstelement_class, &amp;sinktemplate);
  *
  *   gst_element_class_set_static_metadata (gstelement_class,
  *       "Sink name",
  *       "Sink",
  *       "My Sink element",
- *       "The author &lt;my.sink@my.email&gt;");
+ *       "The author <my.sink@my.email>");
  * }
  * ]|
  *
@@ -99,7 +99,7 @@
  * should configure itself to process a specific media type.
  *
  * The #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() virtual methods
- * will be called when resources should be allocated. Any 
+ * will be called when resources should be allocated. Any
  * #GstBaseSinkClass.preroll(), #GstBaseSinkClass.render() and
  * #GstBaseSinkClass.set_caps() function will be called between the
  * #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() calls.
 GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
 #define GST_CAT_DEFAULT gst_base_sink_debug
 
-#define GST_BASE_SINK_GET_PRIVATE(obj)  \
-   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))
-
 #define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR
 
 typedef struct
@@ -181,6 +178,7 @@ struct _GstBaseSinkPrivate
   gboolean async_enabled;
   GstClockTimeDiff ts_offset;
   GstClockTime render_delay;
+  GstClockTime processing_deadline;
 
   /* start, stop of current buffer, stream time, used to report position */
   GstClockTime current_sstart;
@@ -202,19 +200,8 @@ struct _GstBaseSinkPrivate
   GstClockTime last_left;
 
   /* running averages go here these are done on running time */
-  GstClockTime avg_pt;
-  GstClockTime avg_duration;
-  gdouble avg_rate;
-  GstClockTime avg_in_diff;
-
-  /* these are done on system time. avg_jitter and avg_render are
-   * compared to eachother to see if the rendering time takes a
-   * huge amount of the processing, If so we are flooded with
-   * buffers. */
-  GstClockTime last_left_systime;
-  GstClockTime avg_jitter;
-  GstClockTime start, stop;
-  GstClockTime avg_render;
+  GstClockTime avg_pt, avg_in_diff;
+  gdouble avg_rate;             /* average with infinite window */
 
   /* number of rendered and dropped frames */
   guint64 rendered;
@@ -223,8 +210,8 @@ struct _GstBaseSinkPrivate
   /* latency stuff */
   GstClockTime latency;
 
-  /* if we already commited the state */
-  gboolean commited;
+  /* if we already committed the state */
+  gboolean committed;
   /* state change to playing ongoing */
   gboolean to_playing;
 
@@ -238,6 +225,7 @@ struct _GstBaseSinkPrivate
   gint enable_last_sample;      /* atomic */
   GstBuffer *last_buffer;
   GstCaps *last_caps;
+  GstBufferList *last_buffer_list;
 
   /* negotiated caps */
   GstCaps *caps;
@@ -269,6 +257,8 @@ struct _GstBaseSinkPrivate
   GstClockTime rc_time;
   GstClockTime rc_next;
   gsize rc_accumulated;
+
+  gboolean drop_out_of_segment;
 };
 
 #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
@@ -297,6 +287,8 @@ struct _GstBaseSinkPrivate
 #define DEFAULT_ENABLE_LAST_SAMPLE  TRUE
 #define DEFAULT_THROTTLE_TIME       0
 #define DEFAULT_MAX_BITRATE         0
+#define DEFAULT_DROP_OUT_OF_SEGMENT TRUE
+#define DEFAULT_PROCESSING_DEADLINE (20 * GST_MSECOND)
 
 enum
 {
@@ -312,10 +304,12 @@ enum
   PROP_RENDER_DELAY,
   PROP_THROTTLE_TIME,
   PROP_MAX_BITRATE,
+  PROP_PROCESSING_DEADLINE,
   PROP_LAST
 };
 
 static GstElementClass *parent_class = NULL;
+static gint private_offset = 0;
 
 static void gst_base_sink_class_init (GstBaseSinkClass * klass);
 static void gst_base_sink_init (GstBaseSink * trans, gpointer g_class);
@@ -342,11 +336,21 @@ gst_base_sink_get_type (void)
 
     _type = g_type_register_static (GST_TYPE_ELEMENT,
         "GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT);
+
+    private_offset =
+        g_type_add_instance_private (_type, sizeof (GstBaseSinkPrivate));
+
     g_once_init_leave (&base_sink_type, _type);
   }
   return base_sink_type;
 }
 
+static inline GstBaseSinkPrivate *
+gst_base_sink_get_instance_private (GstBaseSink * self)
+{
+  return (G_STRUCT_MEMBER_P (self, private_offset));
+}
+
 static void gst_base_sink_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_base_sink_get_property (GObject * object, guint prop_id,
@@ -414,11 +418,12 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
   gobject_class = G_OBJECT_CLASS (klass);
   gstelement_class = GST_ELEMENT_CLASS (klass);
 
+  if (private_offset != 0)
+    g_type_class_adjust_private_offset (klass, &private_offset);
+
   GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0,
       "basesink element");
 
-  g_type_class_add_private (klass, sizeof (GstBaseSinkPrivate));
-
   parent_class = g_type_class_peek_parent (klass);
 
   gobject_class->finalize = gst_base_sink_finalize;
@@ -492,7 +497,7 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    *
    * The amount of bytes to pull when operating in pull mode.
    */
-  /* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
+  /* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
   g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
       g_param_spec_uint ("blocksize", "Block size",
           "Size in bytes to pull per buffer (0 = default)", 0, G_MAXUINT,
@@ -534,6 +539,20 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
           "The maximum bits per second to render (0 = disabled)", 0,
           G_MAXUINT64, DEFAULT_MAX_BITRATE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstBaseSink:processing-deadline:
+   *
+   * Maximum amount of time (in nanoseconds) that the pipeline can take
+   * for processing the buffer. This is added to the latency of live
+   * pipelines.
+   *
+   * Since: 1.16
+   */
+  g_object_class_install_property (gobject_class, PROP_PROCESSING_DEADLINE,
+      g_param_spec_uint64 ("processing-deadline", "Processing deadline",
+          "Maximum processing deadline in nanoseconds", 0, G_MAXUINT64,
+          DEFAULT_PROCESSING_DEADLINE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
@@ -629,7 +648,7 @@ gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
   GstPadTemplate *pad_template;
   GstBaseSinkPrivate *priv;
 
-  basesink->priv = priv = GST_BASE_SINK_GET_PRIVATE (basesink);
+  basesink->priv = priv = gst_base_sink_get_instance_private (basesink);
 
   pad_template =
       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
@@ -660,12 +679,15 @@ gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
   priv->async_enabled = DEFAULT_ASYNC;
   priv->ts_offset = DEFAULT_TS_OFFSET;
   priv->render_delay = DEFAULT_RENDER_DELAY;
+  priv->processing_deadline = DEFAULT_PROCESSING_DEADLINE;
   priv->blocksize = DEFAULT_BLOCKSIZE;
   priv->cached_clock_id = NULL;
   g_atomic_int_set (&priv->enable_last_sample, DEFAULT_ENABLE_LAST_SAMPLE);
   priv->throttle_time = DEFAULT_THROTTLE_TIME;
   priv->max_bitrate = DEFAULT_MAX_BITRATE;
 
+  priv->drop_out_of_segment = DEFAULT_DROP_OUT_OF_SEGMENT;
+
   GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_SINK);
 }
 
@@ -727,6 +749,53 @@ gst_base_sink_get_sync (GstBaseSink * sink)
 }
 
 /**
+ * gst_base_sink_set_drop_out_of_segment:
+ * @sink: the sink
+ * @drop_out_of_segment: drop buffers outside the segment
+ *
+ * Configure @sink to drop buffers which are outside the current segment
+ *
+ * Since: 1.12
+ */
+void
+gst_base_sink_set_drop_out_of_segment (GstBaseSink * sink,
+    gboolean drop_out_of_segment)
+{
+  g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+  GST_OBJECT_LOCK (sink);
+  sink->priv->drop_out_of_segment = drop_out_of_segment;
+  GST_OBJECT_UNLOCK (sink);
+
+}
+
+/**
+ * gst_base_sink_get_drop_out_of_segment:
+ * @sink: the sink
+ *
+ * Checks if @sink is currently configured to drop buffers which are outside
+ * the current segment
+ *
+ * Returns: %TRUE if the sink is configured to drop buffers outside the
+ * current segment.
+ *
+ * Since: 1.12
+ */
+gboolean
+gst_base_sink_get_drop_out_of_segment (GstBaseSink * sink)
+{
+  gboolean res;
+
+  g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
+
+  GST_OBJECT_LOCK (sink);
+  res = sink->priv->drop_out_of_segment;
+  GST_OBJECT_UNLOCK (sink);
+
+  return res;
+}
+
+/**
  * gst_base_sink_set_max_lateness:
  * @sink: the sink
  * @max_lateness: the new max lateness value.
@@ -750,7 +819,7 @@ gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
  * gst_base_sink_get_max_lateness:
  * @sink: the sink
  *
- * Gets the max lateness value. See gst_base_sink_set_max_lateness for
+ * Gets the max lateness value. See gst_base_sink_set_max_lateness() for
  * more details.
  *
  * Returns: The maximum time in nanoseconds that a buffer can be late
@@ -904,9 +973,9 @@ gst_base_sink_get_ts_offset (GstBaseSink * sink)
  *
  * Free-function: gst_sample_unref
  *
- * 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.
+ * Returns: (transfer full) (nullable): 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.
  */
 GstSample *
 gst_base_sink_get_last_sample (GstBaseSink * sink)
@@ -916,7 +985,16 @@ gst_base_sink_get_last_sample (GstBaseSink * sink)
   g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL);
 
   GST_OBJECT_LOCK (sink);
-  if (sink->priv->last_buffer) {
+  if (sink->priv->last_buffer_list) {
+    GstBuffer *first_buffer = NULL;
+
+    /* Set the first buffer in the list to last sample's buffer */
+    first_buffer = gst_buffer_list_get (sink->priv->last_buffer_list, 0);
+    res =
+        gst_sample_new (first_buffer, sink->priv->last_caps, &sink->segment,
+        NULL);
+    gst_sample_set_buffer_list (res, sink->priv->last_buffer_list);
+  } else if (sink->priv->last_buffer) {
     res = gst_sample_new (sink->priv->last_buffer,
         sink->priv->last_caps, &sink->segment, NULL);
   }
@@ -954,6 +1032,32 @@ gst_base_sink_set_last_buffer_unlocked (GstBaseSink * sink, GstBuffer * buffer)
   }
 }
 
+/* with OBJECT_LOCK */
+static void
+gst_base_sink_set_last_buffer_list_unlocked (GstBaseSink * sink,
+    GstBufferList * buffer_list)
+{
+  GstBufferList *old;
+
+  old = sink->priv->last_buffer_list;
+  if (G_LIKELY (old != buffer_list)) {
+    GST_DEBUG_OBJECT (sink, "setting last buffer list to %p", buffer_list);
+    if (G_LIKELY (buffer_list))
+      gst_mini_object_ref (GST_MINI_OBJECT_CAST (buffer_list));
+    sink->priv->last_buffer_list = buffer_list;
+  } else {
+    old = NULL;
+  }
+
+  /* avoid unreffing with the lock because cleanup code might want to take the
+   * lock too */
+  if (G_LIKELY (old)) {
+    GST_OBJECT_UNLOCK (sink);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (old));
+    GST_OBJECT_LOCK (sink);
+  }
+}
+
 static void
 gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
 {
@@ -965,6 +1069,18 @@ gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
   GST_OBJECT_UNLOCK (sink);
 }
 
+static void
+gst_base_sink_set_last_buffer_list (GstBaseSink * sink,
+    GstBufferList * buffer_list)
+{
+  if (!g_atomic_int_get (&sink->priv->enable_last_sample))
+    return;
+
+  GST_OBJECT_LOCK (sink);
+  gst_base_sink_set_last_buffer_list_unlocked (sink, buffer_list);
+  GST_OBJECT_UNLOCK (sink);
+}
+
 /**
  * gst_base_sink_set_last_sample_enabled:
  * @sink: the sink
@@ -983,6 +1099,7 @@ gst_base_sink_set_last_sample_enabled (GstBaseSink * sink, gboolean enabled)
           !enabled, enabled) && !enabled) {
     GST_OBJECT_LOCK (sink);
     gst_base_sink_set_last_buffer_unlocked (sink, NULL);
+    gst_base_sink_set_last_buffer_list_unlocked (sink, NULL);
     GST_OBJECT_UNLOCK (sink);
   }
 }
@@ -1051,7 +1168,7 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
     GstClockTime * max_latency)
 {
   gboolean l, us_live, res, have_latency;
-  GstClockTime min, max, render_delay;
+  GstClockTime min, max, render_delay, processing_deadline;
   GstQuery *query;
   GstClockTime us_min, us_max;
 
@@ -1060,6 +1177,7 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
   l = sink->sync;
   have_latency = sink->priv->have_latency;
   render_delay = sink->priv->render_delay;
+  processing_deadline = sink->priv->processing_deadline;
   GST_OBJECT_UNLOCK (sink);
 
   /* assume no latency */
@@ -1083,11 +1201,28 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
          * values to create the complete latency. */
         min = us_min;
         max = us_max;
+
+        if (l) {
+          if (max == -1 || min + processing_deadline <= max)
+            min += processing_deadline;
+          else {
+            GST_ELEMENT_WARNING (sink, CORE, CLOCK,
+                (_("Pipeline construction is invalid, please add queues.")),
+                ("Not enough buffering available for "
+                    " the processing deadline of %" GST_TIME_FORMAT
+                    ", add enough queues to buffer  %" GST_TIME_FORMAT
+                    " additional data. Shortening processing latency to %"
+                    GST_TIME_FORMAT ".",
+                    GST_TIME_ARGS (processing_deadline),
+                    GST_TIME_ARGS (min + processing_deadline - max),
+                    GST_TIME_ARGS (max - min)));
+            min = max;
+          }
+        }
       }
       if (l) {
         /* we need to add the render delay if we are live */
-        if (min != -1)
-          min += render_delay;
+        min += render_delay;
         if (max != -1)
           max += render_delay;
       }
@@ -1192,7 +1327,7 @@ gst_base_sink_get_render_delay (GstBaseSink * sink)
  * Set the number of bytes that the sink will pull when it is operating in pull
  * mode.
  */
-/* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
+/* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
 void
 gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
 {
@@ -1213,7 +1348,7 @@ gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
  *
  * Returns: the number of bytes @sink will pull in pull mode.
  */
-/* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
+/* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
 guint
 gst_base_sink_get_blocksize (GstBaseSink * sink)
 {
@@ -1235,7 +1370,7 @@ gst_base_sink_get_blocksize (GstBaseSink * sink)
  *
  * Set the time that will be inserted between rendered buffers. This
  * can be used to control the maximum buffers per second that the sink
- * will render. 
+ * will render.
  */
 void
 gst_base_sink_set_throttle_time (GstBaseSink * sink, guint64 throttle)
@@ -1252,7 +1387,7 @@ gst_base_sink_set_throttle_time (GstBaseSink * sink, guint64 throttle)
  * gst_base_sink_get_throttle_time:
  * @sink: a #GstBaseSink
  *
- * Get the time that will be inserted between frames to control the 
+ * Get the time that will be inserted between frames to control the
  * maximum buffers per second.
  *
  * Returns: the number of nanoseconds @sink will put between frames.
@@ -1315,6 +1450,67 @@ gst_base_sink_get_max_bitrate (GstBaseSink * sink)
   return res;
 }
 
+/**
+ * gst_base_sink_set_processing_deadline:
+ * @sink: a #GstBaseSink
+ * @processing_deadline: the new processing deadline in nanoseconds.
+ *
+ * Maximum amount of time (in nanoseconds) that the pipeline can take
+ * for processing the buffer. This is added to the latency of live
+ * pipelines.
+ *
+ * This function is usually called by subclasses.
+ *
+ * Since: 1.16
+ */
+void
+gst_base_sink_set_processing_deadline (GstBaseSink * sink,
+    GstClockTime processing_deadline)
+{
+  GstClockTime old_processing_deadline;
+
+  g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+  GST_OBJECT_LOCK (sink);
+  old_processing_deadline = sink->priv->processing_deadline;
+  sink->priv->processing_deadline = processing_deadline;
+  GST_LOG_OBJECT (sink, "set render processing_deadline to %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (processing_deadline));
+  GST_OBJECT_UNLOCK (sink);
+
+  if (processing_deadline != old_processing_deadline) {
+    GST_DEBUG_OBJECT (sink, "posting latency changed");
+    gst_element_post_message (GST_ELEMENT_CAST (sink),
+        gst_message_new_latency (GST_OBJECT_CAST (sink)));
+  }
+}
+
+/**
+ * gst_base_sink_get_processing_deadline:
+ * @sink: a #GstBaseSink
+ *
+ * Get the processing deadline of @sink. see
+ * gst_base_sink_set_processing_deadline() for more information about
+ * the processing deadline.
+ *
+ * Returns: the processing deadline
+ *
+ * Since: 1.16
+ */
+GstClockTime
+gst_base_sink_get_processing_deadline (GstBaseSink * sink)
+{
+  GstClockTimeDiff res;
+
+  g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
+
+  GST_OBJECT_LOCK (sink);
+  res = sink->priv->processing_deadline;
+  GST_OBJECT_UNLOCK (sink);
+
+  return res;
+}
+
 static void
 gst_base_sink_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
@@ -1352,6 +1548,9 @@ gst_base_sink_set_property (GObject * object, guint prop_id,
     case PROP_MAX_BITRATE:
       gst_base_sink_set_max_bitrate (sink, g_value_get_uint64 (value));
       break;
+    case PROP_PROCESSING_DEADLINE:
+      gst_base_sink_set_processing_deadline (sink, g_value_get_uint64 (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1381,7 +1580,7 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
       g_value_set_int64 (value, gst_base_sink_get_ts_offset (sink));
       break;
     case PROP_LAST_SAMPLE:
-      gst_value_take_buffer (value, gst_base_sink_get_last_sample (sink));
+      gst_value_take_sample (value, gst_base_sink_get_last_sample (sink));
       break;
     case PROP_ENABLE_LAST_SAMPLE:
       g_value_set_boolean (value, gst_base_sink_is_last_sample_enabled (sink));
@@ -1398,6 +1597,9 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_MAX_BITRATE:
       g_value_set_uint64 (value, gst_base_sink_get_max_bitrate (sink));
       break;
+    case PROP_PROCESSING_DEADLINE:
+      g_value_set_uint64 (value, gst_base_sink_get_processing_deadline (sink));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1439,11 +1641,11 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
   switch (pending) {
     case GST_STATE_PLAYING:
     {
-      GST_DEBUG_OBJECT (basesink, "commiting state to PLAYING");
+      GST_DEBUG_OBJECT (basesink, "committing state to PLAYING");
 
       basesink->need_preroll = FALSE;
       post_async_done = TRUE;
-      basesink->priv->commited = TRUE;
+      basesink->priv->committed = TRUE;
       post_playing = TRUE;
       /* post PAUSED too when we were READY */
       if (current == GST_STATE_READY) {
@@ -1452,10 +1654,10 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
       break;
     }
     case GST_STATE_PAUSED:
-      GST_DEBUG_OBJECT (basesink, "commiting state to PAUSED");
+      GST_DEBUG_OBJECT (basesink, "committing state to PAUSED");
       post_paused = TRUE;
       post_async_done = TRUE;
-      basesink->priv->commited = TRUE;
+      basesink->priv->committed = TRUE;
       post_pending = GST_STATE_VOID_PENDING;
       break;
     case GST_STATE_READY:
@@ -1507,7 +1709,7 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
       /* state change function could have been executed and we could be
        * flushing now */
       if (G_UNLIKELY (basesink->flushing))
-        goto stopping;
+        goto stopping_unlocked;
     }
     GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
     /* FIXME, we released the PREROLL lock above, it's possible that this
@@ -1547,6 +1749,11 @@ nothing_pending:
     GST_OBJECT_UNLOCK (basesink);
     return TRUE;
   }
+stopping_unlocked:
+  {
+    GST_OBJECT_LOCK (basesink);
+    goto stopping;
+  }
 stopping:
   {
     /* app is going to READY */
@@ -1606,7 +1813,9 @@ start_stepping (GstBaseSink * sink, GstSegment * segment,
       /* update the segment clipping regions for non-flushing seeks */
       if (segment->rate > 0.0) {
         if (end != -1)
-          position = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
+          position =
+              gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+              end);
         else
           position = segment->stop;
 
@@ -1614,7 +1823,9 @@ start_stepping (GstBaseSink * sink, GstSegment * segment,
         segment->position = position;
       } else {
         if (end != -1)
-          position = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
+          position =
+              gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+              end);
         else
           position = segment->start;
 
@@ -1750,10 +1961,14 @@ handle_stepping (GstBaseSink * sink, GstSegment * segment,
         step_end = TRUE;
         if (segment->rate > 0.0) {
           *rstart = end;
-          *cstart = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
+          *cstart =
+              gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+              end);
         } else {
           *rstop = end;
-          *cstop = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
+          *cstop =
+              gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+              end);
         }
       }
       GST_DEBUG_OBJECT (sink,
@@ -1844,7 +2059,7 @@ again:
         }
 
         rstart = rstop = rnext = priv->eos_rtime;
-        *do_sync = rstart != -1;
+        *do_sync = GST_CLOCK_TIME_IS_VALID (rstart);
         GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT,
             GST_TIME_ARGS (rstart));
         /* if we are stepping, we end now */
@@ -1877,8 +2092,8 @@ again:
     /* else do buffer sync code */
     GstBuffer *buffer = GST_BUFFER_CAST (obj);
 
-    /* just get the times to see if we need syncing, if the start returns -1 we
-     * don't sync. */
+    /* just get the times to see if we need syncing, if the retuned start is -1
+     * we don't sync. */
     if (bclass->get_times)
       bclass->get_times (basesink, buffer, &start, &stop);
 
@@ -2071,11 +2286,8 @@ gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,
   time += base_time;
 
   /* Re-use existing clockid if available */
-  /* FIXME: Casting to GstClockEntry only works because the types
-   * are the same */
   if (G_LIKELY (sink->priv->cached_clock_id != NULL
-          && GST_CLOCK_ENTRY_CLOCK ((GstClockEntry *) sink->
-              priv->cached_clock_id) == clock)) {
+          && gst_clock_id_uses_clock (sink->priv->cached_clock_id, clock))) {
     if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id,
             time)) {
       gst_clock_id_unref (sink->priv->cached_clock_id);
@@ -2131,6 +2343,13 @@ no_clock:
  * against the clock it must unblock when going from PLAYING to the PAUSED state
  * and call this method before continuing to render the remaining data.
  *
+ * If the #GstBaseSinkClass.render() method can block on something else than
+ * the clock, it must also be ready to unblock immediately on
+ * the #GstBaseSinkClass.unlock() method and cause the
+ * #GstBaseSinkClass.render() method to immediately call this function.
+ * In this case, the subclass must be prepared to continue rendering where it
+ * left off if this function returns %GST_FLOW_OK.
+ *
  * 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
@@ -2202,13 +2421,17 @@ gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
 
       if (GST_IS_BUFFER_LIST (obj)) {
         buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+        gst_base_sink_set_last_buffer (sink, buf);
+        gst_base_sink_set_last_buffer_list (sink, GST_BUFFER_LIST_CAST (obj));
         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
+        gst_base_sink_set_last_buffer_list (sink, NULL);
+      } else {
         buf = NULL;
+      }
 
       if (buf) {
         GST_DEBUG_OBJECT (sink, "preroll buffer %" GST_TIME_FORMAT,
@@ -2260,7 +2483,7 @@ preroll_canceled:
   }
 stopping:
   {
-    GST_DEBUG_OBJECT (sink, "stopping while commiting state");
+    GST_DEBUG_OBJECT (sink, "stopping while committing state");
     return GST_FLOW_FLUSHING;
   }
 preroll_failed:
@@ -2331,7 +2554,7 @@ gst_base_sink_wait (GstBaseSink * sink, GstClockTime time,
       goto flushing;
 
     /* retry if we got unscheduled, which means we did not reach the timeout
-     * yet. if some other error occures, we continue. */
+     * yet. if some other error occurs, we continue. */
   } while (status == GST_CLOCK_UNSCHEDULED);
 
   GST_DEBUG_OBJECT (sink, "end of stream");
@@ -2416,7 +2639,8 @@ do_step:
   priv->eos_rtime = (do_sync ? rnext : GST_CLOCK_TIME_NONE);
 
   /* calculate inter frame spacing */
-  if (G_UNLIKELY (priv->prev_rstart != -1 && priv->prev_rstart < rstart)) {
+  if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->prev_rstart) &&
+          priv->prev_rstart < rstart)) {
     GstClockTime in_diff;
 
     in_diff = rstart - priv->prev_rstart;
@@ -2432,8 +2656,8 @@ do_step:
   }
   priv->prev_rstart = rstart;
 
-  if (G_UNLIKELY (priv->earliest_in_time != -1
-          && rstart < priv->earliest_in_time))
+  if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->earliest_in_time) &&
+          rstart < priv->earliest_in_time))
     goto qos_dropped;
 
 again:
@@ -2604,11 +2828,11 @@ gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped)
     left = start + jitter;
   }
 
-  /* calculate duration of the buffer */
-  if (GST_CLOCK_TIME_IS_VALID (stop) && stop != start)
-    duration = stop - start;
-  else
-    duration = priv->avg_in_diff;
+  /* calculate duration of the buffer, only use buffer durations if not in
+   * trick mode or key-unit mode. Otherwise the buffer durations will be
+   * meaningless as frames are being dropped in-between without updating the
+   * durations. */
+  duration = priv->avg_in_diff;
 
   /* if we have the time when the last buffer left us, calculate
    * processing time */
@@ -2629,29 +2853,24 @@ gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped)
       GST_TIME_ARGS (entered), GST_TIME_ARGS (left), GST_TIME_ARGS (pt),
       GST_TIME_ARGS (duration), jitter);
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "avg_duration: %" GST_TIME_FORMAT
-      ", avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g",
-      GST_TIME_ARGS (priv->avg_duration), GST_TIME_ARGS (priv->avg_pt),
-      priv->avg_rate);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink,
+      "avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g",
+      GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
 
   /* collect running averages. for first observations, we copy the
    * values */
-  if (!GST_CLOCK_TIME_IS_VALID (priv->avg_duration))
-    priv->avg_duration = duration;
-  else
-    priv->avg_duration = UPDATE_RUNNING_AVG (priv->avg_duration, duration);
-
   if (!GST_CLOCK_TIME_IS_VALID (priv->avg_pt))
     priv->avg_pt = pt;
   else
     priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt);
 
-  if (priv->avg_duration != 0)
+  if (duration != -1 && duration != 0) {
     rate =
         gst_guint64_to_gdouble (priv->avg_pt) /
-        gst_guint64_to_gdouble (priv->avg_duration);
-  else
+        gst_guint64_to_gdouble (duration);
+  } else {
     rate = 1.0;
+  }
 
   if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
     if (dropped || priv->avg_rate < 0.0) {
@@ -2665,9 +2884,8 @@ gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped)
   }
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink,
-      "updated: avg_duration: %" GST_TIME_FORMAT ", avg_pt: %" GST_TIME_FORMAT
-      ", avg_rate: %g", GST_TIME_ARGS (priv->avg_duration),
-      GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
+      "updated: avg_pt: %" GST_TIME_FORMAT
+      ", avg_rate: %g", GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
 
 
   if (priv->avg_rate >= 0.0) {
@@ -2713,10 +2931,8 @@ gst_base_sink_reset_qos (GstBaseSink * sink)
   priv->prev_rstart = GST_CLOCK_TIME_NONE;
   priv->earliest_in_time = GST_CLOCK_TIME_NONE;
   priv->last_left = GST_CLOCK_TIME_NONE;
-  priv->avg_duration = GST_CLOCK_TIME_NONE;
   priv->avg_pt = GST_CLOCK_TIME_NONE;
   priv->avg_rate = -1.0;
-  priv->avg_render = GST_CLOCK_TIME_NONE;
   priv->avg_in_diff = GST_CLOCK_TIME_NONE;
   priv->rendered = 0;
   priv->dropped = 0;
@@ -2825,42 +3041,14 @@ no_timestamp:
   }
 }
 
-/* called before and after calling the render vmethod. It keeps track of how
- * much time was spent in the render method and is used to check if we are
- * flooded */
-static void
-gst_base_sink_do_render_stats (GstBaseSink * basesink, gboolean start)
-{
-  GstBaseSinkPrivate *priv;
-
-  priv = basesink->priv;
-
-  if (start) {
-    priv->start = gst_util_get_timestamp ();
-  } else {
-    GstClockTime elapsed;
-
-    priv->stop = gst_util_get_timestamp ();
-
-    elapsed = GST_CLOCK_DIFF (priv->start, priv->stop);
-
-    if (!GST_CLOCK_TIME_IS_VALID (priv->avg_render))
-      priv->avg_render = elapsed;
-    else
-      priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed);
-
-    GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
-        "avg_render: %" GST_TIME_FORMAT, GST_TIME_ARGS (priv->avg_render));
-  }
-}
-
 static void
 gst_base_sink_update_start_time (GstBaseSink * basesink)
 {
   GstClock *clock;
 
   GST_OBJECT_LOCK (basesink);
-  if ((clock = GST_ELEMENT_CLOCK (basesink))) {
+  if (GST_STATE (basesink) == GST_STATE_PLAYING
+      && (clock = GST_ELEMENT_CLOCK (basesink))) {
     GstClockTime now;
 
     gst_object_ref (clock);
@@ -2917,6 +3105,7 @@ gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
     basesink->priv->have_latency = TRUE;
   }
   gst_base_sink_set_last_buffer (basesink, NULL);
+  gst_base_sink_set_last_buffer_list (basesink, NULL);
   GST_PAD_STREAM_UNLOCK (pad);
 }
 
@@ -3064,19 +3253,28 @@ gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
     }
     case GST_EVENT_CAPS:
     {
-      GstCaps *caps;
+      GstCaps *caps, *current_caps;
 
       GST_DEBUG_OBJECT (basesink, "caps %p", event);
 
       gst_event_parse_caps (event, &caps);
-      if (bclass->set_caps)
-        result = bclass->set_caps (basesink, caps);
+      current_caps = gst_pad_get_current_caps (GST_BASE_SINK_PAD (basesink));
 
-      if (result) {
-        GST_OBJECT_LOCK (basesink);
-        gst_caps_replace (&basesink->priv->caps, caps);
-        GST_OBJECT_UNLOCK (basesink);
+      if (current_caps && gst_caps_is_equal (current_caps, caps)) {
+        GST_DEBUG_OBJECT (basesink,
+            "New caps equal to old ones: %" GST_PTR_FORMAT, caps);
+      } else {
+        if (bclass->set_caps)
+          result = bclass->set_caps (basesink, caps);
+
+        if (result) {
+          GST_OBJECT_LOCK (basesink);
+          gst_caps_replace (&basesink->priv->caps, caps);
+          GST_OBJECT_UNLOCK (basesink);
+        }
       }
+      if (current_caps)
+        gst_caps_unref (current_caps);
       break;
     }
     case GST_EVENT_SEGMENT:
@@ -3091,6 +3289,7 @@ gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
       GST_DEBUG_OBJECT (basesink, "configured segment %" GST_SEGMENT_FORMAT,
           &basesink->segment);
       basesink->have_newsegment = TRUE;
+      gst_base_sink_reset_qos (basesink);
       GST_OBJECT_UNLOCK (basesink);
       break;
     case GST_EVENT_GAP:
@@ -3247,13 +3446,6 @@ gst_base_sink_needs_preroll (GstBaseSink * basesink)
   return res;
 }
 
-static gboolean
-count_list_bytes (GstBuffer ** buffer, guint idx, GstBaseSinkPrivate * priv)
-{
-  priv->rc_accumulated += gst_buffer_get_size (*buffer);
-  return TRUE;
-}
-
 /* with STREAM_LOCK, PREROLL_LOCK
  *
  * Takes a buffer and compare the timestamps with the last segment.
@@ -3272,7 +3464,6 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
   GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
   GstSegment *segment;
   GstBuffer *sync_buf;
-  gint do_qos;
   gboolean late, step_end, prepared = FALSE;
 
   if (G_UNLIKELY (basesink->flushing))
@@ -3282,7 +3473,12 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
     goto was_eos;
 
   if (is_list) {
-    sync_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+    GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
+
+    if (gst_buffer_list_length (buffer_list) == 0)
+      goto empty_list;
+
+    sync_buf = gst_buffer_list_get (buffer_list, 0);
     g_assert (NULL != sync_buf);
   } else {
     sync_buf = GST_BUFFER_CAST (obj);
@@ -3327,10 +3523,22 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
   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 */
+  /* a dropped buffer does not participate in anything. Buffer can only be
+   * dropped if their PTS falls completely outside the segment, while we sync
+   * preferably on DTS */
   if (GST_CLOCK_TIME_IS_VALID (start) && (segment->format == GST_FORMAT_TIME)) {
+    GstClockTime pts = GST_BUFFER_PTS (sync_buf);
+    GstClockTime pts_end = GST_CLOCK_TIME_NONE;
+
+    if (!GST_CLOCK_TIME_IS_VALID (pts))
+      pts = start;
+
+    if (GST_CLOCK_TIME_IS_VALID (end))
+      pts_end = pts + (end - start);
+
     if (G_UNLIKELY (!gst_segment_clip (segment,
-                GST_FORMAT_TIME, start, end, NULL, NULL)))
+                GST_FORMAT_TIME, pts, pts_end, NULL, NULL)
+            && priv->drop_out_of_segment))
       goto out_of_segment;
   }
 
@@ -3350,10 +3558,34 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
     if (G_UNLIKELY (stepped))
       goto dropped;
 
-    if (syncable && do_sync)
-      late =
-          gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
-          GST_CLOCK_EARLY, 0, FALSE);
+    if (syncable && do_sync && gst_base_sink_get_sync (basesink)) {
+      GstClock *clock;
+
+      GST_OBJECT_LOCK (basesink);
+      clock = GST_ELEMENT_CLOCK (basesink);
+      if (clock && GST_STATE (basesink) == GST_STATE_PLAYING) {
+        GstClockTime base_time;
+        GstClockTime stime;
+        GstClockTime now;
+
+        base_time = GST_ELEMENT_CAST (basesink)->base_time;
+        stime = base_time + gst_base_sink_adjust_time (basesink, rstart);
+        now = gst_clock_get_time (clock);
+        GST_OBJECT_UNLOCK (basesink);
+
+        late =
+            gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
+            GST_CLOCK_EARLY, GST_CLOCK_DIFF (stime, now), FALSE);
+      } else {
+        GST_OBJECT_UNLOCK (basesink);
+      }
+    }
+
+    /* We are about to prepare the first frame, make sure we have prerolled
+     * already. This prevent nesting prepare/render calls. */
+    ret = gst_base_sink_do_preroll (basesink, obj);
+    if (G_UNLIKELY (ret != GST_FLOW_OK))
+      goto preroll_failed;
 
     if (G_UNLIKELY (late))
       goto dropped;
@@ -3394,38 +3626,37 @@ again:
     goto dropped;
 
   if (priv->max_bitrate) {
-    if (is_list) {
-      gst_buffer_list_foreach (GST_BUFFER_LIST_CAST (obj),
-          (GstBufferListFunc) count_list_bytes, priv);
-    } else {
-      priv->rc_accumulated += gst_buffer_get_size (GST_BUFFER_CAST (obj));
-    }
+    gsize size;
+
+    if (is_list)
+      size = gst_buffer_list_calculate_size (GST_BUFFER_LIST_CAST (obj));
+    else
+      size = gst_buffer_get_size (GST_BUFFER_CAST (obj));
+
+    priv->rc_accumulated += size;
     priv->rc_next = priv->rc_time + gst_util_uint64_scale (priv->rc_accumulated,
         8 * GST_SECOND, priv->max_bitrate);
   }
 
-  /* 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));
+    gst_base_sink_set_last_buffer_list (basesink, NULL);
 
     if (bclass->render)
       ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
   } else {
+    GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
+
     if (bclass->render_list)
-      ret = bclass->render_list (basesink, GST_BUFFER_LIST_CAST (obj));
-  }
+      ret = bclass->render_list (basesink, buffer_list);
 
-  if (do_qos)
-    gst_base_sink_do_render_stats (basesink, FALSE);
+    /* Set the first buffer and buffer list to be included in last sample */
+    gst_base_sink_set_last_buffer (basesink, sync_buf);
+    gst_base_sink_set_last_buffer_list (basesink, buffer_list);
+  }
 
   if (ret == GST_FLOW_STEP)
     goto again;
@@ -3464,6 +3695,12 @@ was_eos:
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
     return GST_FLOW_EOS;
   }
+empty_list:
+  {
+    GST_DEBUG_OBJECT (basesink, "buffer list with no buffers");
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
+    return GST_FLOW_OK;
+  }
 out_of_segment:
   {
     GST_DEBUG_OBJECT (basesink, "dropping buffer, out of clipping segment");
@@ -3515,6 +3752,12 @@ dropped:
     }
     goto done;
   }
+preroll_failed:
+  {
+    GST_DEBUG_OBJECT (basesink, "preroll failed: %s", gst_flow_get_name (ret));
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
+    return ret;
+  }
 }
 
 /* with STREAM_LOCK
@@ -3876,6 +4119,7 @@ gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
     priv->eos_rtime = GST_CLOCK_TIME_NONE;
     priv->call_preroll = TRUE;
     gst_base_sink_set_last_buffer (sink, NULL);
+    gst_base_sink_set_last_buffer_list (sink, NULL);
     gst_base_sink_reset_qos (sink);
 
     if (sink->clock_id) {
@@ -3961,14 +4205,12 @@ paused:
       }
     } 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. 
+       * first so the app knows about the error first.
        * wrong-state is not a fatal error because it happens due to
        * flushing and posting an error message in that case is the
        * wrong thing to do, e.g. when basesrc is doing a flushing
        * seek. */
-      GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
-          (_("Internal data stream error.")),
-          ("stream stopped, reason %s", gst_flow_get_name (result)));
+      GST_ELEMENT_FLOW_ERROR (basesink, result);
       gst_base_sink_event (pad, parent, gst_event_new_eos ());
     }
     return;
@@ -4080,7 +4322,7 @@ gst_base_sink_pad_activate (GstPad * pad, GstObject * parent)
 
   if (!gst_pad_peer_query (pad, query)) {
     gst_query_unref (query);
-    GST_DEBUG_OBJECT (basesink, "peer query faild, no pull mode");
+    GST_DEBUG_OBJECT (basesink, "peer query failed, no pull mode");
     goto fallback;
   }
 
@@ -4366,6 +4608,8 @@ gst_base_sink_send_event (GstElement * element, GstEvent * event)
   }
 
   if (forward) {
+    GST_DEBUG_OBJECT (basesink, "sending event %p %" GST_PTR_FORMAT, event,
+        event);
     result = gst_pad_push_event (pad, event);
   } else {
     /* not forwarded, unref the event */
@@ -4374,8 +4618,7 @@ gst_base_sink_send_event (GstElement * element, GstEvent * event)
 
   gst_object_unref (pad);
 
-  GST_DEBUG_OBJECT (basesink, "handled event %p %" GST_PTR_FORMAT ": %d", event,
-      event, result);
+  GST_DEBUG_OBJECT (basesink, "handled event: %d", result);
 
   return result;
 }
@@ -4390,7 +4633,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
   GstSegment *segment;
   GstClockTime now, latency;
   GstClockTimeDiff base_time;
-  gint64 time, base, duration;
+  gint64 time, base, offset, duration;
   gdouble rate;
   gint64 last;
   gboolean last_seen, with_clock, in_paused;
@@ -4419,7 +4662,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
 
   /* assume we will use the clock for getting the current position */
   with_clock = TRUE;
-  if (basesink->sync == FALSE)
+  if (!basesink->sync)
     with_clock = FALSE;
 
   /* and we need a clock */
@@ -4442,6 +4685,11 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
   else
     time = 0;
 
+  if (GST_CLOCK_TIME_IS_VALID (segment->offset))
+    offset = segment->offset;
+  else
+    offset = 0;
+
   if (GST_CLOCK_TIME_IS_VALID (segment->stop))
     duration = segment->stop - segment->start;
   else
@@ -4563,11 +4811,15 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
     if (rate < 0.0)
       time += duration;
 
-    *cur = time + gst_guint64_to_gdouble (now - base_time) * rate;
+    *cur = time + offset + gst_guint64_to_gdouble (now - base_time) * rate;
 
     /* never report more than last seen position */
-    if (last != -1)
-      *cur = MIN (last, *cur);
+    if (last != -1) {
+      if (rate > 0.0)
+        *cur = MIN (last, *cur);
+      else
+        *cur = MAX (last, *cur);
+    }
 
     GST_DEBUG_OBJECT (basesink,
         "now %" GST_TIME_FORMAT " - base_time %" GST_TIME_FORMAT " - base %"
@@ -4787,6 +5039,25 @@ default_element_query (GstElement * element, GstQuery * query)
   return res;
 }
 
+static void
+gst_base_sink_drain (GstBaseSink * basesink)
+{
+  GstBuffer *old;
+  GstBufferList *old_list;
+
+  GST_OBJECT_LOCK (basesink);
+  if ((old = basesink->priv->last_buffer))
+    basesink->priv->last_buffer = gst_buffer_copy_deep (old);
+
+  if ((old_list = basesink->priv->last_buffer_list))
+    basesink->priv->last_buffer_list = gst_buffer_list_copy_deep (old_list);
+  GST_OBJECT_UNLOCK (basesink);
+
+  if (old)
+    gst_buffer_unref (old);
+  if (old_list)
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (old_list));
+}
 
 static gboolean
 gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
@@ -4799,6 +5070,7 @@ gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_ALLOCATION:
     {
+      gst_base_sink_drain (basesink);
       if (bclass->propose_allocation)
         res = bclass->propose_allocation (basesink, query);
       else
@@ -4835,17 +5107,15 @@ gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
     }
     case GST_QUERY_DRAIN:
     {
-      GstBuffer *old;
-
-      GST_OBJECT_LOCK (basesink);
-      if ((old = basesink->priv->last_buffer))
-        basesink->priv->last_buffer = gst_buffer_copy (old);
-      GST_OBJECT_UNLOCK (basesink);
-      if (old)
-        gst_buffer_unref (old);
+      gst_base_sink_drain (basesink);
       res = TRUE;
       break;
     }
+    case GST_QUERY_POSITION:
+    {
+      res = default_element_query (GST_ELEMENT (basesink), query);
+      break;
+    }
     default:
       res =
           gst_pad_query_default (basesink->sinkpad, GST_OBJECT_CAST (basesink),
@@ -4911,7 +5181,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       priv->received_eos = FALSE;
       gst_base_sink_reset_qos (basesink);
       priv->rc_next = -1;
-      priv->commited = FALSE;
+      priv->committed = FALSE;
       priv->call_preroll = TRUE;
       priv->current_step.valid = FALSE;
       priv->pending_step.valid = FALSE;
@@ -4952,7 +5222,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
         basesink->need_preroll = TRUE;
         basesink->playing_async = TRUE;
         priv->call_preroll = TRUE;
-        priv->commited = FALSE;
+        priv->committed = FALSE;
         if (priv->async_enabled) {
           GST_DEBUG_OBJECT (basesink, "doing async state change");
           ret = GST_STATE_CHANGE_ASYNC;
@@ -4997,11 +5267,6 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       if (bclass->unlock_stop)
         bclass->unlock_stop (basesink);
 
-      /* we need preroll again and we set the flag before unlocking the clockid
-       * because if the clockid is unlocked before a current buffer expired, we
-       * can use that buffer to preroll with */
-      basesink->need_preroll = TRUE;
-
       if (basesink->clock_id) {
         GST_DEBUG_OBJECT (basesink, "unschedule clock");
         gst_clock_id_unschedule (basesink->clock_id);
@@ -5012,6 +5277,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       if (!gst_base_sink_needs_preroll (basesink)) {
         GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, we are prerolled");
         basesink->playing_async = FALSE;
+        basesink->need_preroll = FALSE;
       } else {
         if (GST_STATE_TARGET (GST_ELEMENT (basesink)) <= GST_STATE_READY) {
           GST_DEBUG_OBJECT (basesink, "element is <= READY");
@@ -5020,7 +5286,8 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
           GST_DEBUG_OBJECT (basesink,
               "PLAYING to PAUSED, we are not prerolled");
           basesink->playing_async = TRUE;
-          priv->commited = FALSE;
+          basesink->need_preroll = TRUE;
+          priv->committed = FALSE;
           priv->call_preroll = TRUE;
           if (priv->async_enabled) {
             GST_DEBUG_OBJECT (basesink, "doing async state change");
@@ -5054,9 +5321,10 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       GST_OBJECT_UNLOCK (basesink);
 
       gst_base_sink_set_last_buffer (basesink, NULL);
+      gst_base_sink_set_last_buffer_list (basesink, NULL);
       priv->call_preroll = FALSE;
 
-      if (!priv->commited) {
+      if (!priv->committed) {
         if (priv->async_enabled) {
           GST_DEBUG_OBJECT (basesink, "PAUSED to READY, posting async-done");
 
@@ -5068,7 +5336,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
               gst_message_new_async_done (GST_OBJECT_CAST (basesink),
                   GST_CLOCK_TIME_NONE));
         }
-        priv->commited = TRUE;
+        priv->committed = TRUE;
       } else {
         GST_DEBUG_OBJECT (basesink, "PAUSED to READY, don't need_preroll");
       }
@@ -5081,6 +5349,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
         }
       }
       gst_base_sink_set_last_buffer (basesink, NULL);
+      gst_base_sink_set_last_buffer_list (basesink, NULL);
       priv->call_preroll = FALSE;
       break;
     default:
@@ -5093,6 +5362,10 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
 start_failed:
   {
     GST_DEBUG_OBJECT (basesink, "failed to start");
+    /* subclass is supposed to post a message but we post one as a fallback
+     * just in case */
+    GST_ELEMENT_ERROR (basesink, CORE, STATE_CHANGE, (NULL),
+        ("Failed to start"));
     return GST_STATE_CHANGE_FAILURE;
   }
 activate_failed: