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 c7101b2..4368c1a 100644 (file)
@@ -21,6 +21,7 @@
 
 /**
  * SECTION:gstbasesink
+ * @title: GstBaseSink
  * @short_description: Base class for sink elements
  * @see_also: #GstBaseTransform, #GstBaseSrc
  *
  * #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)
  * {
  *   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (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_PAD_SINK and name "sink"
+ *   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>");
  * }
  * ]|
  *
  * #GstBaseSink will handle the prerolling correctly. This means that it will
- * return #GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first
+ * return %GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first
  * buffer arrives in this element. The base class will call the
  * #GstBaseSinkClass.preroll() vmethod with this preroll buffer and will then
  * commit the state change to the next asynchronously pending state.
  *
  * When the element is set to PLAYING, #GstBaseSink will synchronise on the
  * clock using the times returned from #GstBaseSinkClass.get_times(). If this
- * function returns #GST_CLOCK_TIME_NONE for the start time, no synchronisation
+ * function returns %GST_CLOCK_TIME_NONE for the start time, no synchronisation
  * will be done. Synchronisation can be disabled entirely by setting the object
  * #GstBaseSink:sync property to %FALSE.
  *
  * element receives EOS in PAUSED, preroll completes, the event is queued and an
  * EOS message is posted when going to PLAYING.
  *
- * #GstBaseSink will internally use the #GST_EVENT_SEGMENT events to schedule
+ * #GstBaseSink will internally use the %GST_EVENT_SEGMENT events to schedule
  * synchronisation and clipping of buffers. Buffers that fall completely outside
  * of the current segment are dropped. Buffers that fall partially in the
  * segment are rendered (and prerolled). Subclasses should do any subbuffer
  * clipping themselves when needed.
  *
  * #GstBaseSink will by default report the current playback position in
- * #GST_FORMAT_TIME based on the current clock time and segment information.
+ * %GST_FORMAT_TIME based on the current clock time and segment information.
  * If no clock has been set on the element, the query will be forwarded
  * upstream.
  *
  * 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.
  *
  * The #GstBaseSinkClass.event() virtual method will be called when an event is
- * received by #GstBaseSink. Normally this method should only be overriden by
+ * received by #GstBaseSink. Normally this method should only be overridden by
  * very specific elements (such as file sinks) which need to handle the
  * newsegment event specially.
  *
  * The #GstBaseSink:async property can be used to instruct the sink to never
  * perform an ASYNC state change. This feature is mostly usable when dealing
  * with non-synchronized streams or sparse streams.
- *
- * Last reviewed on 2007-08-29 (0.10.15)
  */
 
 #ifdef HAVE_CONFIG_H
 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
@@ -183,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;
@@ -204,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;
@@ -225,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;
 
@@ -240,6 +225,7 @@ struct _GstBaseSinkPrivate
   gint enable_last_sample;      /* atomic */
   GstBuffer *last_buffer;
   GstCaps *last_caps;
+  GstBufferList *last_buffer_list;
 
   /* negotiated caps */
   GstCaps *caps;
@@ -271,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))
@@ -279,7 +267,7 @@ struct _GstBaseSinkPrivate
 #define UPDATE_RUNNING_AVG(avg,val)   DO_RUNNING_AVG(avg,val,8)
 
 /* the windows for these running averages are experimentally obtained.
- * possitive values get averaged more while negative values use a small
+ * positive values get averaged more while negative values use a small
  * window so we can react faster to badness. */
 #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)
@@ -299,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
 {
@@ -314,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);
@@ -344,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,
@@ -405,7 +407,7 @@ 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);
+    GstClockReturn status, GstClockTimeDiff jitter, gboolean render);
 
 static void
 gst_base_sink_class_init (GstBaseSinkClass * klass)
@@ -416,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;
@@ -444,8 +447,8 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
   /**
    * GstBaseSink:async:
    *
-   * If set to #TRUE, the basesink will perform asynchronous state changes.
-   * When set to #FALSE, the sink will not signal the parent when it prerolls.
+   * If set to %TRUE, the basesink will perform asynchronous state changes.
+   * When set to %FALSE, the sink will not signal the parent when it prerolls.
    * Use this option when dealing with sparse streams or when synchronisation is
    * not required.
    */
@@ -468,9 +471,9 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
   /**
    * GstBaseSink:enable-last-sample:
    *
-   * Enable the last-sample property. If FALSE, basesink doesn't keep a
+   * 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
+   * 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.
    */
   g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_SAMPLE,
@@ -483,7 +486,7 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    *
    * 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
-   * can be NULL when the sink has not yet received a bufer.
+   * can be %NULL when the sink has not yet received a buffer.
    */
   g_object_class_install_property (gobject_class, PROP_LAST_SAMPLE,
       g_param_spec_boxed ("last-sample", "Last Sample",
@@ -494,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,
@@ -529,13 +532,27 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    * Setting this property to a value bigger than 0 will make the sink delay
    * rendering of the buffers when it would exceed to max-bitrate.
    *
-   * Since: 1.1.1
+   * Since: 1.2
    */
   g_object_class_install_property (gobject_class, PROP_MAX_BITRATE,
       g_param_spec_uint64 ("max-bitrate", "Max Bitrate",
           "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);
@@ -631,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");
@@ -662,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);
 }
 
@@ -690,8 +710,8 @@ gst_base_sink_finalize (GObject * object)
  * @sync: the new sync value.
  *
  * Configures @sink to synchronize on the clock or not. When
- * @sync is FALSE, incoming samples will be played as fast as
- * possible. If @sync is TRUE, the timestamps of the incomming
+ * @sync is %FALSE, incoming samples will be played as fast as
+ * possible. If @sync is %TRUE, the timestamps of the incoming
  * buffers will be used to schedule the exact render time of its
  * contents.
  */
@@ -712,7 +732,7 @@ gst_base_sink_set_sync (GstBaseSink * sink, gboolean sync)
  * Checks if @sink is currently configured to synchronize against the
  * clock.
  *
- * Returns: TRUE if the sink is configured to synchronize against the clock.
+ * Returns: %TRUE if the sink is configured to synchronize against the clock.
  */
 gboolean
 gst_base_sink_get_sync (GstBaseSink * sink)
@@ -729,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.
@@ -752,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
@@ -795,7 +862,7 @@ gst_base_sink_set_qos_enabled (GstBaseSink * sink, gboolean enabled)
  * Checks if @sink is currently configured to send Quality-of-Service events
  * upstream.
  *
- * Returns: TRUE if the sink is configured to perform Quality-of-Service.
+ * Returns: %TRUE if the sink is configured to perform Quality-of-Service.
  */
 gboolean
 gst_base_sink_is_qos_enabled (GstBaseSink * sink)
@@ -814,7 +881,7 @@ gst_base_sink_is_qos_enabled (GstBaseSink * sink)
  * @sink: the sink
  * @enabled: the new async value.
  *
- * Configures @sink to perform all state changes asynchronusly. When async is
+ * Configures @sink to perform all state changes asynchronously. When async is
  * disabled, the sink will immediately go to PAUSED instead of waiting for a
  * preroll buffer. This feature is useful if the sink does not synchronize
  * against the clock or when it is dealing with sparse streams.
@@ -837,7 +904,7 @@ gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled)
  * Checks if @sink is currently configured to perform asynchronous state
  * changes to PAUSED.
  *
- * Returns: TRUE if the sink is configured to perform asynchronous state
+ * Returns: %TRUE if the sink is configured to perform asynchronous state
  * changes.
  */
 gboolean
@@ -906,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)
@@ -918,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);
   }
@@ -956,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)
 {
@@ -967,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
@@ -985,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);
   }
 }
@@ -996,7 +1111,7 @@ gst_base_sink_set_last_sample_enabled (GstBaseSink * sink, gboolean enabled)
  * 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 sample.
+ * Returns: %TRUE if the sink is configured to store the last received sample.
  */
 gboolean
 gst_base_sink_is_last_sample_enabled (GstBaseSink * sink)
@@ -1035,17 +1150,17 @@ gst_base_sink_get_latency (GstBaseSink * sink)
  * @max_latency: (out) (allow-none): the max latency of the upstream elements
  *
  * Query the sink for the latency parameters. The latency will be queried from
- * the upstream elements. @live will be TRUE if @sink is configured to
- * synchronize against the clock. @upstream_live will be TRUE if an upstream
+ * the upstream elements. @live will be %TRUE if @sink is configured to
+ * synchronize against the clock. @upstream_live will be %TRUE if an upstream
  * element is live.
  *
- * If both @live and @upstream_live are TRUE, the sink will want to compensate
+ * If both @live and @upstream_live are %TRUE, the sink will want to compensate
  * for the latency introduced by the upstream elements by setting the
- * @min_latency to a strictly possitive value.
+ * @min_latency to a strictly positive value.
  *
  * This function is mostly used by subclasses.
  *
- * Returns: TRUE if the query succeeded.
+ * Returns: %TRUE if the query succeeded.
  */
 gboolean
 gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
@@ -1053,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;
 
@@ -1062,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 */
@@ -1085,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;
       }
@@ -1194,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)
 {
@@ -1215,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)
 {
@@ -1237,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)
@@ -1254,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.
@@ -1280,7 +1413,7 @@ gst_base_sink_get_throttle_time (GstBaseSink * sink)
  *
  * Set the maximum amount of bits per second that the sink will render.
  *
- * Since: 1.1.1
+ * Since: 1.2
  */
 void
 gst_base_sink_set_max_bitrate (GstBaseSink * sink, guint64 max_bitrate)
@@ -1301,7 +1434,7 @@ gst_base_sink_set_max_bitrate (GstBaseSink * sink, guint64 max_bitrate)
  *
  * Returns: the maximum number of bits per second @sink will render.
  *
- * Since: 1.1.1
+ * Since: 1.2
  */
 guint64
 gst_base_sink_get_max_bitrate (GstBaseSink * sink)
@@ -1317,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)
@@ -1354,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;
@@ -1383,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));
@@ -1400,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;
@@ -1441,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) {
@@ -1454,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:
@@ -1491,7 +1691,29 @@ gst_base_sink_commit_state (GstBaseSink * basesink)
             GST_CLOCK_TIME_NONE));
   }
   if (post_playing) {
+    if (post_paused) {
+      GstElementClass *klass;
+
+      klass = GST_ELEMENT_GET_CLASS (basesink);
+      basesink->have_preroll = TRUE;
+      /* after releasing this lock, the state change function
+       * can execute concurrently with this thread. There is nothing we do to
+       * prevent this for now. subclasses should be prepared to handle it. */
+      GST_BASE_SINK_PREROLL_UNLOCK (basesink);
+
+      if (klass->change_state)
+        klass->change_state (GST_ELEMENT_CAST (basesink),
+            GST_STATE_CHANGE_PAUSED_TO_PLAYING);
+
+      GST_BASE_SINK_PREROLL_LOCK (basesink);
+      /* state change function could have been executed and we could be
+       * flushing now */
+      if (G_UNLIKELY (basesink->flushing))
+        goto stopping_unlocked;
+    }
     GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
+    /* FIXME, we released the PREROLL lock above, it's possible that this
+     * message is not correct anymore when the element went back to PAUSED */
     gst_element_post_message (GST_ELEMENT_CAST (basesink),
         gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
             next, pending, GST_STATE_VOID_PENDING));
@@ -1506,8 +1728,8 @@ nothing_pending:
     /* Depending on the state, set our vars. We get in this situation when the
      * state change function got a change to update the state vars before the
      * streaming thread did. This is fine but we need to make sure that we
-     * update the need_preroll var since it was TRUE when we got here and might
-     * become FALSE if we got to PLAYING. */
+     * update the need_preroll var since it was %TRUE when we got here and might
+     * become %FALSE if we got to PLAYING. */
     GST_DEBUG_OBJECT (basesink, "nothing to commit, now in %s",
         gst_element_state_get_name (current));
     switch (current) {
@@ -1527,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 */
@@ -1586,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;
 
@@ -1594,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;
 
@@ -1730,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,
@@ -1767,7 +2002,7 @@ handle_stepping (GstBaseSink * sink, GstSegment * segment,
 
 /* with STREAM_LOCK, PREROLL_LOCK
  *
- * Returns TRUE if the object needs synchronisation and takes therefore
+ * Returns %TRUE if the object needs synchronisation and takes therefore
  * part in prerolling.
  *
  * rsstart/rsstop contain the start/stop in stream time.
@@ -1824,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 */
@@ -1857,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);
 
@@ -1959,7 +2194,7 @@ eos_done:
 out_of_segment:
   {
     /* we usually clip in the chain function already but stepping could cause
-     * the segment to be updated later. we return FALSE so that we don't try
+     * the segment to be updated later. we return %FALSE so that we don't try
      * to sync on it. */
     GST_LOG_OBJECT (basesink, "buffer skipped, not in segment");
     return FALSE;
@@ -1980,7 +2215,7 @@ gst_base_sink_adjust_time (GstBaseSink * basesink, GstClockTime time)
 
   time += basesink->priv->latency;
 
-  /* apply offset, be carefull for underflows */
+  /* apply offset, be careful for underflows */
   ts_offset = basesink->priv->ts_offset;
   if (ts_offset < 0) {
     ts_offset = -ts_offset;
@@ -2004,14 +2239,14 @@ gst_base_sink_adjust_time (GstBaseSink * basesink, GstClockTime time)
  * gst_base_sink_wait_clock:
  * @sink: the sink
  * @time: the running_time to be reached
- * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL
+ * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL
  *
  * This function will block until @time is reached. It is usually called by
  * subclasses that use their own internal synchronisation.
  *
- * If @time is not valid, no sycnhronisation is done and #GST_CLOCK_BADTIME is
+ * If @time is not valid, no synchronisation is done and %GST_CLOCK_BADTIME is
  * returned. Likewise, if synchronisation is disabled in the element or there
- * is no clock, no synchronisation is done and #GST_CLOCK_BADTIME is returned.
+ * is no clock, no synchronisation is done and %GST_CLOCK_BADTIME is returned.
  *
  * This function should only be called with the PREROLL_LOCK held, like when
  * receiving an EOS event in the #GstBaseSinkClass.event() vmethod or when
@@ -2051,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);
@@ -2111,15 +2343,22 @@ 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
+ * 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_FLUSHING).
+ * returns %GST_FLOW_FLUSHING).
  *
  * This function should only be called with the PREROLL_LOCK held, like in the
  * render function.
  *
- * Returns: #GST_FLOW_OK if the preroll completed and processing can
+ * Returns: %GST_FLOW_OK if the preroll completed and processing can
  * continue. Any other return value should be returned from the render vmethod.
  */
 GstFlowReturn
@@ -2164,7 +2403,7 @@ step_unlocked:
  *
  * This function should be called with the PREROLL_LOCK held.
  *
- * Returns: #GST_FLOW_OK if the preroll completed and processing can
+ * Returns: %GST_FLOW_OK if the preroll completed and processing can
  * continue. Any other return value should be returned from the render vmethod.
  */
 GstFlowReturn
@@ -2182,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,
@@ -2240,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:
@@ -2254,7 +2497,7 @@ preroll_failed:
  * gst_base_sink_wait:
  * @sink: the sink
  * @time: the running_time to be reached
- * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL
+ * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL
  *
  * This function will wait for preroll to complete and will then block until @time
  * is reached. It is usually called by subclasses that use their own internal
@@ -2311,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");
@@ -2340,7 +2583,7 @@ flushing:
  * immediately returns GST_FLOW_OK.
  *
  * for objects that arrive later than max-lateness to be synchronized to the
- * clock have the @late boolean set to TRUE.
+ * clock have the @late boolean set to %TRUE.
  *
  * This function keeps a running average of the jitter (the diff between the
  * clock time and the requested sync time). The jitter is negative for
@@ -2396,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;
@@ -2412,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:
@@ -2489,7 +2733,7 @@ again:
 
   /* check if the object should be dropped */
   *late = gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
-      status, jitter);
+      status, jitter, TRUE);
 
 done:
   return GST_FLOW_OK;
@@ -2584,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 */
@@ -2609,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) {
@@ -2645,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) {
@@ -2693,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;
@@ -2710,12 +2946,12 @@ gst_base_sink_reset_qos (GstBaseSink * sink)
  *
  * status and jitter contain the return values from the clock wait.
  *
- * returns TRUE if the buffer was too late.
+ * returns %TRUE if the buffer was too late.
  */
 static gboolean
 gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj,
     GstClockTime rstart, GstClockTime rstop,
-    GstClockReturn status, GstClockTimeDiff jitter)
+    GstClockReturn status, GstClockTimeDiff jitter, gboolean render)
 {
   gboolean late;
   guint64 max_lateness;
@@ -2774,7 +3010,7 @@ gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj,
   }
 
 done:
-  if (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_render_time)) {
+  if (render && (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_render_time))) {
     priv->last_render_time = rstart;
     /* the next allowed input timestamp */
     if (priv->throttle_time > 0)
@@ -2805,33 +3041,43 @@ 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)
+gst_base_sink_update_start_time (GstBaseSink * basesink)
 {
-  GstBaseSinkPrivate *priv;
-
-  priv = basesink->priv;
-
-  if (start) {
-    priv->start = gst_util_get_timestamp ();
-  } else {
-    GstClockTime elapsed;
+  GstClock *clock;
 
-    priv->stop = gst_util_get_timestamp ();
+  GST_OBJECT_LOCK (basesink);
+  if (GST_STATE (basesink) == GST_STATE_PLAYING
+      && (clock = GST_ELEMENT_CLOCK (basesink))) {
+    GstClockTime now;
 
-    elapsed = GST_CLOCK_DIFF (priv->start, priv->stop);
+    gst_object_ref (clock);
+    GST_OBJECT_UNLOCK (basesink);
 
-    if (!GST_CLOCK_TIME_IS_VALID (priv->avg_render))
-      priv->avg_render = elapsed;
-    else
-      priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed);
+    /* calculate the time when we stopped */
+    now = gst_clock_get_time (clock);
+    gst_object_unref (clock);
 
-    GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
-        "avg_render: %" GST_TIME_FORMAT, GST_TIME_ARGS (priv->avg_render));
+    GST_OBJECT_LOCK (basesink);
+    /* store the current running time */
+    if (GST_ELEMENT_START_TIME (basesink) != GST_CLOCK_TIME_NONE) {
+      if (now != GST_CLOCK_TIME_NONE)
+        GST_ELEMENT_START_TIME (basesink) =
+            now - GST_ELEMENT_CAST (basesink)->base_time;
+      else
+        GST_WARNING_OBJECT (basesink,
+            "Clock %s returned invalid time, can't calculate "
+            "running_time when going to the PAUSED state",
+            GST_OBJECT_NAME (clock));
+    }
+    GST_DEBUG_OBJECT (basesink,
+        "start_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT
+        ", base_time %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (GST_ELEMENT_START_TIME (basesink)),
+        GST_TIME_ARGS (now),
+        GST_TIME_ARGS (GST_ELEMENT_CAST (basesink)->base_time));
   }
+  GST_OBJECT_UNLOCK (basesink);
 }
 
 static void
@@ -2850,6 +3096,7 @@ gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
    * prerolled buffer */
   basesink->playing_async = TRUE;
   if (basesink->priv->async_enabled) {
+    gst_base_sink_update_start_time (basesink);
     gst_element_lost_state (GST_ELEMENT_CAST (basesink));
   } else {
     /* start time reset in above case as well;
@@ -2858,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);
 }
 
@@ -2898,7 +3146,7 @@ static GstFlowReturn
 gst_base_sink_default_wait_event (GstBaseSink * basesink, GstEvent * event)
 {
   GstFlowReturn ret;
-  gboolean late, step_end;
+  gboolean late, step_end = FALSE;
 
   ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (event),
       &late, &step_end);
@@ -2986,30 +3234,47 @@ gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
     {
       GstMessage *message;
       guint32 seqnum;
+      guint group_id;
 
       seqnum = gst_event_get_seqnum (event);
       GST_DEBUG_OBJECT (basesink, "Now posting STREAM_START (seqnum:%d)",
           seqnum);
       message = gst_message_new_stream_start (GST_OBJECT_CAST (basesink));
+      if (gst_event_parse_group_id (event, &group_id)) {
+        gst_message_set_group_id (message, group_id);
+      } else {
+        GST_FIXME_OBJECT (basesink, "stream-start event without group-id. "
+            "Consider implementing group-id handling in the upstream "
+            "elements");
+      }
       gst_message_set_seqnum (message, seqnum);
       gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
       break;
     }
     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:
@@ -3021,9 +3286,10 @@ gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
       /* 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,
+      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:
@@ -3180,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.
@@ -3201,12 +3460,11 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
 {
   GstBaseSinkClass *bclass;
   GstBaseSinkPrivate *priv = basesink->priv;
-  GstFlowReturn ret;
+  GstFlowReturn ret = GST_FLOW_OK;
   GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
   GstSegment *segment;
   GstBuffer *sync_buf;
-  gint do_qos;
-  gboolean late, step_end;
+  gboolean late, step_end, prepared = FALSE;
 
   if (G_UNLIKELY (basesink->flushing))
     goto flushing;
@@ -3215,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);
@@ -3260,29 +3523,71 @@ 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;
   }
 
   if (bclass->prepare || bclass->prepare_list) {
-    gboolean late = FALSE;
-    gboolean do_sync = TRUE, stepped = FALSE, step_end = FALSE, syncable = TRUE;
+    gboolean do_sync = TRUE, stepped = FALSE, syncable = TRUE;
     GstClockTime sstart, sstop, rstart, rstop, rnext;
     GstStepInfo *current;
 
+    late = FALSE;
+    step_end = FALSE;
+
     current = &priv->current_step;
     syncable =
         gst_base_sink_get_sync_times (basesink, obj, &sstart, &sstop, &rstart,
         &rstop, &rnext, &do_sync, &stepped, current, &step_end);
 
-    if (!stepped && syncable && do_sync)
-      late =
-          gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
-          GST_CLOCK_EARLY, 0);
-    if (late)
+    if (G_UNLIKELY (stepped))
+      goto dropped;
+
+    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;
 
     if (!is_list) {
@@ -3298,6 +3603,8 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
           goto prepare_failed;
       }
     }
+
+    prepared = TRUE;
   }
 
 again:
@@ -3311,43 +3618,45 @@ again:
   if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto sync_failed;
 
+  /* Don't skip if prepare() was called on time */
+  late = late && !prepared;
+
   /* drop late buffers unconditionally, let's hope it's unlikely */
   if (G_UNLIKELY (late))
     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;
@@ -3386,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");
@@ -3437,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
@@ -3500,7 +3821,7 @@ gst_base_sink_chain_list (GstPad * pad, GstObject * parent,
     guint i, len;
     GstBuffer *buffer;
 
-    GST_INFO_OBJECT (pad, "chaining each buffer in list");
+    GST_LOG_OBJECT (pad, "chaining each buffer in list");
 
     len = gst_buffer_list_length (list);
 
@@ -3674,7 +3995,6 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
     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 (TRUE));
@@ -3694,6 +4014,9 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
     res = FALSE;
   }
 
+  GST_INFO_OBJECT (sink, "seeking done %d: %" GST_SEGMENT_FORMAT, res,
+      &seeksegment);
+
   /* if successful seek, we update our real segment and push
    * out the new segment. */
   if (res) {
@@ -3785,6 +4108,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_base_sink_update_start_time (sink);
       gst_element_lost_state (GST_ELEMENT_CAST (sink));
     } else {
       sink->priv->have_latency = TRUE;
@@ -3795,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) {
@@ -3880,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;
@@ -3999,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;
   }
 
@@ -4094,7 +4417,7 @@ gst_base_sink_negotiate_pull (GstBaseSink * basesink)
   result = FALSE;
 
   /* this returns the intersection between our caps and the peer caps. If there
-   * is no peer, it returns NULL and we can't operate in pull mode so we can
+   * is no peer, it returns %NULL and we can't operate in pull mode so we can
    * fail the negotiation. */
   caps = gst_pad_get_allowed_caps (GST_BASE_SINK_PAD (basesink));
   if (caps == NULL || gst_caps_is_empty (caps))
@@ -4285,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 */
@@ -4293,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;
 }
@@ -4309,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;
@@ -4338,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 */
@@ -4361,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
@@ -4370,14 +4699,35 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
   rate = segment->rate * segment->applied_rate;
   latency = basesink->priv->latency;
 
+  if (in_paused) {
+    /* in paused, use start_time */
+    base_time = GST_ELEMENT_START_TIME (basesink);
+    GST_DEBUG_OBJECT (basesink, "in paused, using start time %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (base_time));
+  } else if (with_clock) {
+    /* else use clock when needed */
+    base_time = GST_ELEMENT_CAST (basesink)->base_time;
+    GST_DEBUG_OBJECT (basesink, "using clock and base time %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (base_time));
+  } else {
+    /* else, no sync or clock -> no base time */
+    GST_DEBUG_OBJECT (basesink, "no sync or no clock");
+    base_time = -1;
+  }
+
+  /* no base_time, we can't calculate running_time, use last seem timestamp to report
+   * time */
+  if (base_time == -1)
+    last_seen = TRUE;
+
   if (oformat == GST_FORMAT_TIME) {
     gint64 start, stop;
 
     start = basesink->priv->current_sstart;
     stop = basesink->priv->current_sstop;
 
-    if (in_paused) {
-      /* in paused we use the last position as a lower bound */
+    if (last_seen) {
+      /* when we don't use the clock, we use the last position as a lower bound */
       if (stop == -1 || segment->rate > 0.0)
         last = start;
       else
@@ -4386,7 +4736,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
       GST_DEBUG_OBJECT (basesink, "in PAUSED using last %" GST_TIME_FORMAT,
           GST_TIME_ARGS (last));
     } else {
-      /* in playing, use last stop time as upper bound */
+      /* in playing and paused, use last stop time as upper bound */
       if (start == -1 || segment->rate > 0.0)
         last = stop;
       else
@@ -4396,33 +4746,12 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
           GST_TIME_ARGS (last));
     }
   } else {
-    /* convert last stop to stream time */
+    /* convert position to stream time */
     last = gst_segment_to_stream_time (segment, oformat, segment->position);
 
     GST_DEBUG_OBJECT (basesink, "in using last %" G_GINT64_FORMAT, last);
   }
 
-  if (in_paused) {
-    /* in paused, use start_time */
-    base_time = GST_ELEMENT_START_TIME (basesink);
-    GST_DEBUG_OBJECT (basesink, "in paused, using start time %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (base_time));
-  } else if (with_clock) {
-    /* else use clock when needed */
-    base_time = GST_ELEMENT_CAST (basesink)->base_time;
-    GST_DEBUG_OBJECT (basesink, "using clock and base time %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (base_time));
-  } else {
-    /* else, no sync or clock -> no base time */
-    GST_DEBUG_OBJECT (basesink, "no sync or no clock");
-    base_time = -1;
-  }
-
-  /* no base_time, we can't calculate running_time, use last seem timestamp to report
-   * time */
-  if (base_time == -1)
-    last_seen = TRUE;
-
   /* need to release the object lock before we can get the time,
    * a clock might take the LOCK of the provider, which could be
    * a basesink subclass. */
@@ -4482,16 +4811,14 @@ 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;
 
-    if (in_paused) {
-      /* never report less than segment values in paused */
-      if (last != -1)
-        *cur = MAX (last, *cur);
-    } else {
-      /* never report more than last seen position in playing */
-      if (last != -1)
+    /* never report more than last seen position */
+    if (last != -1) {
+      if (rate > 0.0)
         *cur = MIN (last, *cur);
+      else
+        *cur = MAX (last, *cur);
     }
 
     GST_DEBUG_OBJECT (basesink,
@@ -4679,8 +5006,21 @@ default_element_query (GstElement * element, GstQuery * query)
     case GST_QUERY_SEGMENT:
     {
       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);
+        GstFormat format;
+        gint64 start, stop;
+
+        format = basesink->segment.format;
+
+        start =
+            gst_segment_to_stream_time (&basesink->segment, format,
+            basesink->segment.start);
+        if ((stop = basesink->segment.stop) == -1)
+          stop = basesink->segment.duration;
+        else
+          stop = gst_segment_to_stream_time (&basesink->segment, format, stop);
+
+        gst_query_set_segment (query, basesink->segment.rate, format, start,
+            stop);
         res = TRUE;
       } else {
         res = gst_pad_peer_query (basesink->sinkpad, query);
@@ -4699,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)
@@ -4711,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
@@ -4737,14 +5097,25 @@ gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
       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_DEBUG_OBJECT (basesink, "Checking if requested caps %" GST_PTR_FORMAT
+          " are a subset of pad caps %" GST_PTR_FORMAT " result %d", caps,
+          allowed, subset);
       gst_caps_unref (allowed);
       gst_query_set_accept_caps_result (query, subset);
       res = TRUE;
       break;
     }
     case GST_QUERY_DRAIN:
+    {
+      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),
@@ -4810,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;
@@ -4851,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;
@@ -4896,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);
@@ -4911,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");
@@ -4919,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");
@@ -4953,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");
 
@@ -4967,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");
       }
@@ -4980,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:
@@ -4992,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: