Port gtk-doc comments to their equivalent markdown syntax
[platform/upstream/gstreamer.git] / libs / gst / base / gstbasesink.c
index 2466acd..5432450 100644 (file)
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 /**
- * SECTION:gstbasesink:
+ * 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));
- *   // see #GstElementDetails
- *   gst_element_class_set_details (gstelement_class, &amp;details);
+ *   // %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 <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.
  *
  * After synchronisation the virtual method #GstBaseSinkClass.render() will be
  * called. Subclasses should minimally implement this method.
  *
- * Since 0.10.3 subclasses that synchronise on the clock in the
- * #GstBaseSinkClass.render() method are supported as well. These classes
- * typically receive a buffer in the render method and can then potentially
- * block on the clock while rendering. A typical example is an audiosink.
- * Since 0.10.11 these subclasses can use gst_base_sink_wait_preroll() to
- * perform the blocking wait.
+ * Subclasses that synchronise on the clock in the #GstBaseSinkClass.render()
+ * method are supported as well. These classes typically receive a buffer in
+ * the render method and can then potentially block on the clock while
+ * rendering. A typical example is an audiosink.
+ * These subclasses can use gst_base_sink_wait_preroll() to perform the
+ * blocking wait.
  *
  * Upon receiving the EOS event in the PLAYING state, #GstBaseSink will wait
  * for the clock to reach the time indicated by the stop time of the last
  * 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_NEWSEGMENT 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.
  *
  * information can then be used by upstream elements to reduce their processing
  * rate, for example.
  *
- * Since 0.10.15 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)
+ * 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.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -200,19 +202,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;
@@ -236,6 +227,7 @@ struct _GstBaseSinkPrivate
   gint enable_last_sample;      /* atomic */
   GstBuffer *last_buffer;
   GstCaps *last_caps;
+  GstBufferList *last_buffer_list;
 
   /* negotiated caps */
   GstCaps *caps;
@@ -261,6 +253,14 @@ struct _GstBaseSinkPrivate
   /* for throttling and QoS */
   GstClockTime earliest_in_time;
   GstClockTime throttle_time;
+
+  /* for rate control */
+  guint64 max_bitrate;
+  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))
@@ -269,7 +269,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)
@@ -288,6 +288,8 @@ struct _GstBaseSinkPrivate
 #define DEFAULT_RENDER_DELAY        0
 #define DEFAULT_ENABLE_LAST_SAMPLE  TRUE
 #define DEFAULT_THROTTLE_TIME       0
+#define DEFAULT_MAX_BITRATE         0
+#define DEFAULT_DROP_OUT_OF_SEGMENT TRUE
 
 enum
 {
@@ -302,6 +304,7 @@ enum
   PROP_BLOCKSIZE,
   PROP_RENDER_DELAY,
   PROP_THROTTLE_TIME,
+  PROP_MAX_BITRATE,
   PROP_LAST
 };
 
@@ -377,7 +380,7 @@ static gboolean gst_base_sink_pad_activate_mode (GstPad * pad,
     GstObject * parent, GstPadMode mode, gboolean active);
 static gboolean gst_base_sink_default_event (GstBaseSink * basesink,
     GstEvent * event);
-static GstFlowReturn gst_base_sink_default_wait_eos (GstBaseSink * basesink,
+static GstFlowReturn gst_base_sink_default_wait_event (GstBaseSink * basesink,
     GstEvent * event);
 static gboolean gst_base_sink_event (GstPad * pad, GstObject * parent,
     GstEvent * event);
@@ -393,7 +396,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)
@@ -432,12 +435,10 @@ 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.
-   *
-   * Since: 0.10.15
    */
   g_object_class_install_property (gobject_class, PROP_ASYNC,
       g_param_spec_boolean ("async", "Async",
@@ -449,8 +450,6 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    * Controls the final synchronisation, a negative value will render the buffer
    * earlier while a positive value delays playback. This property can be
    * used to fix synchronisation in bad files.
-   *
-   * Since: 0.10.15
    */
   g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
       g_param_spec_int64 ("ts-offset", "TS Offset",
@@ -460,12 +459,10 @@ 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.
-   *
-   * Since: 0.10.30
    */
   g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_SAMPLE,
       g_param_spec_boolean ("enable-last-sample", "Enable Last Buffer",
@@ -477,9 +474,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.
-   *
-   * Since: 0.10.15
+   * 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",
@@ -489,10 +484,8 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    * GstBaseSink:blocksize:
    *
    * The amount of bytes to pull when operating in pull mode.
-   *
-   * Since: 0.10.22
    */
-  /* 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,
@@ -503,8 +496,6 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    * The additional delay between synchronisation and actual rendering of the
    * media. This property will add additional latency to the device in order to
    * make other sinks compensate for the delay.
-   *
-   * Since: 0.10.22
    */
   g_object_class_install_property (gobject_class, PROP_RENDER_DELAY,
       g_param_spec_uint64 ("render-delay", "Render Delay",
@@ -516,13 +507,26 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
    * The time to insert between buffers. This property can be used to control
    * the maximum amount of buffers per second to render. Setting this property
    * to a value bigger than 0 will make the sink create THROTTLE QoS events.
-   *
-   * Since: 0.10.33
    */
   g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME,
       g_param_spec_uint64 ("throttle-time", "Throttle time",
-          "The time to keep between rendered buffers", 0, G_MAXUINT64,
-          DEFAULT_THROTTLE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          "The time to keep between rendered buffers (0 = disabled)", 0,
+          G_MAXUINT64, DEFAULT_THROTTLE_TIME,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstBaseSink:max-bitrate:
+   *
+   * Control the maximum amount of bits that will be rendered per second.
+   * 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.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));
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
@@ -537,7 +541,7 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
   klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_times);
   klass->query = GST_DEBUG_FUNCPTR (gst_base_sink_default_query);
   klass->event = GST_DEBUG_FUNCPTR (gst_base_sink_default_event);
-  klass->wait_eos = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_eos);
+  klass->wait_event = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_event);
 
   /* Registering debug symbols for function pointers */
   GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_fixate);
@@ -653,6 +657,9 @@ gst_base_sink_init (GstBaseSink * basesink, gpointer g_class)
   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);
 }
@@ -676,12 +683,10 @@ 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.
- *
- * Since: 0.10.4
  */
 void
 gst_base_sink_set_sync (GstBaseSink * sink, gboolean sync)
@@ -700,9 +705,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.
- *
- * Since: 0.10.4
+ * Returns: %TRUE if the sink is configured to synchronize against the clock.
  */
 gboolean
 gst_base_sink_get_sync (GstBaseSink * sink)
@@ -719,6 +722,60 @@ 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)
+{
+  GstBaseSinkPrivate *priv;
+
+  g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+  priv = GST_BASE_SINK_GET_PRIVATE (sink);
+
+  GST_OBJECT_LOCK (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)
+{
+  GstBaseSinkPrivate *priv;
+  gboolean res;
+
+  g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
+
+  priv = GST_BASE_SINK_GET_PRIVATE (sink);
+
+  GST_OBJECT_LOCK (sink);
+  res = 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.
@@ -727,8 +784,6 @@ gst_base_sink_get_sync (GstBaseSink * sink)
  * used to decide if a buffer should be dropped or not based on the
  * buffer timestamp and the current clock time. A value of -1 means
  * an unlimited time.
- *
- * Since: 0.10.4
  */
 void
 gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
@@ -750,8 +805,6 @@ gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
  * Returns: The maximum time in nanoseconds that a buffer can be late
  * before it is dropped and not rendered. A value of -1 means an
  * unlimited time.
- *
- * Since: 0.10.4
  */
 gint64
 gst_base_sink_get_max_lateness (GstBaseSink * sink)
@@ -773,8 +826,6 @@ gst_base_sink_get_max_lateness (GstBaseSink * sink)
  * @enabled: the new qos value.
  *
  * Configures @sink to send Quality-of-Service events upstream.
- *
- * Since: 0.10.5
  */
 void
 gst_base_sink_set_qos_enabled (GstBaseSink * sink, gboolean enabled)
@@ -791,9 +842,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.
- *
- * Since: 0.10.5
+ * Returns: %TRUE if the sink is configured to perform Quality-of-Service.
  */
 gboolean
 gst_base_sink_is_qos_enabled (GstBaseSink * sink)
@@ -812,12 +861,10 @@ 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.
- *
- * Since: 0.10.15
  */
 void
 gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled)
@@ -837,10 +884,8 @@ 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.
- *
- * Since: 0.10.15
  */
 gboolean
 gst_base_sink_is_async_enabled (GstBaseSink * sink)
@@ -863,8 +908,6 @@ gst_base_sink_is_async_enabled (GstBaseSink * sink)
  * render buffers earlier than their timestamp. A positive value will delay
  * rendering. This function can be used to fix playback of badly timestamped
  * buffers.
- *
- * Since: 0.10.15
  */
 void
 gst_base_sink_set_ts_offset (GstBaseSink * sink, GstClockTimeDiff offset)
@@ -884,8 +927,6 @@ gst_base_sink_set_ts_offset (GstBaseSink * sink, GstClockTimeDiff offset)
  * Get the synchronisation offset of @sink.
  *
  * Returns: The synchronisation offset.
- *
- * Since: 0.10.15
  */
 GstClockTimeDiff
 gst_base_sink_get_ts_offset (GstBaseSink * sink)
@@ -912,11 +953,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.
- *
- * Since: 0.10.15
+ * 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)
@@ -926,7 +965,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);
   }
@@ -964,6 +1012,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)
 {
@@ -975,6 +1049,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
@@ -982,8 +1068,6 @@ gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
  *
  * Configures @sink to store the last received sample in the last-sample
  * property.
- *
- * Since: 0.10.30
  */
 void
 gst_base_sink_set_last_sample_enabled (GstBaseSink * sink, gboolean enabled)
@@ -995,6 +1079,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);
   }
 }
@@ -1006,9 +1091,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.
- *
- * Since: 0.10.30
+ * Returns: %TRUE if the sink is configured to store the last received sample.
  */
 gboolean
 gst_base_sink_is_last_sample_enabled (GstBaseSink * sink)
@@ -1025,8 +1108,6 @@ gst_base_sink_is_last_sample_enabled (GstBaseSink * sink)
  * Get the currently configured latency.
  *
  * Returns: The configured latency.
- *
- * Since: 0.10.12
  */
 GstClockTime
 gst_base_sink_get_latency (GstBaseSink * sink)
@@ -1049,19 +1130,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.
- *
- * Since: 0.10.12
+ * Returns: %TRUE if the query succeeded.
  */
 gboolean
 gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
@@ -1104,8 +1183,7 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
       }
       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;
       }
@@ -1157,8 +1235,6 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
  * other sinks will adjust their latency to delay the rendering of their media.
  *
  * This function is usually called by subclasses.
- *
- * Since: 0.10.21
  */
 void
 gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay)
@@ -1189,8 +1265,6 @@ gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay)
  * information about the render delay.
  *
  * Returns: the render delay of @sink.
- *
- * Since: 0.10.21
  */
 GstClockTime
 gst_base_sink_get_render_delay (GstBaseSink * sink)
@@ -1213,10 +1287,8 @@ 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.
- *
- * Since: 0.10.22
  */
-/* 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)
 {
@@ -1236,10 +1308,8 @@ gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
  * mode.
  *
  * Returns: the number of bytes @sink will pull in pull mode.
- *
- * Since: 0.10.22
  */
-/* 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)
 {
@@ -1261,9 +1331,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. 
- *
- * Since: 0.10.33
+ * will render.
  */
 void
 gst_base_sink_set_throttle_time (GstBaseSink * sink, guint64 throttle)
@@ -1280,12 +1348,10 @@ 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.
- *
- * Since: 0.10.33
  */
 guint64
 gst_base_sink_get_throttle_time (GstBaseSink * sink)
@@ -1301,6 +1367,50 @@ gst_base_sink_get_throttle_time (GstBaseSink * sink)
   return res;
 }
 
+/**
+ * gst_base_sink_set_max_bitrate:
+ * @sink: a #GstBaseSink
+ * @max_bitrate: the max_bitrate in bits per second
+ *
+ * Set the maximum amount of bits per second that the sink will render.
+ *
+ * Since: 1.2
+ */
+void
+gst_base_sink_set_max_bitrate (GstBaseSink * sink, guint64 max_bitrate)
+{
+  g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+  GST_OBJECT_LOCK (sink);
+  sink->priv->max_bitrate = max_bitrate;
+  GST_LOG_OBJECT (sink, "set max_bitrate to %" G_GUINT64_FORMAT, max_bitrate);
+  GST_OBJECT_UNLOCK (sink);
+}
+
+/**
+ * gst_base_sink_get_max_bitrate:
+ * @sink: a #GstBaseSink
+ *
+ * Get the maximum amount of bits per second that the sink will render.
+ *
+ * Returns: the maximum number of bits per second @sink will render.
+ *
+ * Since: 1.2
+ */
+guint64
+gst_base_sink_get_max_bitrate (GstBaseSink * sink)
+{
+  guint64 res;
+
+  g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
+
+  GST_OBJECT_LOCK (sink);
+  res = sink->priv->max_bitrate;
+  GST_OBJECT_UNLOCK (sink);
+
+  return res;
+}
+
 static void
 gst_base_sink_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
@@ -1335,6 +1445,9 @@ gst_base_sink_set_property (GObject * object, guint prop_id,
     case PROP_THROTTLE_TIME:
       gst_base_sink_set_throttle_time (sink, g_value_get_uint64 (value));
       break;
+    case PROP_MAX_BITRATE:
+      gst_base_sink_set_max_bitrate (sink, g_value_get_uint64 (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1364,7 +1477,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));
@@ -1378,6 +1491,9 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_THROTTLE_TIME:
       g_value_set_uint64 (value, gst_base_sink_get_throttle_time (sink));
       break;
+    case PROP_MAX_BITRATE:
+      g_value_set_uint64 (value, gst_base_sink_get_max_bitrate (sink));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1469,7 +1585,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));
@@ -1484,8 +1622,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) {
@@ -1505,6 +1643,11 @@ nothing_pending:
     GST_OBJECT_UNLOCK (basesink);
     return TRUE;
   }
+stopping_unlocked:
+  {
+    GST_OBJECT_LOCK (basesink);
+    goto stopping;
+  }
 stopping:
   {
     /* app is going to READY */
@@ -1552,16 +1695,34 @@ start_stepping (GstBaseSink * sink, GstSegment * segment,
     current->start_start = segment->start;
 
   if (current->format == GST_FORMAT_TIME) {
-    end = current->start + current->amount;
+    /* calculate the running-time when the step operation should stop */
+    if (current->amount != -1)
+      end = current->start + current->amount;
+    else
+      end = -1;
+
     if (!current->flush) {
+      gint64 position;
+
       /* update the segment clipping regions for non-flushing seeks */
       if (segment->rate > 0.0) {
-        segment->stop = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
-        segment->position = segment->stop;
+        if (end != -1)
+          position =
+              gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+              end);
+        else
+          position = segment->stop;
+
+        segment->stop = position;
+        segment->position = position;
       } else {
-        gint64 position;
+        if (end != -1)
+          position =
+              gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+              end);
+        else
+          position = segment->start;
 
-        position = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
         segment->time = position;
         segment->start = position;
         segment->position = position;
@@ -1573,14 +1734,9 @@ start_stepping (GstBaseSink * sink, GstSegment * segment,
   GST_DEBUG_OBJECT (sink, "step started at running_time %" GST_TIME_FORMAT,
       GST_TIME_ARGS (current->start));
 
-  if (current->amount == -1) {
-    GST_DEBUG_OBJECT (sink, "step amount == -1, stop stepping");
-    current->valid = FALSE;
-  } else {
-    GST_DEBUG_OBJECT (sink, "step amount: %" G_GUINT64_FORMAT ", format: %s, "
-        "rate: %f", current->amount, gst_format_get_name (current->format),
-        current->rate);
-  }
+  GST_DEBUG_OBJECT (sink, "step amount: %" G_GUINT64_FORMAT ", format: %s, "
+      "rate: %f", current->amount, gst_format_get_name (current->format),
+      current->rate);
 }
 
 static void
@@ -1651,6 +1807,10 @@ handle_stepping (GstBaseSink * sink, GstSegment * segment,
 {
   gboolean step_end = FALSE;
 
+  /* stepping never stops */
+  if (current->amount == -1)
+    return FALSE;
+
   /* see if we need to skip this buffer because of stepping */
   switch (current->format) {
     case GST_FORMAT_TIME:
@@ -1695,10 +1855,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,
@@ -1732,7 +1896,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.
@@ -1741,13 +1905,14 @@ handle_stepping (GstBaseSink * sink, GstSegment * segment,
 static gboolean
 gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
     GstClockTime * rsstart, GstClockTime * rsstop,
-    GstClockTime * rrstart, GstClockTime * rrstop, gboolean * do_sync,
-    gboolean * stepped, GstStepInfo * step, gboolean * step_end)
+    GstClockTime * rrstart, GstClockTime * rrstop, GstClockTime * rrnext,
+    gboolean * do_sync, gboolean * stepped, GstStepInfo * step,
+    gboolean * step_end)
 {
   GstBaseSinkClass *bclass;
   GstClockTime start, stop;     /* raw start/stop timestamps */
   guint64 cstart, cstop;        /* clipped raw timestamps */
-  guint64 rstart, rstop;        /* clipped timestamps converted to running time */
+  guint64 rstart, rstop, rnext; /* clipped timestamps converted to running time */
   GstClockTime sstart, sstop;   /* clipped timestamps converted to stream time */
   GstFormat format;
   GstBaseSinkPrivate *priv;
@@ -1787,7 +1952,7 @@ again:
           }
         }
 
-        rstart = rstop = priv->eos_rtime;
+        rstart = rstop = rnext = priv->eos_rtime;
         *do_sync = rstart != -1;
         GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT,
             GST_TIME_ARGS (rstart));
@@ -1801,6 +1966,10 @@ again:
         GstClockTime timestamp, duration;
         gst_event_parse_gap (event, &timestamp, &duration);
 
+        GST_DEBUG_OBJECT (basesink, "Got Gap time %" GST_TIME_FORMAT
+            " duration %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
+
         if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
           start = timestamp;
           if (GST_CLOCK_TIME_IS_VALID (duration))
@@ -1873,6 +2042,11 @@ do_times:
   rstart = gst_segment_to_running_time (segment, format, cstart);
   rstop = gst_segment_to_running_time (segment, format, cstop);
 
+  if (GST_CLOCK_TIME_IS_VALID (stop))
+    rnext = rstop;
+  else
+    rnext = rstart;
+
   if (G_UNLIKELY (step->valid)) {
     if (!(*step_end = handle_stepping (basesink, segment, step, &cstart, &cstop,
                 &rstart, &rstop))) {
@@ -1905,6 +2079,7 @@ eos_done:
   *rsstop = sstop;
   *rrstart = rstart;
   *rrstop = rstop;
+  *rrnext = rnext;
 
   /* buffers and EOS always need syncing and preroll */
   return TRUE;
@@ -1913,7 +2088,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;
@@ -1934,7 +2109,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;
@@ -1958,14 +2133,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
@@ -1976,8 +2151,6 @@ gst_base_sink_adjust_time (GstBaseSink * basesink, GstClockTime time)
  * return and is not adjusted with any latency or offset configured in the
  * sink.
  *
- * Since: 0.10.20
- *
  * Returns: #GstClockReturn
  */
 GstClockReturn
@@ -2067,18 +2240,23 @@ 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.
- *
- * Since: 0.10.11
  */
 GstFlowReturn
 gst_base_sink_wait_preroll (GstBaseSink * sink)
@@ -2122,10 +2300,8 @@ 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.
- *
- * Since: 0.10.22
  */
 GstFlowReturn
 gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
@@ -2142,13 +2318,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,
@@ -2214,7 +2394,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
@@ -2229,8 +2409,6 @@ preroll_failed:
  * and will be adjusted with any latency and offset configured in the sink.
  *
  * Returns: #GstFlowReturn
- *
- * Since: 0.10.15
  */
 GstFlowReturn
 gst_base_sink_wait (GstBaseSink * sink, GstClockTime time,
@@ -2302,7 +2480,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
@@ -2317,7 +2495,7 @@ gst_base_sink_do_sync (GstBaseSink * basesink,
   GstClockTimeDiff jitter = 0;
   gboolean syncable;
   GstClockReturn status = GST_CLOCK_OK;
-  GstClockTime rstart, rstop, sstart, sstop, stime;
+  GstClockTime rstart, rstop, rnext, sstart, sstop, stime;
   gboolean do_sync;
   GstBaseSinkPrivate *priv;
   GstFlowReturn ret;
@@ -2327,7 +2505,7 @@ gst_base_sink_do_sync (GstBaseSink * basesink,
   priv = basesink->priv;
 
 do_step:
-  sstart = sstop = rstart = rstop = GST_CLOCK_TIME_NONE;
+  sstart = sstop = rstart = rstop = rnext = GST_CLOCK_TIME_NONE;
   do_sync = TRUE;
   stepped = FALSE;
 
@@ -2339,7 +2517,8 @@ do_step:
 
   /* get timing information for this object against the render segment */
   syncable = gst_base_sink_get_sync_times (basesink, obj,
-      &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, current, step_end);
+      &sstart, &sstop, &rstart, &rstop, &rnext, &do_sync, &stepped, current,
+      step_end);
 
   if (G_UNLIKELY (stepped))
     goto step_skipped;
@@ -2354,7 +2533,7 @@ do_step:
   priv->current_rstop = (GST_CLOCK_TIME_IS_VALID (rstop) ? rstop : rstart);
 
   /* save sync time for eos when the previous object needed sync */
-  priv->eos_rtime = (do_sync ? priv->current_rstop : GST_CLOCK_TIME_NONE);
+  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)) {
@@ -2405,6 +2584,18 @@ again:
   /* adjust for latency */
   stime = gst_base_sink_adjust_time (basesink, rstart);
 
+  /* adjust for rate control */
+  if (priv->rc_next == -1 || (stime != -1 && stime >= priv->rc_next)) {
+    GST_DEBUG_OBJECT (basesink, "reset rc_time to time %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (stime));
+    priv->rc_time = stime;
+    priv->rc_accumulated = 0;
+  } else {
+    GST_DEBUG_OBJECT (basesink, "rate control next %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (priv->rc_next));
+    stime = priv->rc_next;
+  }
+
   /* preroll done, we can sync since we are in PLAYING now. */
   GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %"
       GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT,
@@ -2438,7 +2629,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;
@@ -2533,11 +2724,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 */
@@ -2558,29 +2749,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) {
@@ -2594,9 +2780,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) {
@@ -2642,10 +2827,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;
@@ -2659,12 +2842,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;
@@ -2723,7 +2906,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)
@@ -2754,33 +2937,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
@@ -2799,6 +2992,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;
@@ -2807,6 +3001,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);
 }
 
@@ -2831,6 +3026,7 @@ gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad,
     basesink->have_newsegment = FALSE;
     if (reset_time) {
       gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
+      GST_ELEMENT_START_TIME (basesink) = 0;
     }
   }
   GST_OBJECT_UNLOCK (basesink);
@@ -2843,10 +3039,10 @@ gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad,
 }
 
 static GstFlowReturn
-gst_base_sink_default_wait_eos (GstBaseSink * basesink, GstEvent * event)
+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);
@@ -2854,6 +3050,22 @@ gst_base_sink_default_wait_eos (GstBaseSink * basesink, GstEvent * event)
   return ret;
 }
 
+static GstFlowReturn
+gst_base_sink_wait_event (GstBaseSink * basesink, GstEvent * event)
+{
+  GstFlowReturn ret;
+  GstBaseSinkClass *bclass;
+
+  bclass = GST_BASE_SINK_GET_CLASS (basesink);
+
+  if (G_LIKELY (bclass->wait_event))
+    ret = bclass->wait_event (basesink, event);
+  else
+    ret = GST_FLOW_NOT_SUPPORTED;
+
+  return ret;
+}
+
 static gboolean
 gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
 {
@@ -2889,14 +3101,10 @@ gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
       basesink->priv->received_eos = TRUE;
 
       /* wait for EOS */
-      if (G_LIKELY (bclass->wait_eos)) {
-        GstFlowReturn ret;
-
-        ret = bclass->wait_eos (basesink, event);
-        if (G_UNLIKELY (ret != GST_FLOW_OK)) {
-          result = FALSE;
-          goto done;
-        }
+      if (G_UNLIKELY (gst_base_sink_wait_event (basesink,
+                  event) != GST_FLOW_OK)) {
+        result = FALSE;
+        goto done;
       }
 
       /* the EOS event is completely handled so we mark
@@ -2922,30 +3130,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:
@@ -2957,11 +3182,19 @@ 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:
+    {
+      if (G_UNLIKELY (gst_base_sink_wait_event (basesink,
+                  event) != GST_FLOW_OK))
+        result = FALSE;
+      break;
+    }
     case GST_EVENT_TAG:
     {
       GstTagList *taglist;
@@ -3109,6 +3342,13 @@ 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.
@@ -3123,12 +3363,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;
@@ -3137,7 +3376,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);
@@ -3182,25 +3426,88 @@ 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 completly 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 (!is_list) {
-    if (bclass->prepare) {
-      ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj));
-      if (G_UNLIKELY (ret != GST_FLOW_OK))
-        goto prepare_failed;
+  if (bclass->prepare || bclass->prepare_list) {
+    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 (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);
+      }
     }
-  } else {
-    if (bclass->prepare_list) {
-      ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj));
-      if (G_UNLIKELY (ret != GST_FLOW_OK))
-        goto prepare_failed;
+
+    /* 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) {
+      if (bclass->prepare) {
+        ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj));
+        if (G_UNLIKELY (ret != GST_FLOW_OK))
+          goto prepare_failed;
+      }
+    } else {
+      if (bclass->prepare_list) {
+        ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj));
+        if (G_UNLIKELY (ret != GST_FLOW_OK))
+          goto prepare_failed;
+      }
     }
+
+    prepared = TRUE;
   }
 
 again:
@@ -3214,32 +3521,43 @@ 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;
 
-  /* read once, to get same value before and after */
-  do_qos = g_atomic_int_get (&priv->qos_enabled);
+  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));
+    }
+    priv->rc_next = priv->rc_time + gst_util_uint64_scale (priv->rc_accumulated,
+        8 * GST_SECOND, priv->max_bitrate);
+  }
 
   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;
@@ -3278,6 +3596,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");
@@ -3329,6 +3653,11 @@ dropped:
     }
     goto done;
   }
+preroll_failed:
+  {
+    GST_DEBUG_OBJECT (basesink, "preroll failed: %s", gst_flow_get_name (ret));
+    return ret;
+  }
 }
 
 /* with STREAM_LOCK
@@ -3392,7 +3721,7 @@ gst_base_sink_chain_list (GstPad * pad, GstObject * parent,
     guint i, len;
     GstBuffer *buffer;
 
-    GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
+    GST_LOG_OBJECT (pad, "chaining each buffer in list");
 
     len = gst_buffer_list_length (list);
 
@@ -3442,8 +3771,8 @@ gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
    *     seek format, adjust by the relative seek offset and then convert back to
    *     the processing format
    */
-  GstSeekType cur_type, stop_type;
-  gint64 cur, stop;
+  GstSeekType start_type, stop_type;
+  gint64 start, stop;
   GstSeekFlags flags;
   GstFormat seek_format;
   gdouble rate;
@@ -3451,24 +3780,24 @@ gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
   gboolean res = TRUE;
 
   gst_event_parse_seek (event, &rate, &seek_format, &flags,
-      &cur_type, &cur, &stop_type, &stop);
+      &start_type, &start, &stop_type, &stop);
 
   if (seek_format == segment->format) {
     gst_segment_do_seek (segment, rate, seek_format, flags,
-        cur_type, cur, stop_type, stop, &update);
+        start_type, start, stop_type, stop, &update);
     return TRUE;
   }
 
-  if (cur_type != GST_SEEK_TYPE_NONE) {
-    /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+  if (start_type != GST_SEEK_TYPE_NONE) {
+    /* FIXME: Handle seek_end by converting the input segment vals */
     res =
-        gst_pad_query_convert (sink->sinkpad, seek_format, cur, segment->format,
-        &cur);
-    cur_type = GST_SEEK_TYPE_SET;
+        gst_pad_query_convert (sink->sinkpad, seek_format, start,
+        segment->format, &start);
+    start_type = GST_SEEK_TYPE_SET;
   }
 
   if (res && stop_type != GST_SEEK_TYPE_NONE) {
-    /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+    /* FIXME: Handle seek_end by converting the input segment vals */
     res =
         gst_pad_query_convert (sink->sinkpad, seek_format, stop,
         segment->format, &stop);
@@ -3476,7 +3805,7 @@ gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
   }
 
   /* And finally, configure our output segment in the desired format */
-  gst_segment_do_seek (segment, rate, segment->format, flags, cur_type, cur,
+  gst_segment_do_seek (segment, rate, segment->format, flags, start_type, start,
       stop_type, stop, &update);
 
   if (!res)
@@ -3499,9 +3828,9 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
   gdouble rate;
   GstFormat seek_format, dest_format;
   GstSeekFlags flags;
-  GstSeekType cur_type, stop_type;
+  GstSeekType start_type, stop_type;
   gboolean seekseg_configured = FALSE;
-  gint64 cur, stop;
+  gint64 start, stop;
   gboolean update, res = TRUE;
   GstSegment seeksegment;
 
@@ -3510,7 +3839,7 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
   if (event) {
     GST_DEBUG_OBJECT (sink, "performing seek with event %p", event);
     gst_event_parse_seek (event, &rate, &seek_format, &flags,
-        &cur_type, &cur, &stop_type, &stop);
+        &start_type, &start, &stop_type, &stop);
 
     flush = flags & GST_SEEK_FLAG_FLUSH;
   } else {
@@ -3550,7 +3879,7 @@ gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
         /* The seek format matches our processing format, no need to ask the
          * the subclass to configure the segment. */
         gst_segment_do_seek (&seeksegment, rate, seek_format, flags,
-            cur_type, cur, stop_type, stop, &update);
+            start_type, start, stop_type, stop, &update);
       }
     }
     /* Else, no seek event passed, so we're just (re)starting the
@@ -3566,7 +3895,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));
@@ -3586,12 +3914,15 @@ 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) {
     gst_segment_copy_into (&seeksegment, &sink->segment);
 
-    if (sink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+    if (sink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
       gst_element_post_message (GST_ELEMENT (sink),
           gst_message_new_segment_start (GST_OBJECT (sink),
               sink->segment.format, sink->segment.position));
@@ -3677,6 +4008,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;
@@ -3687,6 +4019,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) {
@@ -3760,7 +4093,7 @@ paused:
     gst_pad_pause_task (pad);
     if (result == GST_FLOW_EOS) {
       /* perform EOS logic */
-      if (basesink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+      if (basesink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
         gst_element_post_message (GST_ELEMENT_CAST (basesink),
             gst_message_new_segment_done (GST_OBJECT_CAST (basesink),
                 basesink->segment.format, basesink->segment.position));
@@ -3772,14 +4105,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;
@@ -3986,7 +4317,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))
@@ -4177,6 +4508,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 */
@@ -4185,8 +4518,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;
 }
@@ -4230,7 +4562,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 */
@@ -4262,30 +4594,6 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
   rate = segment->rate * segment->applied_rate;
   latency = basesink->priv->latency;
 
-  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 (stop == -1 || segment->rate > 0.0)
-        last = start;
-      else
-        last = stop;
-    } else {
-      /* in playing, use last stop time as upper bound */
-      if (start == -1 || segment->rate > 0.0)
-        last = stop;
-      else
-        last = start;
-    }
-  } else {
-    /* convert last stop to stream time */
-    last = gst_segment_to_stream_time (segment, oformat, segment->position);
-  }
-
   if (in_paused) {
     /* in paused, use start_time */
     base_time = GST_ELEMENT_START_TIME (basesink);
@@ -4307,6 +4615,38 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
   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 (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
+        last = stop;
+
+      GST_DEBUG_OBJECT (basesink, "in PAUSED using last %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (last));
+    } else {
+      /* in playing and paused, use last stop time as upper bound */
+      if (start == -1 || segment->rate > 0.0)
+        last = stop;
+      else
+        last = start;
+
+      GST_DEBUG_OBJECT (basesink, "in PLAYING using last %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (last));
+    }
+  } else {
+    /* 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);
+  }
+
   /* 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. */
@@ -4368,14 +4708,12 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
 
     *cur = time + 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,
@@ -4563,8 +4901,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);
@@ -4583,6 +4934,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)
@@ -4595,6 +4965,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
@@ -4621,14 +4992,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),
@@ -4693,6 +5075,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       basesink->eos = FALSE;
       priv->received_eos = FALSE;
       gst_base_sink_reset_qos (basesink);
+      priv->rc_next = -1;
       priv->commited = FALSE;
       priv->call_preroll = TRUE;
       priv->current_step.valid = FALSE;
@@ -4779,11 +5162,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);
@@ -4794,6 +5172,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");
@@ -4802,6 +5181,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
           GST_DEBUG_OBJECT (basesink,
               "PLAYING to PAUSED, we are not prerolled");
           basesink->playing_async = TRUE;
+          basesink->need_preroll = TRUE;
           priv->commited = FALSE;
           priv->call_preroll = TRUE;
           if (priv->async_enabled) {
@@ -4836,6 +5216,7 @@ 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) {
@@ -4863,6 +5244,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:
@@ -4875,6 +5257,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: