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 971d87a..4368c1a 100644 (file)
@@ -21,6 +21,7 @@
 
 /**
  * SECTION:gstbasesink
+ * @title: GstBaseSink
  * @short_description: Base class for sink elements
  * @see_also: #GstBaseTransform, #GstBaseSrc
  *
@@ -37,7 +38,7 @@
  * #GstBaseSink provides support for exactly one sink pad, which should be
  * named "sink". A sink implementation (subclass of #GstBaseSink) should
  * install a pad template in its class_init function, like so:
- * |[
+ * |[<!-- language="C" -->
  * static void
  * my_element_class_init (GstMyElementClass *klass)
  * {
  *
  *   // sinktemplate should be a #GstStaticPadTemplate with direction
  *   // %GST_PAD_SINK and name "sink"
- *   gst_element_class_add_pad_template (gstelement_class,
- *       gst_static_pad_template_get (&amp;sinktemplate));
+ *   gst_element_class_add_static_pad_template (gstelement_class, &amp;sinktemplate);
  *
  *   gst_element_class_set_static_metadata (gstelement_class,
  *       "Sink name",
  *       "Sink",
  *       "My Sink element",
- *       "The author &lt;my.sink@my.email&gt;");
+ *       "The author <my.sink@my.email>");
  * }
  * ]|
  *
@@ -99,7 +99,7 @@
  * should configure itself to process a specific media type.
  *
  * The #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() virtual methods
- * will be called when resources should be allocated. Any 
+ * will be called when resources should be allocated. Any
  * #GstBaseSinkClass.preroll(), #GstBaseSinkClass.render() and
  * #GstBaseSinkClass.set_caps() function will be called between the
  * #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() calls.
 GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
 #define GST_CAT_DEFAULT gst_base_sink_debug
 
-#define GST_BASE_SINK_GET_PRIVATE(obj)  \
-   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))
-
 #define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR
 
 typedef struct
@@ -181,6 +178,7 @@ struct _GstBaseSinkPrivate
   gboolean async_enabled;
   GstClockTimeDiff ts_offset;
   GstClockTime render_delay;
+  GstClockTime processing_deadline;
 
   /* start, stop of current buffer, stream time, used to report position */
   GstClockTime current_sstart;
@@ -202,19 +200,8 @@ struct _GstBaseSinkPrivate
   GstClockTime last_left;
 
   /* running averages go here these are done on running time */
-  GstClockTime avg_pt;
-  GstClockTime avg_duration;
-  gdouble avg_rate;
-  GstClockTime avg_in_diff;
-
-  /* these are done on system time. avg_jitter and avg_render are
-   * compared to eachother to see if the rendering time takes a
-   * huge amount of the processing, If so we are flooded with
-   * buffers. */
-  GstClockTime last_left_systime;
-  GstClockTime avg_jitter;
-  GstClockTime start, stop;
-  GstClockTime avg_render;
+  GstClockTime avg_pt, avg_in_diff;
+  gdouble avg_rate;             /* average with infinite window */
 
   /* number of rendered and dropped frames */
   guint64 rendered;
@@ -223,8 +210,8 @@ struct _GstBaseSinkPrivate
   /* latency stuff */
   GstClockTime latency;
 
-  /* if we already commited the state */
-  gboolean commited;
+  /* if we already committed the state */
+  gboolean committed;
   /* state change to playing ongoing */
   gboolean to_playing;
 
@@ -270,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))
@@ -298,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
 {
@@ -313,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);
@@ -343,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,
@@ -415,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;
@@ -535,6 +539,20 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
           "The maximum bits per second to render (0 = disabled)", 0,
           G_MAXUINT64, DEFAULT_MAX_BITRATE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstBaseSink:processing-deadline:
+   *
+   * Maximum amount of time (in nanoseconds) that the pipeline can take
+   * for processing the buffer. This is added to the latency of live
+   * pipelines.
+   *
+   * Since: 1.16
+   */
+  g_object_class_install_property (gobject_class, PROP_PROCESSING_DEADLINE,
+      g_param_spec_uint64 ("processing-deadline", "Processing deadline",
+          "Maximum processing deadline in nanoseconds", 0, G_MAXUINT64,
+          DEFAULT_PROCESSING_DEADLINE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
@@ -630,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");
@@ -661,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);
 }
 
@@ -728,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.
@@ -751,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
@@ -1100,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;
 
@@ -1109,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 */
@@ -1132,6 +1201,24 @@ 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 */
@@ -1283,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)
@@ -1300,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.
@@ -1363,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)
@@ -1400,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;
@@ -1446,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;
@@ -1487,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) {
@@ -1500,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:
@@ -1659,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;
 
@@ -1667,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;
 
@@ -1803,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,
@@ -1897,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 */
@@ -1930,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);
 
@@ -2124,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);
@@ -2184,6 +2343,13 @@ no_clock:
  * against the clock it must unblock when going from PLAYING to the PAUSED state
  * and call this method before continuing to render the remaining data.
  *
+ * If the #GstBaseSinkClass.render() method can block on something else than
+ * the clock, it must also be ready to unblock immediately on
+ * the #GstBaseSinkClass.unlock() method and cause the
+ * #GstBaseSinkClass.render() method to immediately call this function.
+ * In this case, the subclass must be prepared to continue rendering where it
+ * left off if this function returns %GST_FLOW_OK.
+ *
  * This function will block until a state change to PLAYING happens (in which
  * case this function returns %GST_FLOW_OK) or the processing must be stopped due
  * to a state change to READY or a FLUSH event (in which case this function
@@ -2317,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:
@@ -2388,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");
@@ -2473,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;
@@ -2489,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:
@@ -2661,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 */
@@ -2686,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) {
@@ -2722,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) {
@@ -2770,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;
@@ -2882,42 +3041,14 @@ no_timestamp:
   }
 }
 
-/* called before and after calling the render vmethod. It keeps track of how
- * much time was spent in the render method and is used to check if we are
- * flooded */
-static void
-gst_base_sink_do_render_stats (GstBaseSink * basesink, gboolean start)
-{
-  GstBaseSinkPrivate *priv;
-
-  priv = basesink->priv;
-
-  if (start) {
-    priv->start = gst_util_get_timestamp ();
-  } else {
-    GstClockTime elapsed;
-
-    priv->stop = gst_util_get_timestamp ();
-
-    elapsed = GST_CLOCK_DIFF (priv->start, priv->stop);
-
-    if (!GST_CLOCK_TIME_IS_VALID (priv->avg_render))
-      priv->avg_render = elapsed;
-    else
-      priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed);
-
-    GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
-        "avg_render: %" GST_TIME_FORMAT, GST_TIME_ARGS (priv->avg_render));
-  }
-}
-
 static void
 gst_base_sink_update_start_time (GstBaseSink * basesink)
 {
   GstClock *clock;
 
   GST_OBJECT_LOCK (basesink);
-  if ((clock = GST_ELEMENT_CLOCK (basesink))) {
+  if (GST_STATE (basesink) == GST_STATE_PLAYING
+      && (clock = GST_ELEMENT_CLOCK (basesink))) {
     GstClockTime now;
 
     gst_object_ref (clock);
@@ -3315,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.
@@ -3340,7 +3464,6 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
   GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
   GstSegment *segment;
   GstBuffer *sync_buf;
-  gint do_qos;
   gboolean late, step_end, prepared = FALSE;
 
   if (G_UNLIKELY (basesink->flushing))
@@ -3400,10 +3523,22 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
   GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
       ", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
 
-  /* a dropped buffer does not participate in anything */
+  /* a dropped buffer does not participate in anything. Buffer can only be
+   * dropped if their PTS falls completely outside the segment, while we sync
+   * preferably on DTS */
   if (GST_CLOCK_TIME_IS_VALID (start) && (segment->format == GST_FORMAT_TIME)) {
+    GstClockTime pts = GST_BUFFER_PTS (sync_buf);
+    GstClockTime pts_end = GST_CLOCK_TIME_NONE;
+
+    if (!GST_CLOCK_TIME_IS_VALID (pts))
+      pts = start;
+
+    if (GST_CLOCK_TIME_IS_VALID (end))
+      pts_end = pts + (end - start);
+
     if (G_UNLIKELY (!gst_segment_clip (segment,
-                GST_FORMAT_TIME, start, end, NULL, NULL)))
+                GST_FORMAT_TIME, pts, pts_end, NULL, NULL)
+            && priv->drop_out_of_segment))
       goto out_of_segment;
   }
 
@@ -3423,7 +3558,7 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
     if (G_UNLIKELY (stepped))
       goto dropped;
 
-    if (syncable && do_sync) {
+    if (syncable && do_sync && gst_base_sink_get_sync (basesink)) {
       GstClock *clock;
 
       GST_OBJECT_LOCK (basesink);
@@ -3446,6 +3581,12 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
       }
     }
 
+    /* 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;
 
@@ -3485,25 +3626,20 @@ again:
     goto dropped;
 
   if (priv->max_bitrate) {
-    if (is_list) {
-      gst_buffer_list_foreach (GST_BUFFER_LIST_CAST (obj),
-          (GstBufferListFunc) count_list_bytes, priv);
-    } else {
-      priv->rc_accumulated += gst_buffer_get_size (GST_BUFFER_CAST (obj));
-    }
+    gsize size;
+
+    if (is_list)
+      size = gst_buffer_list_calculate_size (GST_BUFFER_LIST_CAST (obj));
+    else
+      size = gst_buffer_get_size (GST_BUFFER_CAST (obj));
+
+    priv->rc_accumulated += size;
     priv->rc_next = priv->rc_time + gst_util_uint64_scale (priv->rc_accumulated,
         8 * GST_SECOND, priv->max_bitrate);
   }
 
-  /* read once, to get same value before and after */
-  do_qos = g_atomic_int_get (&priv->qos_enabled);
-
   GST_DEBUG_OBJECT (basesink, "rendering object %p", obj);
 
-  /* record rendering time for QoS and stats */
-  if (do_qos)
-    gst_base_sink_do_render_stats (basesink, TRUE);
-
   if (!is_list) {
     /* For buffer lists do not set last buffer for now. */
     gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj));
@@ -3522,9 +3658,6 @@ again:
     gst_base_sink_set_last_buffer_list (basesink, buffer_list);
   }
 
-  if (do_qos)
-    gst_base_sink_do_render_stats (basesink, FALSE);
-
   if (ret == GST_FLOW_STEP)
     goto again;
 
@@ -3619,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
@@ -4066,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;
@@ -4185,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;
   }
 
@@ -4471,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 */
@@ -4479,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;
 }
@@ -4495,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;
@@ -4547,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
@@ -4668,7 +4811,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
     if (rate < 0.0)
       time += duration;
 
-    *cur = time + gst_guint64_to_gdouble (now - base_time) * rate;
+    *cur = time + offset + gst_guint64_to_gdouble (now - base_time) * rate;
 
     /* never report more than last seen position */
     if (last != -1) {
@@ -4900,13 +5043,20 @@ 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
@@ -5031,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;
@@ -5072,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;
@@ -5137,7 +5287,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
               "PLAYING to PAUSED, we are not prerolled");
           basesink->playing_async = TRUE;
           basesink->need_preroll = TRUE;
-          priv->commited = FALSE;
+          priv->committed = FALSE;
           priv->call_preroll = TRUE;
           if (priv->async_enabled) {
             GST_DEBUG_OBJECT (basesink, "doing async state change");
@@ -5174,7 +5324,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition)
       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");
 
@@ -5186,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");
       }
@@ -5212,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: