downloadbuffer: unlock mutex in error case
[platform/upstream/gstreamer.git] / plugins / elements / gstqueue2.c
index f0dd2ec..51db828 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:element-queue2
+ * @title: queue2
  *
  * Data is queued until one of the limits specified by the
  * #GstQueue2:max-size-buffers, #GstQueue2:max-size-bytes and/or
  * The default queue size limits are 100 buffers, 2MB of data, or
  * two seconds worth of data, whichever is reached first.
  *
- * If you set temp-tmpl to a value such as /tmp/gstreamer-XXXXXX, the element
+ * If you set temp-template to a value such as /tmp/gstreamer-XXXXXX, the element
  * will allocate a random free filename and buffer data in the file.
  * By using this, it will buffer the entire stream data on the file independently
  * of the queue size limits, they will only be used for buffering statistics.
  *
- * Since 0.10.24, setting the temp-location property with a filename is deprecated
- * because it's impossible to securely open a temporary file in this way. The
- * property will still be used to notify the application of the allocated
- * filename, though.
- *
- * Last reviewed on 2009-07-10 (0.10.24)
+ * The temp-location property will be used to notify the application of the
+ * allocated filename.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <unistd.h>
 #endif
 
+#ifdef __BIONIC__               /* Android */
+#undef lseek
+#define lseek lseek64
+#undef off_t
+#define off_t guint64
+#include <fcntl.h>
+#endif
+
 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
@@ -98,7 +103,7 @@ enum
 
 /* other defines */
 #define DEFAULT_BUFFER_SIZE 4096
-#define QUEUE_IS_USING_TEMP_FILE(queue) ((queue)->temp_location_set || (queue)->temp_template != NULL)
+#define QUEUE_IS_USING_TEMP_FILE(queue) ((queue)->temp_template != NULL)
 #define QUEUE_IS_USING_RING_BUFFER(queue) ((queue)->ring_buffer_max_size != 0)  /* for consistency with the above macro */
 #define QUEUE_IS_USING_QUEUE(queue) (!QUEUE_IS_USING_TEMP_FILE(queue) && !QUEUE_IS_USING_RING_BUFFER (queue))
 
@@ -109,9 +114,12 @@ enum
 #define DEFAULT_MAX_SIZE_BYTES     (2 * 1024 * 1024)    /* 2 MB */
 #define DEFAULT_MAX_SIZE_TIME      2 * GST_SECOND       /* 2 seconds */
 #define DEFAULT_USE_BUFFERING      FALSE
+#define DEFAULT_USE_TAGS_BITRATE   FALSE
 #define DEFAULT_USE_RATE_ESTIMATE  TRUE
 #define DEFAULT_LOW_PERCENT        10
 #define DEFAULT_HIGH_PERCENT       99
+#define DEFAULT_LOW_WATERMARK      0.01
+#define DEFAULT_HIGH_WATERMARK     0.99
 #define DEFAULT_TEMP_REMOVE        TRUE
 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
 
@@ -125,16 +133,46 @@ enum
   PROP_MAX_SIZE_BYTES,
   PROP_MAX_SIZE_TIME,
   PROP_USE_BUFFERING,
+  PROP_USE_TAGS_BITRATE,
   PROP_USE_RATE_ESTIMATE,
   PROP_LOW_PERCENT,
   PROP_HIGH_PERCENT,
+  PROP_LOW_WATERMARK,
+  PROP_HIGH_WATERMARK,
   PROP_TEMP_TEMPLATE,
   PROP_TEMP_LOCATION,
   PROP_TEMP_REMOVE,
   PROP_RING_BUFFER_MAX_SIZE,
+  PROP_AVG_IN_RATE,
   PROP_LAST
 };
 
+/* Explanation for buffer levels and percentages:
+ *
+ * The buffering_level functions here return a value in a normalized range
+ * that specifies the queue's current fill level. The range goes from 0 to
+ * MAX_BUFFERING_LEVEL. The low/high watermarks also use this same range.
+ *
+ * This is not to be confused with the buffering_percent value, which is
+ * a *relative* quantity - relative to the low/high watermarks.
+ * buffering_percent = 0% means buffering_level is at the low watermark.
+ * buffering_percent = 100% means buffering_level is at the high watermark.
+ * buffering_percent is used for determining if the fill level has reached
+ * the high watermark, and for producing BUFFERING messages. This value
+ * always uses a 0..100 range (since it is a percentage).
+ *
+ * To avoid future confusions, whenever "buffering level" is mentioned, it
+ * refers to the absolute level which is in the 0..MAX_BUFFERING_LEVEL
+ * range. Whenever "buffering_percent" is mentioned, it refers to the
+ * percentage value that is relative to the low/high watermark. */
+
+/* Using a buffering level range of 0..1000000 to allow for a
+ * resolution in ppm (1 ppm = 0.0001%) */
+#define MAX_BUFFERING_LEVEL 1000000
+
+/* How much 1% makes up in the buffer level range */
+#define BUF_LEVEL_PERCENT_FACTOR ((MAX_BUFFERING_LEVEL) / 100)
+
 #define GST_QUEUE2_CLEAR_LEVEL(l) G_STMT_START {         \
   l.buffers = 0;                                        \
   l.bytes = 0;                                          \
@@ -159,7 +197,7 @@ enum
                         queue->queue.length))
 
 #define GST_QUEUE2_MUTEX_LOCK(q) G_STMT_START {                          \
-  g_mutex_lock (q->qlock);                                              \
+  g_mutex_lock (&q->qlock);                                              \
 } G_STMT_END
 
 #define GST_QUEUE2_MUTEX_LOCK_CHECK(q,res,label) G_STMT_START {         \
@@ -169,13 +207,13 @@ enum
 } G_STMT_END
 
 #define GST_QUEUE2_MUTEX_UNLOCK(q) G_STMT_START {                        \
-  g_mutex_unlock (q->qlock);                                            \
+  g_mutex_unlock (&q->qlock);                                            \
 } G_STMT_END
 
 #define GST_QUEUE2_WAIT_DEL_CHECK(q, res, label) G_STMT_START {         \
   STATUS (queue, q->sinkpad, "wait for DEL");                           \
   q->waiting_del = TRUE;                                                \
-  g_cond_wait (q->item_del, queue->qlock);                              \
+  g_cond_wait (&q->item_del, &queue->qlock);                              \
   q->waiting_del = FALSE;                                               \
   if (res != GST_FLOW_OK) {                                             \
     STATUS (queue, q->srcpad, "received DEL wakeup");                   \
@@ -187,7 +225,7 @@ enum
 #define GST_QUEUE2_WAIT_ADD_CHECK(q, res, label) G_STMT_START {         \
   STATUS (queue, q->srcpad, "wait for ADD");                            \
   q->waiting_add = TRUE;                                                \
-  g_cond_wait (q->item_add, q->qlock);                                  \
+  g_cond_wait (&q->item_add, &q->qlock);                                  \
   q->waiting_add = FALSE;                                               \
   if (res != GST_FLOW_OK) {                                             \
     STATUS (queue, q->srcpad, "received ADD wakeup");                   \
@@ -199,24 +237,33 @@ enum
 #define GST_QUEUE2_SIGNAL_DEL(q) G_STMT_START {                          \
   if (q->waiting_del) {                                                 \
     STATUS (q, q->srcpad, "signal DEL");                                \
-    g_cond_signal (q->item_del);                                        \
+    g_cond_signal (&q->item_del);                                        \
   }                                                                     \
 } G_STMT_END
 
 #define GST_QUEUE2_SIGNAL_ADD(q) G_STMT_START {                          \
   if (q->waiting_add) {                                                 \
     STATUS (q, q->sinkpad, "signal ADD");                               \
-    g_cond_signal (q->item_add);                                        \
+    g_cond_signal (&q->item_add);                                        \
   }                                                                     \
 } G_STMT_END
 
-#define _do_init(bla) \
+#define SET_PERCENT(q, perc) G_STMT_START {                              \
+  if (perc != q->buffering_percent) {                                    \
+    q->buffering_percent = perc;                                         \
+    q->percent_changed = TRUE;                                           \
+    GST_DEBUG_OBJECT (q, "buffering %d percent", perc);                  \
+    get_buffering_stats (q, perc, &q->mode, &q->avg_in, &q->avg_out,     \
+        &q->buffering_left);                                             \
+  }                                                                      \
+} G_STMT_END
+
+#define _do_init \
     GST_DEBUG_CATEGORY_INIT (queue_debug, "queue2", 0, "queue element"); \
     GST_DEBUG_CATEGORY_INIT (queue_dataflow, "queue2_dataflow", 0, \
         "dataflow inside the queue element");
-
-GST_BOILERPLATE_FULL (GstQueue2, gst_queue2, GstElement, GST_TYPE_ELEMENT,
-    _do_init);
+#define gst_queue2_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstQueue2, gst_queue2, GST_TYPE_ELEMENT, _do_init);
 
 static void gst_queue2_finalize (GObject * object);
 
@@ -225,31 +272,32 @@ static void gst_queue2_set_property (GObject * object,
 static void gst_queue2_get_property (GObject * object,
     guint prop_id, GValue * value, GParamSpec * pspec);
 
-static GstFlowReturn gst_queue2_chain (GstPad * pad, GstBuffer * buffer);
-static GstFlowReturn gst_queue2_chain_list (GstPad * pad,
+static GstFlowReturn gst_queue2_chain (GstPad * pad, GstObject * parent,
+    GstBuffer * buffer);
+static GstFlowReturn gst_queue2_chain_list (GstPad * pad, GstObject * parent,
     GstBufferList * buffer_list);
-static GstFlowReturn gst_queue2_bufferalloc (GstPad * pad, guint64 offset,
-    guint size, GstCaps * caps, GstBuffer ** buf);
 static GstFlowReturn gst_queue2_push_one (GstQueue2 * queue);
 static void gst_queue2_loop (GstPad * pad);
 
-static gboolean gst_queue2_handle_sink_event (GstPad * pad, GstEvent * event);
+static GstFlowReturn gst_queue2_handle_sink_event (GstPad * pad,
+    GstObject * parent, GstEvent * event);
+static gboolean gst_queue2_handle_sink_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
 
-static gboolean gst_queue2_handle_src_event (GstPad * pad, GstEvent * event);
-static gboolean gst_queue2_handle_src_query (GstPad * pad, GstQuery * query);
+static gboolean gst_queue2_handle_src_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+static gboolean gst_queue2_handle_src_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
 static gboolean gst_queue2_handle_query (GstElement * element,
     GstQuery * query);
 
-static GstCaps *gst_queue2_getcaps (GstPad * pad);
-static gboolean gst_queue2_acceptcaps (GstPad * pad, GstCaps * caps);
-
-static GstFlowReturn gst_queue2_get_range (GstPad * pad, guint64 offset,
-    guint length, GstBuffer ** buffer);
-static gboolean gst_queue2_src_checkgetrange_function (GstPad * pad);
+static GstFlowReturn gst_queue2_get_range (GstPad * pad, GstObject * parent,
+    guint64 offset, guint length, GstBuffer ** buffer);
 
-static gboolean gst_queue2_src_activate_pull (GstPad * pad, gboolean active);
-static gboolean gst_queue2_src_activate_push (GstPad * pad, gboolean active);
-static gboolean gst_queue2_sink_activate_push (GstPad * pad, gboolean active);
+static gboolean gst_queue2_src_activate_mode (GstPad * pad, GstObject * parent,
+    GstPadMode mode, gboolean active);
+static gboolean gst_queue2_sink_activate_mode (GstPad * pad, GstObject * parent,
+    GstPadMode mode, gboolean active);
 static GstStateChangeReturn gst_queue2_change_state (GstElement * element,
     GstStateChange transition);
 
@@ -257,33 +305,26 @@ static gboolean gst_queue2_is_empty (GstQueue2 * queue);
 static gboolean gst_queue2_is_filled (GstQueue2 * queue);
 
 static void update_cur_level (GstQueue2 * queue, GstQueue2Range * range);
+static void update_in_rates (GstQueue2 * queue, gboolean force);
+static GstMessage *gst_queue2_get_buffering_message (GstQueue2 * queue);
+static void gst_queue2_post_buffering (GstQueue2 * queue);
 
 typedef enum
 {
   GST_QUEUE2_ITEM_TYPE_UNKNOWN = 0,
   GST_QUEUE2_ITEM_TYPE_BUFFER,
   GST_QUEUE2_ITEM_TYPE_BUFFER_LIST,
-  GST_QUEUE2_ITEM_TYPE_EVENT
+  GST_QUEUE2_ITEM_TYPE_EVENT,
+  GST_QUEUE2_ITEM_TYPE_QUERY
 } GstQueue2ItemType;
 
-/* static guint gst_queue2_signals[LAST_SIGNAL] = { 0 }; */
-
-static void
-gst_queue2_base_init (gpointer g_class)
+typedef struct
 {
-  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
-
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&srctemplate));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&sinktemplate));
+  GstQueue2ItemType type;
+  GstMiniObject *item;
+} GstQueue2Item;
 
-  gst_element_class_set_details_simple (gstelement_class, "Queue 2",
-      "Generic",
-      "Simple data queue",
-      "Erik Walthinsen <omega@cse.ogi.edu>, "
-      "Wim Taymans <wim.taymans@gmail.com>");
-}
+/* static guint gst_queue2_signals[LAST_SIGNAL] = { 0 }; */
 
 static void
 gst_queue2_class_init (GstQueue2Class * klass)
@@ -312,21 +353,31 @@ gst_queue2_class_init (GstQueue2Class * klass)
       g_param_spec_uint ("max-size-bytes", "Max. size (kB)",
           "Max. amount of data in the queue (bytes, 0=disable)",
           0, G_MAXUINT, DEFAULT_MAX_SIZE_BYTES,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+          G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_MAX_SIZE_BUFFERS,
       g_param_spec_uint ("max-size-buffers", "Max. size (buffers)",
           "Max. number of buffers in the queue (0=disable)", 0, G_MAXUINT,
           DEFAULT_MAX_SIZE_BUFFERS,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+          G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_MAX_SIZE_TIME,
       g_param_spec_uint64 ("max-size-time", "Max. size (ns)",
           "Max. amount of data in the queue (in ns, 0=disable)", 0, G_MAXUINT64,
-          DEFAULT_MAX_SIZE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          DEFAULT_MAX_SIZE_TIME, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+          G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
       g_param_spec_boolean ("use-buffering", "Use buffering",
           "Emit GST_MESSAGE_BUFFERING based on low-/high-percent thresholds",
-          DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_USE_TAGS_BITRATE,
+      g_param_spec_boolean ("use-tags-bitrate", "Use bitrate from tags",
+          "Use a bitrate from upstream tags to estimate buffer duration if not provided",
+          DEFAULT_USE_TAGS_BITRATE,
+          G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+          G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_USE_RATE_ESTIMATE,
       g_param_spec_boolean ("use-rate-estimate", "Use Rate Estimate",
           "Estimate the bitrate of the stream to calculate time level",
@@ -334,13 +385,25 @@ gst_queue2_class_init (GstQueue2Class * klass)
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_LOW_PERCENT,
       g_param_spec_int ("low-percent", "Low percent",
-          "Low threshold for buffering to start. Only used if use-buffering is True",
-          0, 100, DEFAULT_LOW_PERCENT,
+          "Low threshold for buffering to start. Only used if use-buffering is True "
+          "(Deprecated: use low-watermark instead)",
+          0, 100, DEFAULT_LOW_WATERMARK * 100,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_HIGH_PERCENT,
       g_param_spec_int ("high-percent", "High percent",
+          "High threshold for buffering to finish. Only used if use-buffering is True "
+          "(Deprecated: use high-watermark instead)",
+          0, 100, DEFAULT_HIGH_WATERMARK * 100,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_LOW_WATERMARK,
+      g_param_spec_double ("low-watermark", "Low watermark",
+          "Low threshold for buffering to start. Only used if use-buffering is True",
+          0.0, 1.0, DEFAULT_LOW_WATERMARK,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_HIGH_WATERMARK,
+      g_param_spec_double ("high-watermark", "High watermark",
           "High threshold for buffering to finish. Only used if use-buffering is True",
-          0, 100, DEFAULT_HIGH_PERCENT,
+          0.0, 1.0, DEFAULT_HIGH_WATERMARK,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (gobject_class, PROP_TEMP_TEMPLATE,
@@ -351,16 +414,14 @@ gst_queue2_class_init (GstQueue2Class * klass)
 
   g_object_class_install_property (gobject_class, PROP_TEMP_LOCATION,
       g_param_spec_string ("temp-location", "Temporary File Location",
-          "Location to store temporary files in (Deprecated: Only read this "
-          "property, use temp-template to configure the name template)",
-          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          "Location to store temporary files in (Only read this property, "
+          "use temp-template to configure the name template)",
+          NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   /**
    * GstQueue2:temp-remove
    *
    * When temp-template is set, remove the temporary file when going to READY.
-   *
-   * Since: 0.10.26
    */
   g_object_class_install_property (gobject_class, PROP_TEMP_REMOVE,
       g_param_spec_boolean ("temp-remove", "Remove the Temporary File",
@@ -372,8 +433,6 @@ gst_queue2_class_init (GstQueue2Class * klass)
    *
    * The maximum size of the ring buffer in bytes. If set to 0, the ring
    * buffer is disabled. Default 0.
-   *
-   * Since: 0.10.31
    */
   g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
       g_param_spec_uint64 ("ring-buffer-max-size",
@@ -382,15 +441,34 @@ gst_queue2_class_init (GstQueue2Class * klass)
           0, G_MAXUINT64, DEFAULT_RING_BUFFER_MAX_SIZE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstQueue2:avg-in-rate
+   *
+   * The average input data rate.
+   */
+  g_object_class_install_property (gobject_class, PROP_AVG_IN_RATE,
+      g_param_spec_int64 ("avg-in-rate", "Input data rate (bytes/s)",
+          "Average input data rate (bytes/s)",
+          0, G_MAXINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   /* set several parent class virtual functions */
   gobject_class->finalize = gst_queue2_finalize;
 
+  gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
+  gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
+
+  gst_element_class_set_static_metadata (gstelement_class, "Queue 2",
+      "Generic",
+      "Simple data queue",
+      "Erik Walthinsen <omega@cse.ogi.edu>, "
+      "Wim Taymans <wim.taymans@gmail.com>");
+
   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_queue2_change_state);
   gstelement_class->query = GST_DEBUG_FUNCPTR (gst_queue2_handle_query);
 }
 
 static void
-gst_queue2_init (GstQueue2 * queue, GstQueue2Class * g_class)
+gst_queue2_init (GstQueue2 * queue)
 {
   queue->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
 
@@ -398,36 +476,26 @@ gst_queue2_init (GstQueue2 * queue, GstQueue2Class * g_class)
       GST_DEBUG_FUNCPTR (gst_queue2_chain));
   gst_pad_set_chain_list_function (queue->sinkpad,
       GST_DEBUG_FUNCPTR (gst_queue2_chain_list));
-  gst_pad_set_activatepush_function (queue->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_sink_activate_push));
-  gst_pad_set_event_function (queue->sinkpad,
+  gst_pad_set_activatemode_function (queue->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_queue2_sink_activate_mode));
+  gst_pad_set_event_full_function (queue->sinkpad,
       GST_DEBUG_FUNCPTR (gst_queue2_handle_sink_event));
-  gst_pad_set_getcaps_function (queue->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_getcaps));
-  gst_pad_set_acceptcaps_function (queue->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_acceptcaps));
-  gst_pad_set_bufferalloc_function (queue->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_bufferalloc));
+  gst_pad_set_query_function (queue->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_queue2_handle_sink_query));
+  GST_PAD_SET_PROXY_CAPS (queue->sinkpad);
   gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad);
 
   queue->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
 
-  gst_pad_set_activatepull_function (queue->srcpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_src_activate_pull));
-  gst_pad_set_activatepush_function (queue->srcpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_src_activate_push));
+  gst_pad_set_activatemode_function (queue->srcpad,
+      GST_DEBUG_FUNCPTR (gst_queue2_src_activate_mode));
   gst_pad_set_getrange_function (queue->srcpad,
       GST_DEBUG_FUNCPTR (gst_queue2_get_range));
-  gst_pad_set_checkgetrange_function (queue->srcpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_src_checkgetrange_function));
-  gst_pad_set_getcaps_function (queue->srcpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_getcaps));
-  gst_pad_set_acceptcaps_function (queue->srcpad,
-      GST_DEBUG_FUNCPTR (gst_queue2_acceptcaps));
   gst_pad_set_event_function (queue->srcpad,
       GST_DEBUG_FUNCPTR (gst_queue2_handle_src_event));
   gst_pad_set_query_function (queue->srcpad,
       GST_DEBUG_FUNCPTR (gst_queue2_handle_src_query));
+  GST_PAD_SET_PROXY_CAPS (queue->srcpad);
   gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad);
 
   /* levels */
@@ -438,8 +506,8 @@ gst_queue2_init (GstQueue2 * queue, GstQueue2Class * g_class)
   queue->max_level.rate_time = DEFAULT_MAX_SIZE_TIME;
   queue->use_buffering = DEFAULT_USE_BUFFERING;
   queue->use_rate_estimate = DEFAULT_USE_RATE_ESTIMATE;
-  queue->low_percent = DEFAULT_LOW_PERCENT;
-  queue->high_percent = DEFAULT_HIGH_PERCENT;
+  queue->low_watermark = DEFAULT_LOW_WATERMARK * MAX_BUFFERING_LEVEL;
+  queue->high_watermark = DEFAULT_HIGH_WATERMARK * MAX_BUFFERING_LEVEL;
 
   gst_segment_init (&queue->sink_segment, GST_FORMAT_TIME);
   gst_segment_init (&queue->src_segment, GST_FORMAT_TIME);
@@ -449,25 +517,28 @@ gst_queue2_init (GstQueue2 * queue, GstQueue2Class * g_class)
   queue->sink_tainted = TRUE;
   queue->src_tainted = TRUE;
 
-  queue->srcresult = GST_FLOW_WRONG_STATE;
-  queue->sinkresult = GST_FLOW_WRONG_STATE;
+  queue->srcresult = GST_FLOW_FLUSHING;
+  queue->sinkresult = GST_FLOW_FLUSHING;
   queue->is_eos = FALSE;
   queue->in_timer = g_timer_new ();
   queue->out_timer = g_timer_new ();
 
-  queue->qlock = g_mutex_new ();
+  g_mutex_init (&queue->qlock);
   queue->waiting_add = FALSE;
-  queue->item_add = g_cond_new ();
+  g_cond_init (&queue->item_add);
   queue->waiting_del = FALSE;
-  queue->item_del = g_cond_new ();
+  g_cond_init (&queue->item_del);
   g_queue_init (&queue->queue);
 
+  g_cond_init (&queue->query_handled);
+  queue->last_query = FALSE;
+
+  g_mutex_init (&queue->buffering_post_lock);
   queue->buffering_percent = 100;
 
   /* tempfile related */
   queue->temp_template = NULL;
   queue->temp_location = NULL;
-  queue->temp_location_set = FALSE;
   queue->temp_remove = DEFAULT_TEMP_REMOVE;
 
   queue->ring_buffer = NULL;
@@ -486,15 +557,20 @@ gst_queue2_finalize (GObject * object)
   GST_DEBUG_OBJECT (queue, "finalizing queue");
 
   while (!g_queue_is_empty (&queue->queue)) {
-    GstMiniObject *data = g_queue_pop_head (&queue->queue);
+    GstQueue2Item *qitem = g_queue_pop_head (&queue->queue);
 
-    gst_mini_object_unref (data);
+    if (qitem->type != GST_QUEUE2_ITEM_TYPE_QUERY)
+      gst_mini_object_unref (qitem->item);
+    g_slice_free (GstQueue2Item, qitem);
   }
 
+  queue->last_query = FALSE;
   g_queue_clear (&queue->queue);
-  g_mutex_free (queue->qlock);
-  g_cond_free (queue->item_add);
-  g_cond_free (queue->item_del);
+  g_mutex_clear (&queue->qlock);
+  g_mutex_clear (&queue->buffering_post_lock);
+  g_cond_clear (&queue->item_add);
+  g_cond_clear (&queue->item_del);
+  g_cond_clear (&queue->query_handled);
   g_timer_destroy (queue->in_timer);
   g_timer_destroy (queue->out_timer);
 
@@ -572,7 +648,7 @@ update_cur_level (GstQueue2 * queue, GstQueue2Range * range)
 
 /* make a new range for @offset or reuse an existing range */
 static GstQueue2Range *
-add_range (GstQueue2 * queue, guint64 offset)
+add_range (GstQueue2 * queue, guint64 offset, gboolean update_existing)
 {
   GstQueue2Range *range, *prev, *next;
 
@@ -582,7 +658,11 @@ add_range (GstQueue2 * queue, guint64 offset)
     GST_DEBUG_OBJECT (queue,
         "reusing range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, range->offset,
         range->writing_pos);
-    range->writing_pos = offset;
+    if (update_existing && range->writing_pos != offset) {
+      GST_DEBUG_OBJECT (queue, "updating range writing position to "
+          "%" G_GUINT64_FORMAT, offset);
+      range->writing_pos = offset;
+    }
   } else {
     GST_DEBUG_OBJECT (queue,
         "new range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, offset, offset);
@@ -635,58 +715,7 @@ init_ranges (GstQueue2 * queue)
   /* get rid of all the current ranges */
   clean_ranges (queue);
   /* make a range for offset 0 */
-  queue->current = add_range (queue, 0);
-}
-
-static gboolean
-gst_queue2_acceptcaps (GstPad * pad, GstCaps * caps)
-{
-  GstQueue2 *queue;
-  GstPad *otherpad;
-  gboolean result;
-
-  queue = GST_QUEUE2 (GST_PAD_PARENT (pad));
-
-  otherpad = (pad == queue->srcpad ? queue->sinkpad : queue->srcpad);
-  result = gst_pad_peer_accept_caps (otherpad, caps);
-
-  return result;
-}
-
-static GstCaps *
-gst_queue2_getcaps (GstPad * pad)
-{
-  GstQueue2 *queue;
-  GstPad *otherpad;
-  GstCaps *result;
-
-  queue = GST_QUEUE2 (gst_pad_get_parent (pad));
-  if (G_UNLIKELY (queue == NULL))
-    return gst_caps_new_any ();
-
-  otherpad = (pad == queue->srcpad ? queue->sinkpad : queue->srcpad);
-  result = gst_pad_peer_get_caps (otherpad);
-  if (result == NULL)
-    result = gst_caps_new_any ();
-
-  gst_object_unref (queue);
-
-  return result;
-}
-
-static GstFlowReturn
-gst_queue2_bufferalloc (GstPad * pad, guint64 offset, guint size,
-    GstCaps * caps, GstBuffer ** buf)
-{
-  GstQueue2 *queue;
-  GstFlowReturn result;
-
-  queue = GST_QUEUE2 (GST_PAD_PARENT (pad));
-
-  /* Forward to src pad, without setting caps on the src pad */
-  result = gst_pad_alloc_buffer (queue->srcpad, offset, size, caps, buf);
-
-  return result;
+  queue->current = add_range (queue, 0, TRUE);
 }
 
 /* calculate the diff between running time on the sink and src of the queue.
@@ -697,14 +726,14 @@ update_time_level (GstQueue2 * queue)
   if (queue->sink_tainted) {
     queue->sinktime =
         gst_segment_to_running_time (&queue->sink_segment, GST_FORMAT_TIME,
-        queue->sink_segment.last_stop);
+        queue->sink_segment.position);
     queue->sink_tainted = FALSE;
   }
 
   if (queue->src_tainted) {
     queue->srctime =
         gst_segment_to_running_time (&queue->src_segment, GST_FORMAT_TIME,
-        queue->src_segment.last_stop);
+        queue->src_segment.position);
     queue->src_tainted = FALSE;
   }
 
@@ -719,49 +748,33 @@ update_time_level (GstQueue2 * queue)
     queue->cur_level.time = 0;
 }
 
-/* take a NEWSEGMENT event and apply the values to segment, updating the time
+/* take a SEGMENT event and apply the values to segment, updating the time
  * level of queue. */
 static void
 apply_segment (GstQueue2 * queue, GstEvent * event, GstSegment * segment,
     gboolean is_sink)
 {
-  gboolean update;
-  GstFormat format;
-  gdouble rate, arate;
-  gint64 start, stop, time;
-
-  gst_event_parse_new_segment_full (event, &update, &rate, &arate,
-      &format, &start, &stop, &time);
-
-  GST_DEBUG_OBJECT (queue,
-      "received NEWSEGMENT update %d, rate %lf, applied rate %lf, "
-      "format %d, "
-      "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
-      G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
+  gst_event_copy_segment (event, segment);
 
-  if (format == GST_FORMAT_BYTES) {
+  if (segment->format == GST_FORMAT_BYTES) {
     if (!QUEUE_IS_USING_QUEUE (queue) && is_sink) {
       /* start is where we'll be getting from and as such writing next */
-      queue->current = add_range (queue, start);
+      queue->current = add_range (queue, segment->start, TRUE);
     }
   }
 
   /* now configure the values, we use these to track timestamps on the
    * sinkpad. */
-  if (format != GST_FORMAT_TIME) {
-    /* non-time format, pretent the current time segment is closed with a
+  if (segment->format != GST_FORMAT_TIME) {
+    /* non-time format, pretend the current time segment is closed with a
      * 0 start and unknown stop time. */
-    update = FALSE;
-    format = GST_FORMAT_TIME;
-    start = 0;
-    stop = -1;
-    time = 0;
+    segment->format = GST_FORMAT_TIME;
+    segment->start = 0;
+    segment->stop = -1;
+    segment->time = 0;
   }
-  gst_segment_set_newsegment_full (segment, update,
-      rate, arate, format, start, stop, time);
 
-  GST_DEBUG_OBJECT (queue,
-      "configured NEWSEGMENT %" GST_SEGMENT_FORMAT, segment);
+  GST_DEBUG_OBJECT (queue, "configured SEGMENT %" GST_SEGMENT_FORMAT, segment);
 
   if (is_sink)
     queue->sink_tainted = TRUE;
@@ -772,29 +785,64 @@ apply_segment (GstQueue2 * queue, GstEvent * event, GstSegment * segment,
   update_time_level (queue);
 }
 
+static void
+apply_gap (GstQueue2 * queue, GstEvent * event,
+    GstSegment * segment, gboolean is_sink)
+{
+  GstClockTime timestamp;
+  GstClockTime duration;
+
+  gst_event_parse_gap (event, &timestamp, &duration);
+
+  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+
+    if (GST_CLOCK_TIME_IS_VALID (duration)) {
+      timestamp += duration;
+    }
+
+    segment->position = timestamp;
+
+    if (is_sink)
+      queue->sink_tainted = TRUE;
+    else
+      queue->src_tainted = TRUE;
+
+    /* calc diff with other end */
+    update_time_level (queue);
+  }
+}
+
 /* take a buffer and update segment, updating the time level of the queue. */
 static void
 apply_buffer (GstQueue2 * queue, GstBuffer * buffer, GstSegment * segment,
-    gboolean is_sink)
+    guint64 size, gboolean is_sink)
 {
   GstClockTime duration, timestamp;
 
-  timestamp = GST_BUFFER_TIMESTAMP (buffer);
+  timestamp = GST_BUFFER_DTS_OR_PTS (buffer);
   duration = GST_BUFFER_DURATION (buffer);
 
+  /* If we have no duration, pick one from the bitrate if we can */
+  if (duration == GST_CLOCK_TIME_NONE && queue->use_tags_bitrate) {
+    guint bitrate =
+        is_sink ? queue->sink_tags_bitrate : queue->src_tags_bitrate;
+    if (bitrate)
+      duration = gst_util_uint64_scale (size, 8 * GST_SECOND, bitrate);
+  }
+
   /* if no timestamp is set, assume it's continuous with the previous
    * time */
   if (timestamp == GST_CLOCK_TIME_NONE)
-    timestamp = segment->last_stop;
+    timestamp = segment->position;
 
   /* add duration */
   if (duration != GST_CLOCK_TIME_NONE)
     timestamp += duration;
 
-  GST_DEBUG_OBJECT (queue, "last_stop updated to %" GST_TIME_FORMAT,
+  GST_DEBUG_OBJECT (queue, "position updated to %" GST_TIME_FORMAT,
       GST_TIME_ARGS (timestamp));
 
-  gst_segment_set_last_stop (segment, GST_FORMAT_TIME, timestamp);
+  segment->position = timestamp;
 
   if (is_sink)
     queue->sink_tainted = TRUE;
@@ -805,24 +853,41 @@ apply_buffer (GstQueue2 * queue, GstBuffer * buffer, GstSegment * segment,
   update_time_level (queue);
 }
 
-static GstBufferListItem
-buffer_list_apply_time (GstBuffer ** buf, guint group, guint idx, gpointer data)
+struct BufListData
+{
+  GstClockTime timestamp;
+  guint bitrate;
+};
+
+static gboolean
+buffer_list_apply_time (GstBuffer ** buf, guint idx, gpointer data)
 {
-  GstClockTime *timestamp = data;
+  struct BufListData *bld = data;
+  GstClockTime *timestamp = &bld->timestamp;
+  GstClockTime btime;
 
-  GST_TRACE ("buffer %u in group %u has ts %" GST_TIME_FORMAT
-      " duration %" GST_TIME_FORMAT, idx, group,
-      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*buf)),
+  GST_TRACE ("buffer %u has pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT
+      " duration %" GST_TIME_FORMAT, idx,
+      GST_TIME_ARGS (GST_BUFFER_PTS (*buf)),
+      GST_TIME_ARGS (GST_BUFFER_DTS (*buf)),
       GST_TIME_ARGS (GST_BUFFER_DURATION (*buf)));
 
-  if (GST_BUFFER_TIMESTAMP_IS_VALID (*buf))
-    *timestamp = GST_BUFFER_TIMESTAMP (*buf);
+  btime = GST_BUFFER_DTS_OR_PTS (*buf);
+  if (GST_CLOCK_TIME_IS_VALID (btime))
+    *timestamp = btime;
 
   if (GST_BUFFER_DURATION_IS_VALID (*buf))
     *timestamp += GST_BUFFER_DURATION (*buf);
+  else if (bld->bitrate != 0) {
+    guint64 size = gst_buffer_get_size (*buf);
+
+    /* If we have no duration, pick one from the bitrate if we can */
+    *timestamp += gst_util_uint64_scale (bld->bitrate, 8 * GST_SECOND, size);
+  }
+
 
   GST_TRACE ("ts now %" GST_TIME_FORMAT, GST_TIME_ARGS (*timestamp));
-  return GST_BUFFER_LIST_CONTINUE;
+  return TRUE;
 }
 
 /* take a buffer list and update segment, updating the time level of the queue */
@@ -830,17 +895,25 @@ static void
 apply_buffer_list (GstQueue2 * queue, GstBufferList * buffer_list,
     GstSegment * segment, gboolean is_sink)
 {
-  GstClockTime timestamp;
+  struct BufListData bld;
 
   /* if no timestamp is set, assume it's continuous with the previous time */
-  timestamp = segment->last_stop;
+  bld.timestamp = segment->position;
+
+  if (queue->use_tags_bitrate) {
+    if (is_sink)
+      bld.bitrate = queue->sink_tags_bitrate;
+    else
+      bld.bitrate = queue->src_tags_bitrate;
+  } else
+    bld.bitrate = 0;
 
-  gst_buffer_list_foreach (buffer_list, buffer_list_apply_time, &timestamp);
+  gst_buffer_list_foreach (buffer_list, buffer_list_apply_time, &bld);
 
   GST_DEBUG_OBJECT (queue, "last_stop updated to %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (timestamp));
+      GST_TIME_ARGS (bld.timestamp));
 
-  gst_segment_set_last_stop (segment, GST_FORMAT_TIME, timestamp);
+  segment->position = bld.timestamp;
 
   if (is_sink)
     queue->sink_tainted = TRUE;
@@ -851,101 +924,205 @@ apply_buffer_list (GstQueue2 * queue, GstBufferList * buffer_list,
   update_time_level (queue);
 }
 
-static void
-update_buffering (GstQueue2 * queue)
+static inline gint
+normalize_to_buffering_level (guint64 cur_level, guint64 max_level,
+    guint64 alt_max)
 {
-  gint64 percent;
-  gboolean post = FALSE;
+  guint64 p;
 
-  if (queue->high_percent <= 0)
-    return;
+  if (max_level == 0)
+    return 0;
+
+  if (alt_max > 0)
+    p = gst_util_uint64_scale (cur_level, MAX_BUFFERING_LEVEL,
+        MIN (max_level, alt_max));
+  else
+    p = gst_util_uint64_scale (cur_level, MAX_BUFFERING_LEVEL, max_level);
+
+  return MIN (p, MAX_BUFFERING_LEVEL);
+}
+
+static gboolean
+get_buffering_level (GstQueue2 * queue, gboolean * is_buffering,
+    gint * buffering_level)
+{
+  gint buflevel, buflevel2;
 
-#define GET_PERCENT(format,alt_max) ((queue->max_level.format) > 0 ? (queue->cur_level.format) * 100 / ((alt_max) > 0 ? MIN ((alt_max), (queue->max_level.format)) : (queue->max_level.format)) : 0)
+  if (queue->high_watermark <= 0) {
+    if (buffering_level)
+      *buffering_level = MAX_BUFFERING_LEVEL;
+    if (is_buffering)
+      *is_buffering = FALSE;
+    return FALSE;
+  }
+#define GET_BUFFER_LEVEL_FOR_QUANTITY(format,alt_max) \
+    normalize_to_buffering_level (queue->cur_level.format,queue->max_level.format,(alt_max))
 
   if (queue->is_eos) {
     /* on EOS we are always 100% full, we set the var here so that it we can
      * reuse the logic below to stop buffering */
-    percent = 100;
+    buflevel = MAX_BUFFERING_LEVEL;
     GST_LOG_OBJECT (queue, "we are EOS");
   } else {
-    /* figure out the percent we are filled, we take the max of all formats. */
+    GST_LOG_OBJECT (queue,
+        "Cur level bytes/time/buffers %u/%" GST_TIME_FORMAT "/%u",
+        queue->cur_level.bytes, GST_TIME_ARGS (queue->cur_level.time),
+        queue->cur_level.buffers);
 
+    /* figure out the buffering level we are filled, we take the max of all formats. */
     if (!QUEUE_IS_USING_RING_BUFFER (queue)) {
-      percent = GET_PERCENT (bytes, 0);
+      buflevel = GET_BUFFER_LEVEL_FOR_QUANTITY (bytes, 0);
     } else {
       guint64 rb_size = queue->ring_buffer_max_size;
-      percent = GET_PERCENT (bytes, rb_size);
+      buflevel = GET_BUFFER_LEVEL_FOR_QUANTITY (bytes, rb_size);
     }
-    percent = MAX (percent, GET_PERCENT (time, 0));
-    percent = MAX (percent, GET_PERCENT (buffers, 0));
+
+    buflevel2 = GET_BUFFER_LEVEL_FOR_QUANTITY (time, 0);
+    buflevel = MAX (buflevel, buflevel2);
+
+    buflevel2 = GET_BUFFER_LEVEL_FOR_QUANTITY (buffers, 0);
+    buflevel = MAX (buflevel, buflevel2);
 
     /* also apply the rate estimate when we need to */
-    if (queue->use_rate_estimate)
-      percent = MAX (percent, GET_PERCENT (rate_time, 0));
+    if (queue->use_rate_estimate) {
+      buflevel2 = GET_BUFFER_LEVEL_FOR_QUANTITY (rate_time, 0);
+      buflevel = MAX (buflevel, buflevel2);
+    }
+
+    /* Don't get to 0% unless we're really empty */
+    if (queue->cur_level.bytes > 0)
+      buflevel = MAX (1, buflevel);
   }
+#undef GET_BUFFER_LEVEL_FOR_QUANTITY
 
-  if (queue->is_buffering) {
-    post = TRUE;
-    /* if we were buffering see if we reached the high watermark */
-    if (percent >= queue->high_percent)
-      queue->is_buffering = FALSE;
-  } else {
-    /* we were not buffering, check if we need to start buffering if we drop
-     * below the low threshold */
-    if (percent < queue->low_percent) {
-      queue->is_buffering = TRUE;
-      queue->buffering_iteration++;
-      post = TRUE;
+  if (is_buffering)
+    *is_buffering = queue->is_buffering;
+
+  if (buffering_level)
+    *buffering_level = buflevel;
+
+  GST_DEBUG_OBJECT (queue, "buffering %d, level %d", queue->is_buffering,
+      buflevel);
+
+  return TRUE;
+}
+
+static gint
+convert_to_buffering_percent (GstQueue2 * queue, gint buffering_level)
+{
+  int percent;
+
+  /* scale so that if buffering_level equals the high watermark,
+   * the percentage is 100% */
+  percent = buffering_level * 100 / queue->high_watermark;
+  /* clip */
+  if (percent > 100)
+    percent = 100;
+
+  return percent;
+}
+
+static void
+get_buffering_stats (GstQueue2 * queue, gint percent, GstBufferingMode * mode,
+    gint * avg_in, gint * avg_out, gint64 * buffering_left)
+{
+  if (mode) {
+    if (!QUEUE_IS_USING_QUEUE (queue)) {
+      if (QUEUE_IS_USING_RING_BUFFER (queue))
+        *mode = GST_BUFFERING_TIMESHIFT;
+      else
+        *mode = GST_BUFFERING_DOWNLOAD;
+    } else {
+      *mode = GST_BUFFERING_STREAM;
     }
   }
-  if (post) {
-    GstMessage *message;
-    GstBufferingMode mode;
-    gint64 buffering_left = -1;
 
-    /* scale to high percent so that it becomes the 100% mark */
-    percent = percent * 100 / queue->high_percent;
-    /* clip */
-    if (percent > 100)
-      percent = 100;
+  if (avg_in)
+    *avg_in = queue->byte_in_rate;
+  if (avg_out)
+    *avg_out = queue->byte_out_rate;
 
-    if (percent != queue->buffering_percent) {
-      queue->buffering_percent = percent;
+  if (buffering_left) {
+    *buffering_left = (percent == 100 ? 0 : -1);
 
-      if (!QUEUE_IS_USING_QUEUE (queue)) {
-        GstFormat fmt = GST_FORMAT_BYTES;
-        gint64 duration;
+    if (queue->use_rate_estimate) {
+      guint64 max, cur;
 
-        if (QUEUE_IS_USING_RING_BUFFER (queue))
-          mode = GST_BUFFERING_TIMESHIFT;
-        else
-          mode = GST_BUFFERING_DOWNLOAD;
+      max = queue->max_level.rate_time;
+      cur = queue->cur_level.rate_time;
 
-        if (queue->byte_in_rate > 0) {
-          if (gst_pad_query_peer_duration (queue->sinkpad, &fmt, &duration))
-            buffering_left =
-                (gdouble) ((duration -
-                    queue->current->writing_pos) * 1000) / queue->byte_in_rate;
-        } else {
-          buffering_left = G_MAXINT64;
-        }
-      } else {
-        mode = GST_BUFFERING_STREAM;
-      }
+      if (percent != 100 && max > cur)
+        *buffering_left = (max - cur) / 1000000;
+    }
+  }
+}
 
-      GST_DEBUG_OBJECT (queue, "buffering %d percent", (gint) percent);
-      message = gst_message_new_buffering (GST_OBJECT_CAST (queue),
-          (gint) percent);
-      gst_message_set_buffering_stats (message, mode,
-          queue->byte_in_rate, queue->byte_out_rate, buffering_left);
+/* Called with the lock taken */
+static GstMessage *
+gst_queue2_get_buffering_message (GstQueue2 * queue)
+{
+  GstMessage *msg = NULL;
 
-      gst_element_post_message (GST_ELEMENT_CAST (queue), message);
-    }
-  } else {
-    GST_DEBUG_OBJECT (queue, "filled %d percent", (gint) percent);
+  if (queue->percent_changed) {
+    gint percent = queue->buffering_percent;
+
+    queue->percent_changed = FALSE;
+
+    GST_DEBUG_OBJECT (queue, "Going to post buffering: %d%%", percent);
+    msg = gst_message_new_buffering (GST_OBJECT_CAST (queue), percent);
+
+    gst_message_set_buffering_stats (msg, queue->mode, queue->avg_in,
+        queue->avg_out, queue->buffering_left);
   }
 
-#undef GET_PERCENT
+  return msg;
+}
+
+static void
+gst_queue2_post_buffering (GstQueue2 * queue)
+{
+  GstMessage *msg = NULL;
+
+  g_mutex_lock (&queue->buffering_post_lock);
+  GST_QUEUE2_MUTEX_LOCK (queue);
+  msg = gst_queue2_get_buffering_message (queue);
+  GST_QUEUE2_MUTEX_UNLOCK (queue);
+
+  if (msg != NULL)
+    gst_element_post_message (GST_ELEMENT_CAST (queue), msg);
+
+  g_mutex_unlock (&queue->buffering_post_lock);
+}
+
+static void
+update_buffering (GstQueue2 * queue)
+{
+  gint buffering_level, percent;
+
+  /* Ensure the variables used to calculate buffering state are up-to-date. */
+  if (queue->current)
+    update_cur_level (queue, queue->current);
+  update_in_rates (queue, FALSE);
+
+  if (!get_buffering_level (queue, NULL, &buffering_level))
+    return;
+
+  percent = convert_to_buffering_percent (queue, buffering_level);
+
+  if (queue->is_buffering) {
+    /* if we were buffering see if we reached the high watermark */
+    if (percent >= 100)
+      queue->is_buffering = FALSE;
+
+    SET_PERCENT (queue, percent);
+  } else {
+    /* we were not buffering, check if we need to start buffering if we drop
+     * below the low threshold */
+    if (buffering_level < queue->low_watermark) {
+      queue->is_buffering = TRUE;
+      SET_PERCENT (queue, percent);
+    }
+  }
 }
 
 static void
@@ -956,6 +1133,7 @@ reset_rate_timer (GstQueue2 * queue)
   queue->byte_in_rate = 0.0;
   queue->byte_in_period = 0;
   queue->byte_out_rate = 0.0;
+  queue->last_update_in_rates_elapsed = 0.0;
   queue->last_in_elapsed = 0.0;
   queue->last_out_elapsed = 0.0;
   queue->in_timer_started = FALSE;
@@ -975,7 +1153,7 @@ reset_rate_timer (GstQueue2 * queue)
 #define AVG_OUT(avg,val) ((avg) * 3.0 + (val)) / 4.0
 
 static void
-update_in_rates (GstQueue2 * queue)
+update_in_rates (GstQueue2 * queue, gboolean force)
 {
   gdouble elapsed, period;
   gdouble byte_in_rate;
@@ -986,10 +1164,11 @@ update_in_rates (GstQueue2 * queue)
     return;
   }
 
-  elapsed = g_timer_elapsed (queue->in_timer, NULL);
+  queue->last_update_in_rates_elapsed = elapsed =
+      g_timer_elapsed (queue->in_timer, NULL);
 
   /* recalc after each interval. */
-  if (queue->last_in_elapsed + RATE_INTERVAL < elapsed) {
+  if (force || queue->last_in_elapsed + RATE_INTERVAL < elapsed) {
     period = elapsed - queue->last_in_elapsed;
 
     GST_DEBUG_OBJECT (queue,
@@ -1089,6 +1268,8 @@ perform_seek_to_offset (GstQueue2 * queue, guint64 offset)
   queue->seeking = TRUE;
   GST_QUEUE2_MUTEX_UNLOCK (queue);
 
+  debug_ranges (queue);
+
   GST_DEBUG_OBJECT (queue, "Seeking to %" G_GUINT64_FORMAT, offset);
 
   event =
@@ -1099,12 +1280,36 @@ perform_seek_to_offset (GstQueue2 * queue, guint64 offset)
   res = gst_pad_push_event (queue->sinkpad, event);
   GST_QUEUE2_MUTEX_LOCK (queue);
 
-  if (res)
-    queue->current = add_range (queue, offset);
+  if (res) {
+    /* Between us sending the seek event and re-acquiring the lock, the source
+     * thread might already have pushed data and moved along the range's
+     * writing_pos beyond the seek offset. In that case we don't want to set
+     * the writing position back to the requested seek position, as it would
+     * cause data to be written to the wrong offset in the file or ring buffer.
+     * We still do the add_range call to switch the current range to the
+     * requested range, or create one if one doesn't exist yet. */
+    queue->current = add_range (queue, offset, FALSE);
+  }
 
   return res;
 }
 
+/* get the threshold for when we decide to seek rather than wait */
+static guint64
+get_seek_threshold (GstQueue2 * queue)
+{
+  guint64 threshold;
+
+  /* FIXME, find a good threshold based on the incoming rate. */
+  threshold = 1024 * 512;
+
+  if (QUEUE_IS_USING_RING_BUFFER (queue)) {
+    threshold = MIN (threshold,
+        QUEUE_MAX_BYTES (queue) - queue->cur_level.bytes);
+  }
+  return threshold;
+}
+
 /* see if there is enough data in the file to read a full buffer */
 static gboolean
 gst_queue2_have_data (GstQueue2 * queue, guint64 offset, guint length)
@@ -1143,25 +1348,12 @@ gst_queue2_have_data (GstQueue2 * queue, guint64 offset, guint length)
         " len %u", offset, length);
     /* we don't have the range, see how far away we are */
     if (!queue->is_eos && queue->current) {
-      /* FIXME, find a good threshold based on the incoming rate. */
-      guint64 threshold = 1024 * 512;
-
-      if (QUEUE_IS_USING_RING_BUFFER (queue)) {
-        guint64 distance;
-
-        distance = QUEUE_MAX_BYTES (queue) - queue->cur_level.bytes;
-        /* don't wait for the complete buffer to fill */
-        distance = MIN (distance, threshold);
+      guint64 threshold = get_seek_threshold (queue);
 
-        if (offset >= queue->current->offset && offset <=
-            queue->current->writing_pos + distance) {
-          GST_INFO_OBJECT (queue,
-              "requested data is within range, wait for data");
-          return FALSE;
-        }
-      } else if (offset < queue->current->writing_pos + threshold) {
-        update_cur_pos (queue, queue->current, offset + length);
-        GST_INFO_OBJECT (queue, "wait for data");
+      if (offset >= queue->current->offset && offset <=
+          queue->current->writing_pos + threshold) {
+        GST_INFO_OBJECT (queue,
+            "requested data is within range, wait for data");
         return FALSE;
       }
     }
@@ -1181,9 +1373,9 @@ gst_queue2_have_data (GstQueue2 * queue, guint64 offset, guint length)
 #define FSEEK_FILE(file,offset)  (fseek (file, offset, SEEK_SET) != 0)
 #endif
 
-static gint64
+static GstFlowReturn
 gst_queue2_read_data_at_offset (GstQueue2 * queue, guint64 offset, guint length,
-    guint8 * dst)
+    guint8 * dst, gint64 * read_return)
 {
   guint8 *ring_buffer;
   size_t res;
@@ -1215,7 +1407,9 @@ gst_queue2_read_data_at_offset (GstQueue2 * queue, guint64 offset, guint length,
       goto eos;
   }
 
-  return res;
+  *read_return = res;
+
+  return GST_FLOW_OK;
 
 seek_failed:
   {
@@ -1230,7 +1424,7 @@ could_not_read:
 eos:
   {
     GST_DEBUG ("non-regular file hits EOS");
-    return GST_FLOW_UNEXPECTED;
+    return GST_FLOW_EOS;
   }
 }
 
@@ -1239,17 +1433,24 @@ gst_queue2_create_read (GstQueue2 * queue, guint64 offset, guint length,
     GstBuffer ** buffer)
 {
   GstBuffer *buf;
+  GstMapInfo info;
   guint8 *data;
   guint64 file_offset;
   guint block_length, remaining, read_length;
-  gint64 read_return;
   guint64 rb_size;
   guint64 max_size;
   guint64 rpos;
+  GstFlowReturn ret = GST_FLOW_OK;
 
   /* allocate the output buffer of the requested size */
-  buf = gst_buffer_new_and_alloc (length);
-  data = GST_BUFFER_DATA (buf);
+  if (*buffer == NULL)
+    buf = gst_buffer_new_allocate (NULL, length, NULL);
+  else
+    buf = *buffer;
+
+  if (!gst_buffer_map (buf, &info, GST_MAP_WRITE))
+    goto buffer_write_fail;
+  data = info.data;
 
   GST_DEBUG_OBJECT (queue, "Reading %u bytes from %" G_GUINT64_FORMAT, length,
       offset);
@@ -1294,12 +1495,8 @@ gst_queue2_create_read (GstQueue2 * queue, guint64 offset, guint length,
             read_length = level;
             remaining = level;
             length = level;
-          } else {
-            GST_DEBUG_OBJECT (queue,
-                "EOS hit and we don't have any requested data");
-            gst_buffer_unref (buf);
-            return GST_FLOW_UNEXPECTED;
-          }
+          } else
+            goto hit_eos;
         }
       }
 
@@ -1309,7 +1506,12 @@ gst_queue2_create_read (GstQueue2 * queue, guint64 offset, guint length,
               "update current position [%" G_GUINT64_FORMAT "-%"
               G_GUINT64_FORMAT "]", rpos, queue->current->max_reading_pos);
           update_cur_pos (queue, queue->current, rpos);
+          GST_QUEUE2_SIGNAL_DEL (queue);
         }
+
+        if (queue->use_buffering)
+          update_buffering (queue);
+
         GST_DEBUG_OBJECT (queue, "waiting for add");
         GST_QUEUE2_WAIT_ADD_CHECK (queue, queue->srcresult, out_flushing);
         continue;
@@ -1339,10 +1541,12 @@ gst_queue2_create_read (GstQueue2 * queue, guint64 offset, guint length,
 
     /* while we still have data to read, we loop */
     while (read_length > 0) {
-      read_return =
+      gint64 read_return;
+
+      ret =
           gst_queue2_read_data_at_offset (queue, file_offset, block_length,
-          data);
-      if (read_return < 0)
+          data, &read_return);
+      if (ret != GST_FLOW_OK)
         goto read_error;
 
       file_offset += read_return;
@@ -1361,26 +1565,48 @@ gst_queue2_create_read (GstQueue2 * queue, guint64 offset, guint length,
     GST_DEBUG_OBJECT (queue, "%u bytes left to read", remaining);
   }
 
-  GST_BUFFER_SIZE (buf) = length;
+  gst_buffer_unmap (buf, &info);
+  gst_buffer_resize (buf, 0, length);
+
   GST_BUFFER_OFFSET (buf) = offset;
   GST_BUFFER_OFFSET_END (buf) = offset + length;
 
   *buffer = buf;
 
-  return GST_FLOW_OK;
+  return ret;
 
   /* ERRORS */
+hit_eos:
+  {
+    GST_DEBUG_OBJECT (queue, "EOS hit and we don't have any requested data");
+    gst_buffer_unmap (buf, &info);
+    if (*buffer == NULL)
+      gst_buffer_unref (buf);
+    return GST_FLOW_EOS;
+  }
 out_flushing:
   {
     GST_DEBUG_OBJECT (queue, "we are flushing");
-    gst_buffer_unref (buf);
-    return GST_FLOW_WRONG_STATE;
+    gst_buffer_unmap (buf, &info);
+    if (*buffer == NULL)
+      gst_buffer_unref (buf);
+    return GST_FLOW_FLUSHING;
   }
 read_error:
   {
     GST_DEBUG_OBJECT (queue, "we have a read error");
-    gst_buffer_unref (buf);
-    return read_return;
+    gst_buffer_unmap (buf, &info);
+    if (*buffer == NULL)
+      gst_buffer_unref (buf);
+    return ret;
+  }
+buffer_write_fail:
+  {
+    GST_ELEMENT_ERROR (queue, RESOURCE, WRITE, (NULL),
+        ("Can't write to buffer"));
+    if (*buffer == NULL)
+      gst_buffer_unref (buf);
+    return GST_FLOW_ERROR;
   }
 }
 
@@ -1390,12 +1616,15 @@ gst_queue2_read_item_from_file (GstQueue2 * queue)
 {
   GstMiniObject *item;
 
-  if (queue->starting_segment != NULL) {
+  if (queue->stream_start_event != NULL) {
+    item = GST_MINI_OBJECT_CAST (queue->stream_start_event);
+    queue->stream_start_event = NULL;
+  } else if (queue->starting_segment != NULL) {
     item = GST_MINI_OBJECT_CAST (queue->starting_segment);
     queue->starting_segment = NULL;
   } else {
     GstFlowReturn ret;
-    GstBuffer *buffer;
+    GstBuffer *buffer = NULL;
     guint64 reading_pos;
 
     reading_pos = queue->current->reading_pos;
@@ -1408,7 +1637,7 @@ gst_queue2_read_item_from_file (GstQueue2 * queue)
       case GST_FLOW_OK:
         item = GST_MINI_OBJECT_CAST (buffer);
         break;
-      case GST_FLOW_UNEXPECTED:
+      case GST_FLOW_EOS:
         item = GST_MINI_OBJECT_CAST (gst_event_new_eos ());
         break;
       default:
@@ -1432,45 +1661,40 @@ gst_queue2_open_temp_location_file (GstQueue2 * queue)
 
   GST_DEBUG_OBJECT (queue, "opening temp file %s", queue->temp_template);
 
-  /* we have two cases:
-   * - temp_location was set to something !NULL (Deprecated). in this case we
-   *   open the specified filename.
-   * - temp_template was set, allocate a filename and open that filename
-   */
-  if (!queue->temp_location_set) {
-    /* nothing to do */
-    if (queue->temp_template == NULL)
-      goto no_directory;
-
-    /* make copy of the template, we don't want to change this */
-    name = g_strdup (queue->temp_template);
-    fd = g_mkstemp (name);
-    if (fd == -1)
-      goto mkstemp_failed;
-
-    /* open the file for update/writing */
-    queue->temp_file = fdopen (fd, "wb+");
-    /* error creating file */
-    if (queue->temp_file == NULL)
-      goto open_failed;
-
-    g_free (queue->temp_location);
-    queue->temp_location = name;
+  /* If temp_template was set, allocate a filename and open that file */
 
-    GST_QUEUE2_MUTEX_UNLOCK (queue);
+  /* nothing to do */
+  if (queue->temp_template == NULL)
+    goto no_directory;
 
-    /* we can't emit the notify with the lock */
-    g_object_notify (G_OBJECT (queue), "temp-location");
+  /* make copy of the template, we don't want to change this */
+  name = g_strdup (queue->temp_template);
+
+#ifdef __BIONIC__
+  fd = g_mkstemp_full (name, O_RDWR | O_LARGEFILE, S_IRUSR | S_IWUSR);
+#else
+  fd = g_mkstemp (name);
+#endif
+
+  if (fd == -1)
+    goto mkstemp_failed;
+
+  /* open the file for update/writing */
+  queue->temp_file = fdopen (fd, "wb+");
+  /* error creating file */
+  if (queue->temp_file == NULL)
+    goto open_failed;
+
+  g_free (queue->temp_location);
+  queue->temp_location = name;
+
+  GST_QUEUE2_MUTEX_UNLOCK (queue);
+
+  /* we can't emit the notify with the lock */
+  g_object_notify (G_OBJECT (queue), "temp-location");
+
+  GST_QUEUE2_MUTEX_LOCK (queue);
 
-    GST_QUEUE2_MUTEX_LOCK (queue);
-  } else {
-    /* open the file for update/writing, this is deprecated but we still need to
-     * support it for API/ABI compatibility */
-    queue->temp_file = g_fopen (queue->temp_location, "wb+");
-    /* error creating file */
-    if (queue->temp_file == NULL)
-      goto open_failed;
-  }
   GST_DEBUG_OBJECT (queue, "opened temp file %s", queue->temp_template);
 
   return TRUE;
@@ -1518,8 +1742,12 @@ gst_queue2_close_temp_location_file (GstQueue2 * queue)
   fflush (queue->temp_file);
   fclose (queue->temp_file);
 
-  if (queue->temp_remove)
-    remove (queue->temp_location);
+  if (queue->temp_remove) {
+    if (remove (queue->temp_location) < 0) {
+      GST_WARNING_OBJECT (queue, "Failed to remove temporary file %s: %s",
+          queue->temp_location, g_strerror (errno));
+    }
+  }
 
   queue->temp_file = NULL;
   clean_ranges (queue);
@@ -1537,21 +1765,33 @@ gst_queue2_flush_temp_file (GstQueue2 * queue)
 }
 
 static void
-gst_queue2_locked_flush (GstQueue2 * queue)
+gst_queue2_locked_flush (GstQueue2 * queue, gboolean full, gboolean clear_temp)
 {
   if (!QUEUE_IS_USING_QUEUE (queue)) {
-    if (QUEUE_IS_USING_TEMP_FILE (queue))
+    if (QUEUE_IS_USING_TEMP_FILE (queue) && clear_temp)
       gst_queue2_flush_temp_file (queue);
     init_ranges (queue);
   } else {
     while (!g_queue_is_empty (&queue->queue)) {
-      GstMiniObject *data = g_queue_pop_head (&queue->queue);
+      GstQueue2Item *qitem = g_queue_pop_head (&queue->queue);
+
+      if (!full && qitem->type == GST_QUEUE2_ITEM_TYPE_EVENT
+          && GST_EVENT_IS_STICKY (qitem->item)
+          && GST_EVENT_TYPE (qitem->item) != GST_EVENT_SEGMENT
+          && GST_EVENT_TYPE (qitem->item) != GST_EVENT_EOS) {
+        gst_pad_store_sticky_event (queue->srcpad,
+            GST_EVENT_CAST (qitem->item));
+      }
 
       /* Then lose another reference because we are supposed to destroy that
          data when flushing */
-      gst_mini_object_unref (data);
+      if (qitem->type != GST_QUEUE2_ITEM_TYPE_QUERY)
+        gst_mini_object_unref (qitem->item);
+      g_slice_free (GstQueue2Item, qitem);
     }
   }
+  queue->last_query = FALSE;
+  g_cond_signal (&queue->query_handled);
   GST_QUEUE2_CLEAR_LEVEL (queue->cur_level);
   gst_segment_init (&queue->sink_segment, GST_FORMAT_TIME);
   gst_segment_init (&queue->src_segment, GST_FORMAT_TIME);
@@ -1561,6 +1801,7 @@ gst_queue2_locked_flush (GstQueue2 * queue)
     gst_event_unref (queue->starting_segment);
   queue->starting_segment = NULL;
   queue->segment_event_received = FALSE;
+  gst_event_replace (&queue->stream_start_event, NULL);
 
   /* we deleted a lot of something */
   GST_QUEUE2_SIGNAL_DEL (queue);
@@ -1604,10 +1845,12 @@ out_flushing:
 static gboolean
 gst_queue2_create_write (GstQueue2 * queue, GstBuffer * buffer)
 {
+  GstMapInfo info;
   guint8 *data, *ring_buffer;
   guint size, rb_size;
   guint64 writing_pos, new_writing_pos;
   GstQueue2Range *range, *prev, *next;
+  gboolean do_seek = FALSE;
 
   if (QUEUE_IS_USING_RING_BUFFER (queue))
     writing_pos = queue->current->rb_writing_pos;
@@ -1616,12 +1859,23 @@ gst_queue2_create_write (GstQueue2 * queue, GstBuffer * buffer)
   ring_buffer = queue->ring_buffer;
   rb_size = queue->ring_buffer_max_size;
 
-  size = GST_BUFFER_SIZE (buffer);
-  data = GST_BUFFER_DATA (buffer);
+  if (!gst_buffer_map (buffer, &info, GST_MAP_READ))
+    goto buffer_read_error;
+
+  size = info.size;
+  data = info.data;
 
   GST_DEBUG_OBJECT (queue, "Writing %u bytes to %" G_GUINT64_FORMAT, size,
       writing_pos);
 
+  /* sanity check */
+  if (GST_BUFFER_OFFSET_IS_VALID (buffer) &&
+      GST_BUFFER_OFFSET (buffer) != queue->current->writing_pos) {
+    GST_WARNING_OBJECT (queue, "buffer offset does not match current writing "
+        "position! %" G_GINT64_FORMAT " != %" G_GINT64_FORMAT,
+        GST_BUFFER_OFFSET (buffer), queue->current->writing_pos);
+  }
+
   while (size > 0) {
     guint to_write;
 
@@ -1658,6 +1912,9 @@ gst_queue2_create_write (GstQueue2 * queue, GstBuffer * buffer)
         guint64 range_data_start, range_data_end;
         GstQueue2Range *range_to_destroy = NULL;
 
+        if (range == queue->current)
+          goto next_range;
+
         range_data_start = range->rb_offset;
         range_data_end = range->rb_writing_pos;
 
@@ -1771,15 +2028,19 @@ gst_queue2_create_write (GstQueue2 * queue, GstBuffer * buffer)
           GST_DEBUG_OBJECT (queue, "merging ranges %" G_GUINT64_FORMAT,
               next->writing_pos);
 
-          /* remove the group, we could choose to not read the data in this range
-           * again. This would involve us doing a seek to the current writing position
-           * in the range. FIXME, It would probably make sense to do a seek when there
-           * is a lot of data in the range we merged with to avoid reading it all
-           * again. */
+          /* remove the group */
           queue->current->next = next->next;
-          g_slice_free (GstQueue2Range, next);
 
-          debug_ranges (queue);
+          /* We use the threshold to decide if we want to do a seek or simply
+           * read the data again. If there is not so much data in the range we
+           * prefer to avoid to seek and read it again. */
+          if (next->writing_pos > new_writing_pos + get_seek_threshold (queue)) {
+            /* the new range had more data than the threshold, it's worth keeping
+             * it and doing a seek. */
+            new_writing_pos = next->writing_pos;
+            do_seek = TRUE;
+          }
+          g_slice_free (GstQueue2Range, next);
         }
         goto update_and_signal;
       }
@@ -1829,17 +2090,32 @@ gst_queue2_create_write (GstQueue2 * queue, GstBuffer * buffer)
     } else {
       queue->current->writing_pos = writing_pos = new_writing_pos;
     }
+    if (do_seek)
+      perform_seek_to_offset (queue, new_writing_pos);
+
     update_cur_level (queue, queue->current);
 
     /* update the buffering status */
-    if (queue->use_buffering)
+    if (queue->use_buffering) {
+      GstMessage *msg;
       update_buffering (queue);
+      msg = gst_queue2_get_buffering_message (queue);
+      if (msg) {
+        GST_QUEUE2_MUTEX_UNLOCK (queue);
+        g_mutex_lock (&queue->buffering_post_lock);
+        gst_element_post_message (GST_ELEMENT_CAST (queue), msg);
+        g_mutex_unlock (&queue->buffering_post_lock);
+        GST_QUEUE2_MUTEX_LOCK (queue);
+      }
+    }
 
     GST_INFO_OBJECT (queue, "cur_level.bytes %u (max %" G_GUINT64_FORMAT ")",
         queue->cur_level.bytes, QUEUE_MAX_BYTES (queue));
 
     GST_QUEUE2_SIGNAL_ADD (queue);
-  };
+  }
+
+  gst_buffer_unmap (buffer, &info);
 
   return TRUE;
 
@@ -1847,12 +2123,14 @@ gst_queue2_create_write (GstQueue2 * queue, GstBuffer * buffer)
 out_flushing:
   {
     GST_DEBUG_OBJECT (queue, "we are flushing");
-    /* FIXME - GST_FLOW_UNEXPECTED ? */
+    gst_buffer_unmap (buffer, &info);
+    /* FIXME - GST_FLOW_EOS ? */
     return FALSE;
   }
 seek_failed:
   {
     GST_ELEMENT_ERROR (queue, RESOURCE, SEEK, (NULL), GST_ERROR_SYSTEM);
+    gst_buffer_unmap (buffer, &info);
     return FALSE;
   }
 handle_error:
@@ -1868,37 +2146,43 @@ handle_error:
             ("%s", g_strerror (errno)));
       }
     }
+    gst_buffer_unmap (buffer, &info);
+    return FALSE;
+  }
+buffer_read_error:
+  {
+    GST_ELEMENT_ERROR (queue, RESOURCE, READ, (NULL),
+        ("Can't read from buffer"));
     return FALSE;
   }
 }
 
-static GstBufferListItem
-buffer_list_create_write (GstBuffer ** buf, guint group, guint idx, gpointer q)
+static gboolean
+buffer_list_create_write (GstBuffer ** buf, guint idx, gpointer q)
 {
   GstQueue2 *queue = q;
 
-  GST_TRACE_OBJECT (queue, "writing buffer %u in group %u of size %u bytes",
-      idx, group, GST_BUFFER_SIZE (*buf));
+  GST_TRACE_OBJECT (queue,
+      "writing buffer %u of size %" G_GSIZE_FORMAT " bytes", idx,
+      gst_buffer_get_size (*buf));
 
   if (!gst_queue2_create_write (queue, *buf)) {
     GST_INFO_OBJECT (queue, "create_write() returned FALSE, bailing out");
-    return GST_BUFFER_LIST_END;
+    return FALSE;
   }
-
-  return GST_BUFFER_LIST_CONTINUE;
+  return TRUE;
 }
 
-static GstBufferListItem
-buffer_list_calc_size (GstBuffer ** buf, guint group, guint idx, gpointer data)
+static gboolean
+buffer_list_calc_size (GstBuffer ** buf, guint idx, gpointer data)
 {
   guint *p_size = data;
-  guint buf_size;
+  gsize buf_size;
 
-  buf_size = GST_BUFFER_SIZE (*buf);
-  GST_TRACE ("buffer %u in group %u has size %u", idx, group, buf_size);
+  buf_size = gst_buffer_get_size (*buf);
+  GST_TRACE ("buffer %u in has size %" G_GSIZE_FORMAT, idx, buf_size);
   *p_size += buf_size;
-
-  return GST_BUFFER_LIST_CONTINUE;
+  return TRUE;
 }
 
 /* enqueue an item an update the level stats */
@@ -1911,7 +2195,7 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
     guint size;
 
     buffer = GST_BUFFER_CAST (item);
-    size = GST_BUFFER_SIZE (buffer);
+    size = gst_buffer_get_size (buffer);
 
     /* add buffer to the statistics */
     if (QUEUE_IS_USING_QUEUE (queue)) {
@@ -1921,9 +2205,9 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
     queue->bytes_in += size;
 
     /* apply new buffer to segment stats */
-    apply_buffer (queue, buffer, &queue->sink_segment, TRUE);
+    apply_buffer (queue, buffer, &queue->sink_segment, size, TRUE);
     /* update the byterate stats */
-    update_in_rates (queue);
+    update_in_rates (queue, FALSE);
 
     if (!QUEUE_IS_USING_QUEUE (queue)) {
       /* FIXME - check return value? */
@@ -1940,7 +2224,7 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
 
     /* add buffer to the statistics */
     if (QUEUE_IS_USING_QUEUE (queue)) {
-      queue->cur_level.buffers++;
+      queue->cur_level.buffers += gst_buffer_list_length (buffer_list);
       queue->cur_level.bytes += size;
     }
     queue->bytes_in += size;
@@ -1949,7 +2233,7 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
     apply_buffer_list (queue, buffer_list, &queue->sink_segment, TRUE);
 
     /* update the byterate stats */
-    update_in_rates (queue);
+    update_in_rates (queue, FALSE);
 
     if (!QUEUE_IS_USING_QUEUE (queue)) {
       gst_buffer_list_foreach (buffer_list, buffer_list_create_write, queue);
@@ -1965,8 +2249,10 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
          * filled and we can read all data from the queue. */
         GST_DEBUG_OBJECT (queue, "we have EOS");
         queue->is_eos = TRUE;
+        /* Force updating the input bitrate */
+        update_in_rates (queue, TRUE);
         break;
-      case GST_EVENT_NEWSEGMENT:
+      case GST_EVENT_SEGMENT:
         apply_segment (queue, event, &queue->sink_segment, TRUE);
         /* This is our first new segment, we hold it
          * as we can't save it on the temp file */
@@ -1980,15 +2266,42 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
           queue->starting_segment = event;
           item = NULL;
         }
-        /* a new segment allows us to accept more buffers if we got UNEXPECTED
+        /* a new segment allows us to accept more buffers if we got EOS
          * from downstream */
         queue->unexpected = FALSE;
         break;
+      case GST_EVENT_GAP:
+        apply_gap (queue, event, &queue->sink_segment, TRUE);
+        break;
+      case GST_EVENT_STREAM_START:
+        if (!QUEUE_IS_USING_QUEUE (queue)) {
+          gst_event_replace (&queue->stream_start_event, event);
+          gst_event_unref (event);
+          item = NULL;
+        }
+        break;
+      case GST_EVENT_CAPS:{
+        GstCaps *caps;
+
+        gst_event_parse_caps (event, &caps);
+        GST_INFO ("got caps: %" GST_PTR_FORMAT, caps);
+
+        if (!QUEUE_IS_USING_QUEUE (queue)) {
+          GST_LOG ("Dropping caps event, not using queue");
+          gst_event_unref (event);
+          item = NULL;
+        }
+        break;
+      }
       default:
         if (!QUEUE_IS_USING_QUEUE (queue))
           goto unexpected_event;
         break;
     }
+  } else if (GST_IS_QUERY (item)) {
+    /* Can't happen as we check that in the caller */
+    if (!QUEUE_IS_USING_QUEUE (queue))
+      g_assert_not_reached ();
   } else {
     g_warning ("Unexpected item %p added in queue %s (refcounting problem?)",
         item, GST_OBJECT_NAME (queue));
@@ -2002,7 +2315,10 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
       update_buffering (queue);
 
     if (QUEUE_IS_USING_QUEUE (queue)) {
-      g_queue_push_tail (&queue->queue, item);
+      GstQueue2Item *qitem = g_slice_new (GstQueue2Item);
+      qitem->type = item_type;
+      qitem->item = item;
+      g_queue_push_tail (&queue->queue, qitem);
     } else {
       gst_mini_object_unref (GST_MINI_OBJECT_CAST (item));
     }
@@ -2015,10 +2331,11 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
   /* ERRORS */
 unexpected_event:
   {
-    g_warning
-        ("Unexpected event of kind %s can't be added in temp file of queue %s ",
-        gst_event_type_get_name (GST_EVENT_TYPE (item)),
-        GST_OBJECT_NAME (queue));
+    gboolean is_custom = GST_EVENT_TYPE (item) < GST_EVENT_CUSTOM_UPSTREAM;
+
+    GST_WARNING_OBJECT (queue, "%s%s event can't be added to temp file: "
+        "%" GST_PTR_FORMAT, is_custom ? "Unexpected " : "",
+        GST_EVENT_TYPE_NAME (item), GST_EVENT_CAST (item));
     gst_event_unref (GST_EVENT_CAST (item));
     return;
   }
@@ -2030,10 +2347,17 @@ gst_queue2_locked_dequeue (GstQueue2 * queue, GstQueue2ItemType * item_type)
 {
   GstMiniObject *item;
 
-  if (!QUEUE_IS_USING_QUEUE (queue))
+  if (!QUEUE_IS_USING_QUEUE (queue)) {
     item = gst_queue2_read_item_from_file (queue);
-  else
-    item = g_queue_pop_head (&queue->queue);
+  } else {
+    GstQueue2Item *qitem = g_queue_pop_head (&queue->queue);
+
+    if (qitem == NULL)
+      goto no_item;
+
+    item = qitem->item;
+    g_slice_free (GstQueue2Item, qitem);
+  }
 
   if (item == NULL)
     goto no_item;
@@ -2043,7 +2367,7 @@ gst_queue2_locked_dequeue (GstQueue2 * queue, GstQueue2ItemType * item_type)
     guint size;
 
     buffer = GST_BUFFER_CAST (item);
-    size = GST_BUFFER_SIZE (buffer);
+    size = gst_buffer_get_size (buffer);
     *item_type = GST_QUEUE2_ITEM_TYPE_BUFFER;
 
     GST_CAT_LOG_OBJECT (queue_dataflow, queue,
@@ -2055,7 +2379,7 @@ gst_queue2_locked_dequeue (GstQueue2 * queue, GstQueue2ItemType * item_type)
     }
     queue->bytes_out += size;
 
-    apply_buffer (queue, buffer, &queue->src_segment, FALSE);
+    apply_buffer (queue, buffer, &queue->src_segment, size, FALSE);
     /* update the byterate stats */
     update_out_rates (queue);
     /* update the buffering */
@@ -2075,9 +2399,12 @@ gst_queue2_locked_dequeue (GstQueue2 * queue, GstQueue2ItemType * item_type)
         /* queue is empty now that we dequeued the EOS */
         GST_QUEUE2_CLEAR_LEVEL (queue->cur_level);
         break;
-      case GST_EVENT_NEWSEGMENT:
+      case GST_EVENT_SEGMENT:
         apply_segment (queue, event, &queue->src_segment, FALSE);
         break;
+      case GST_EVENT_GAP:
+        apply_gap (queue, event, &queue->src_segment, FALSE);
+        break;
       default:
         break;
     }
@@ -2093,7 +2420,7 @@ gst_queue2_locked_dequeue (GstQueue2 * queue, GstQueue2ItemType * item_type)
         "retrieved buffer list %p from queue", buffer_list);
 
     if (QUEUE_IS_USING_QUEUE (queue)) {
-      queue->cur_level.buffers--;
+      queue->cur_level.buffers -= gst_buffer_list_length (buffer_list);
       queue->cur_level.bytes -= size;
     }
     queue->bytes_out += size;
@@ -2104,7 +2431,10 @@ gst_queue2_locked_dequeue (GstQueue2 * queue, GstQueue2ItemType * item_type)
     /* update the buffering */
     if (queue->use_buffering)
       update_buffering (queue);
-
+  } else if (GST_IS_QUERY (item)) {
+    GST_CAT_LOG_OBJECT (queue_dataflow, queue,
+        "retrieved query %p from queue", item);
+    *item_type = GST_QUEUE2_ITEM_TYPE_QUERY;
   } else {
     g_warning
         ("Unexpected item %p dequeued from queue %s (refcounting problem?)",
@@ -2124,25 +2454,27 @@ no_item:
   }
 }
 
-static gboolean
-gst_queue2_handle_sink_event (GstPad * pad, GstEvent * event)
+static GstFlowReturn
+gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
+    GstEvent * event)
 {
+  gboolean ret = TRUE;
   GstQueue2 *queue;
 
-  queue = GST_QUEUE2 (GST_OBJECT_PARENT (pad));
+  queue = GST_QUEUE2 (parent);
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_FLUSH_START:
     {
       GST_CAT_LOG_OBJECT (queue_dataflow, queue, "received flush start event");
-      if (queue->srcpad->mode == GST_ACTIVATE_PUSH) {
+      if (GST_PAD_MODE (queue->srcpad) == GST_PAD_MODE_PUSH) {
         /* forward event */
-        gst_pad_push_event (queue->srcpad, event);
+        ret = gst_pad_push_event (queue->srcpad, event);
 
         /* now unblock the chain function */
         GST_QUEUE2_MUTEX_LOCK (queue);
-        queue->srcresult = GST_FLOW_WRONG_STATE;
-        queue->sinkresult = GST_FLOW_WRONG_STATE;
+        queue->srcresult = GST_FLOW_FLUSHING;
+        queue->sinkresult = GST_FLOW_FLUSHING;
         /* unblock the loop and chain functions */
         GST_QUEUE2_SIGNAL_ADD (queue);
         GST_QUEUE2_SIGNAL_DEL (queue);
@@ -2152,36 +2484,44 @@ gst_queue2_handle_sink_event (GstPad * pad, GstEvent * event)
          * flush_start downstream. */
         gst_pad_pause_task (queue->srcpad);
         GST_CAT_LOG_OBJECT (queue_dataflow, queue, "loop stopped");
+
+        GST_QUEUE2_MUTEX_LOCK (queue);
+        queue->last_query = FALSE;
+        g_cond_signal (&queue->query_handled);
+        GST_QUEUE2_MUTEX_UNLOCK (queue);
       } else {
         GST_QUEUE2_MUTEX_LOCK (queue);
         /* flush the sink pad */
-        queue->sinkresult = GST_FLOW_WRONG_STATE;
+        queue->sinkresult = GST_FLOW_FLUSHING;
         GST_QUEUE2_SIGNAL_DEL (queue);
+        queue->last_query = FALSE;
+        g_cond_signal (&queue->query_handled);
         GST_QUEUE2_MUTEX_UNLOCK (queue);
 
         gst_event_unref (event);
       }
-      goto done;
+      break;
     }
     case GST_EVENT_FLUSH_STOP:
     {
       GST_CAT_LOG_OBJECT (queue_dataflow, queue, "received flush stop event");
 
-      if (queue->srcpad->mode == GST_ACTIVATE_PUSH) {
+      if (GST_PAD_MODE (queue->srcpad) == GST_PAD_MODE_PUSH) {
         /* forward event */
-        gst_pad_push_event (queue->srcpad, event);
+        ret = gst_pad_push_event (queue->srcpad, event);
 
         GST_QUEUE2_MUTEX_LOCK (queue);
-        gst_queue2_locked_flush (queue);
+        gst_queue2_locked_flush (queue, FALSE, TRUE);
         queue->srcresult = GST_FLOW_OK;
         queue->sinkresult = GST_FLOW_OK;
         queue->is_eos = FALSE;
         queue->unexpected = FALSE;
         queue->seeking = FALSE;
+        queue->src_tags_bitrate = queue->sink_tags_bitrate = 0;
         /* reset rate counters */
         reset_rate_timer (queue);
         gst_pad_start_task (queue->srcpad, (GstTaskFunction) gst_queue2_loop,
-            queue->srcpad);
+            queue->srcpad, NULL);
         GST_QUEUE2_MUTEX_UNLOCK (queue);
       } else {
         GST_QUEUE2_MUTEX_LOCK (queue);
@@ -2190,29 +2530,64 @@ gst_queue2_handle_sink_event (GstPad * pad, GstEvent * event)
         queue->unexpected = FALSE;
         queue->sinkresult = GST_FLOW_OK;
         queue->seeking = FALSE;
+        queue->src_tags_bitrate = queue->sink_tags_bitrate = 0;
         GST_QUEUE2_MUTEX_UNLOCK (queue);
 
         gst_event_unref (event);
       }
-      goto done;
+      break;
+    }
+    case GST_EVENT_TAG:{
+      if (queue->use_tags_bitrate) {
+        GstTagList *tags;
+        guint bitrate;
+
+        gst_event_parse_tag (event, &tags);
+        if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &bitrate) ||
+            gst_tag_list_get_uint (tags, GST_TAG_NOMINAL_BITRATE, &bitrate)) {
+          GST_QUEUE2_MUTEX_LOCK (queue);
+          queue->sink_tags_bitrate = bitrate;
+          GST_QUEUE2_MUTEX_UNLOCK (queue);
+          GST_LOG_OBJECT (queue, "Sink pad bitrate from tags now %u", bitrate);
+        }
+      }
+      /* Fall-through */
     }
     default:
       if (GST_EVENT_IS_SERIALIZED (event)) {
         /* serialized events go in the queue */
         GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->sinkresult, out_flushing);
+        if (queue->srcresult != GST_FLOW_OK) {
+          /* Errors in sticky event pushing are no problem and ignored here
+           * as they will cause more meaningful errors during data flow.
+           * For EOS events, that are not followed by data flow, we still
+           * return FALSE here though and report an error.
+           */
+          if (!GST_EVENT_IS_STICKY (event)) {
+            goto out_flow_error;
+          } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+            if (queue->srcresult == GST_FLOW_NOT_LINKED
+                || queue->srcresult < GST_FLOW_EOS) {
+              GST_ELEMENT_FLOW_ERROR (queue, queue->srcresult);
+            }
+            goto out_flow_error;
+          }
+        }
         /* refuse more events on EOS */
         if (queue->is_eos)
           goto out_eos;
         gst_queue2_locked_enqueue (queue, event, GST_QUEUE2_ITEM_TYPE_EVENT);
         GST_QUEUE2_MUTEX_UNLOCK (queue);
+        gst_queue2_post_buffering (queue);
       } else {
         /* non-serialized events are passed upstream. */
-        gst_pad_push_event (queue->srcpad, event);
+        ret = gst_pad_push_event (queue->srcpad, event);
       }
       break;
   }
-done:
-  return TRUE;
+  if (ret == FALSE)
+    return GST_FLOW_ERROR;
+  return GST_FLOW_OK;
 
   /* ERRORS */
 out_flushing:
@@ -2220,13 +2595,84 @@ out_flushing:
     GST_DEBUG_OBJECT (queue, "refusing event, we are flushing");
     GST_QUEUE2_MUTEX_UNLOCK (queue);
     gst_event_unref (event);
-    return FALSE;
+    return GST_FLOW_FLUSHING;
   }
 out_eos:
   {
     GST_DEBUG_OBJECT (queue, "refusing event, we are EOS");
     GST_QUEUE2_MUTEX_UNLOCK (queue);
     gst_event_unref (event);
+    return GST_FLOW_EOS;
+  }
+out_flow_error:
+  {
+    GST_LOG_OBJECT (queue,
+        "refusing event, we have a downstream flow error: %s",
+        gst_flow_get_name (queue->srcresult));
+    GST_QUEUE2_MUTEX_UNLOCK (queue);
+    gst_event_unref (event);
+    return queue->srcresult;
+  }
+}
+
+static gboolean
+gst_queue2_handle_sink_query (GstPad * pad, GstObject * parent,
+    GstQuery * query)
+{
+  GstQueue2 *queue;
+  gboolean res;
+
+  queue = GST_QUEUE2 (parent);
+
+  switch (GST_QUERY_TYPE (query)) {
+    default:
+      if (GST_QUERY_IS_SERIALIZED (query)) {
+        GST_CAT_LOG_OBJECT (queue_dataflow, queue, "received query %p", query);
+        /* serialized events go in the queue. We need to be certain that we
+         * don't cause deadlocks waiting for the query return value. We check if
+         * the queue is empty (nothing is blocking downstream and the query can
+         * be pushed for sure) or we are not buffering. If we are buffering,
+         * the pipeline waits to unblock downstream until our queue fills up
+         * completely, which can not happen if we block on the query..
+         * Therefore we only potentially block when we are not buffering. */
+        GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->sinkresult, out_flushing);
+        if (QUEUE_IS_USING_QUEUE (queue) && (gst_queue2_is_empty (queue)
+                || !queue->use_buffering)) {
+          if (!g_atomic_int_get (&queue->downstream_may_block)) {
+            gst_queue2_locked_enqueue (queue, query,
+                GST_QUEUE2_ITEM_TYPE_QUERY);
+
+            STATUS (queue, queue->sinkpad, "wait for QUERY");
+            while (queue->sinkresult == GST_FLOW_OK &&
+                queue->last_handled_query != query)
+              g_cond_wait (&queue->query_handled, &queue->qlock);
+            queue->last_handled_query = NULL;
+            if (queue->sinkresult != GST_FLOW_OK)
+              goto out_flushing;
+            res = queue->last_query;
+          } else {
+            GST_DEBUG_OBJECT (queue, "refusing query, downstream might block");
+            res = FALSE;
+          }
+        } else {
+          GST_DEBUG_OBJECT (queue,
+              "refusing query, we are not using the queue");
+          res = FALSE;
+        }
+        GST_QUEUE2_MUTEX_UNLOCK (queue);
+        gst_queue2_post_buffering (queue);
+      } else {
+        res = gst_pad_query_default (pad, parent, query);
+      }
+      break;
+  }
+  return res;
+
+  /* ERRORS */
+out_flushing:
+  {
+    GST_DEBUG_OBJECT (queue, "refusing query, we are flushing");
+    GST_QUEUE2_MUTEX_UNLOCK (queue);
     return FALSE;
   }
 }
@@ -2315,6 +2761,7 @@ gst_queue2_chain_buffer_or_buffer_list (GstQueue2 * queue,
   /* put buffer in queue now */
   gst_queue2_locked_enqueue (queue, item, item_type);
   GST_QUEUE2_MUTEX_UNLOCK (queue);
+  gst_queue2_post_buffering (queue);
 
   return GST_FLOW_OK;
 
@@ -2336,7 +2783,7 @@ out_eos:
     GST_QUEUE2_MUTEX_UNLOCK (queue);
     gst_mini_object_unref (item);
 
-    return GST_FLOW_UNEXPECTED;
+    return GST_FLOW_EOS;
   }
 out_seeking:
   {
@@ -2348,25 +2795,24 @@ out_seeking:
   }
 out_unexpected:
   {
-    GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-        "exit because we received UNEXPECTED");
+    GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because we received EOS");
     GST_QUEUE2_MUTEX_UNLOCK (queue);
     gst_mini_object_unref (item);
 
-    return GST_FLOW_UNEXPECTED;
+    return GST_FLOW_EOS;
   }
 }
 
 static GstFlowReturn
-gst_queue2_chain (GstPad * pad, GstBuffer * buffer)
+gst_queue2_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
 {
   GstQueue2 *queue;
 
-  queue = GST_QUEUE2 (GST_OBJECT_PARENT (pad));
+  queue = GST_QUEUE2 (parent);
 
-  GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-      "received buffer %p of size %d, time %" GST_TIME_FORMAT ", duration %"
-      GST_TIME_FORMAT, buffer, GST_BUFFER_SIZE (buffer),
+  GST_CAT_LOG_OBJECT (queue_dataflow, queue, "received buffer %p of "
+      "size %" G_GSIZE_FORMAT ", time %" GST_TIME_FORMAT ", duration %"
+      GST_TIME_FORMAT, buffer, gst_buffer_get_size (buffer),
       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
 
@@ -2375,11 +2821,12 @@ gst_queue2_chain (GstPad * pad, GstBuffer * buffer)
 }
 
 static GstFlowReturn
-gst_queue2_chain_list (GstPad * pad, GstBufferList * buffer_list)
+gst_queue2_chain_list (GstPad * pad, GstObject * parent,
+    GstBufferList * buffer_list)
 {
   GstQueue2 *queue;
 
-  queue = GST_QUEUE2 (GST_OBJECT_PARENT (pad));
+  queue = GST_QUEUE2 (parent);
 
   GST_CAT_LOG_OBJECT (queue_dataflow, queue,
       "received buffer list %p", buffer_list);
@@ -2389,46 +2836,48 @@ gst_queue2_chain_list (GstPad * pad, GstBufferList * buffer_list)
 }
 
 static GstMiniObject *
-gst_queue2_dequeue_on_unexpected (GstQueue2 * queue,
-    GstQueue2ItemType * item_type)
+gst_queue2_dequeue_on_eos (GstQueue2 * queue, GstQueue2ItemType * item_type)
 {
   GstMiniObject *data;
 
-  GST_CAT_LOG_OBJECT (queue_dataflow, queue, "got UNEXPECTED from downstream");
+  GST_CAT_LOG_OBJECT (queue_dataflow, queue, "got EOS from downstream");
 
   /* stop pushing buffers, we dequeue all items until we see an item that we
-   * can push again, which is EOS or NEWSEGMENT. If there is nothing in the
+   * can push again, which is EOS or SEGMENT. If there is nothing in the
    * queue we can push, we set a flag to make the sinkpad refuse more
-   * buffers with an UNEXPECTED return value until we receive something
+   * buffers with an EOS return value until we receive something
    * pushable again or we get flushed. */
   while ((data = gst_queue2_locked_dequeue (queue, item_type))) {
     if (*item_type == GST_QUEUE2_ITEM_TYPE_BUFFER) {
       GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-          "dropping UNEXPECTED buffer %p", data);
+          "dropping EOS buffer %p", data);
       gst_buffer_unref (GST_BUFFER_CAST (data));
     } else if (*item_type == GST_QUEUE2_ITEM_TYPE_EVENT) {
       GstEvent *event = GST_EVENT_CAST (data);
       GstEventType type = GST_EVENT_TYPE (event);
 
-      if (type == GST_EVENT_EOS || type == GST_EVENT_NEWSEGMENT) {
+      if (type == GST_EVENT_EOS || type == GST_EVENT_SEGMENT) {
         /* we found a pushable item in the queue, push it out */
         GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-            "pushing pushable event %s after UNEXPECTED",
-            GST_EVENT_TYPE_NAME (event));
+            "pushing pushable event %s after EOS", GST_EVENT_TYPE_NAME (event));
         return data;
       }
       GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-          "dropping UNEXPECTED event %p", event);
+          "dropping EOS event %p", event);
       gst_event_unref (event);
     } else if (*item_type == GST_QUEUE2_ITEM_TYPE_BUFFER_LIST) {
       GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-          "dropping UNEXPECTED buffer list %p", data);
+          "dropping EOS buffer list %p", data);
       gst_buffer_list_unref (GST_BUFFER_LIST_CAST (data));
+    } else if (*item_type == GST_QUEUE2_ITEM_TYPE_QUERY) {
+      queue->last_query = FALSE;
+      g_cond_signal (&queue->query_handled);
+      GST_CAT_LOG_OBJECT (queue_dataflow, queue, "dropping EOS query %p", data);
     }
   }
   /* no more items in the queue. Set the unexpected flag so that upstream
    * make us refuse any more buffers on the sinkpad. Since we will still
-   * accept EOS and NEWSEGMENT we return _FLOW_OK to the caller so that the
+   * accept EOS and SEGMENT we return _FLOW_OK to the caller so that the
    * task function does not shut down. */
   queue->unexpected = TRUE;
   return NULL;
@@ -2439,7 +2888,7 @@ gst_queue2_dequeue_on_unexpected (GstQueue2 * queue,
 static GstFlowReturn
 gst_queue2_push_one (GstQueue2 * queue)
 {
-  GstFlowReturn result = GST_FLOW_OK;
+  GstFlowReturn result = queue->srcresult;
   GstMiniObject *data;
   GstQueue2ItemType item_type;
 
@@ -2448,29 +2897,28 @@ gst_queue2_push_one (GstQueue2 * queue)
     goto no_item;
 
 next:
+  STATUS (queue, queue->srcpad, "We have something dequeud");
+  g_atomic_int_set (&queue->downstream_may_block,
+      item_type == GST_QUEUE2_ITEM_TYPE_BUFFER ||
+      item_type == GST_QUEUE2_ITEM_TYPE_BUFFER_LIST);
   GST_QUEUE2_MUTEX_UNLOCK (queue);
+  gst_queue2_post_buffering (queue);
 
   if (item_type == GST_QUEUE2_ITEM_TYPE_BUFFER) {
     GstBuffer *buffer;
-    GstCaps *caps;
 
     buffer = GST_BUFFER_CAST (data);
-    caps = GST_BUFFER_CAPS (buffer);
-
-    /* set caps before pushing the buffer so that core does not try to do
-     * something fancy to check if this is possible. */
-    if (caps && caps != GST_PAD_CAPS (queue->srcpad))
-      gst_pad_set_caps (queue->srcpad, caps);
 
     result = gst_pad_push (queue->srcpad, buffer);
+    g_atomic_int_set (&queue->downstream_may_block, 0);
 
     /* need to check for srcresult here as well */
     GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
-    if (result == GST_FLOW_UNEXPECTED) {
-      data = gst_queue2_dequeue_on_unexpected (queue, &item_type);
+    if (result == GST_FLOW_EOS) {
+      data = gst_queue2_dequeue_on_eos (queue, &item_type);
       if (data != NULL)
         goto next;
-      /* Since we will still accept EOS and NEWSEGMENT we return _FLOW_OK
+      /* Since we will still accept EOS and SEGMENT we return _FLOW_OK
        * to the caller so that the task function does not shut down */
       result = GST_FLOW_OK;
     }
@@ -2478,43 +2926,62 @@ next:
     GstEvent *event = GST_EVENT_CAST (data);
     GstEventType type = GST_EVENT_TYPE (event);
 
+    if (type == GST_EVENT_TAG) {
+      if (queue->use_tags_bitrate) {
+        GstTagList *tags;
+        guint bitrate;
+
+        gst_event_parse_tag (event, &tags);
+        if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &bitrate) ||
+            gst_tag_list_get_uint (tags, GST_TAG_NOMINAL_BITRATE, &bitrate)) {
+          GST_QUEUE2_MUTEX_LOCK (queue);
+          queue->src_tags_bitrate = bitrate;
+          GST_QUEUE2_MUTEX_UNLOCK (queue);
+          GST_LOG_OBJECT (queue, "src pad bitrate from tags now %u", bitrate);
+        }
+      }
+    }
+
     gst_pad_push_event (queue->srcpad, event);
 
-    /* if we're EOS, return UNEXPECTED so that the task pauses. */
+    /* if we're EOS, return EOS so that the task pauses. */
     if (type == GST_EVENT_EOS) {
       GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-          "pushed EOS event %p, return UNEXPECTED", event);
-      result = GST_FLOW_UNEXPECTED;
+          "pushed EOS event %p, return EOS", event);
+      result = GST_FLOW_EOS;
     }
 
     GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
   } else if (item_type == GST_QUEUE2_ITEM_TYPE_BUFFER_LIST) {
     GstBufferList *buffer_list;
-    GstBuffer *first_buf;
-    GstCaps *caps;
 
     buffer_list = GST_BUFFER_LIST_CAST (data);
 
-    first_buf = gst_buffer_list_get (buffer_list, 0, 0);
-    caps = (first_buf != NULL) ? GST_BUFFER_CAPS (first_buf) : NULL;
-
-    /* set caps before pushing the buffer so that core does not try to do
-     * something fancy to check if this is possible. */
-    if (caps && caps != GST_PAD_CAPS (queue->srcpad))
-      gst_pad_set_caps (queue->srcpad, caps);
-
     result = gst_pad_push_list (queue->srcpad, buffer_list);
+    g_atomic_int_set (&queue->downstream_may_block, 0);
 
     /* need to check for srcresult here as well */
     GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
-    if (result == GST_FLOW_UNEXPECTED) {
-      data = gst_queue2_dequeue_on_unexpected (queue, &item_type);
+    if (result == GST_FLOW_EOS) {
+      data = gst_queue2_dequeue_on_eos (queue, &item_type);
       if (data != NULL)
         goto next;
-      /* Since we will still accept EOS and NEWSEGMENT we return _FLOW_OK
+      /* Since we will still accept EOS and SEGMENT we return _FLOW_OK
        * to the caller so that the task function does not shut down */
       result = GST_FLOW_OK;
     }
+  } else if (item_type == GST_QUEUE2_ITEM_TYPE_QUERY) {
+    GstQuery *query = GST_QUERY_CAST (data);
+
+    GST_LOG_OBJECT (queue->srcpad, "Peering query %p", query);
+    queue->last_handled_query = query;
+    queue->last_query = gst_pad_peer_query (queue->srcpad, query);
+    GST_LOG_OBJECT (queue->srcpad, "Peered query");
+    GST_CAT_LOG_OBJECT (queue_dataflow, queue,
+        "did query %p, return %d", query, queue->last_query);
+    g_cond_signal (&queue->query_handled);
+    GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
+    result = GST_FLOW_OK;
   }
   return result;
 
@@ -2528,7 +2995,7 @@ no_item:
 out_flushing:
   {
     GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because we are flushing");
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 }
 
@@ -2572,6 +3039,7 @@ gst_queue2_loop (GstPad * pad)
     goto out_flushing;
 
   GST_QUEUE2_MUTEX_UNLOCK (queue);
+  gst_queue2_post_buffering (queue);
 
   return;
 
@@ -2582,16 +3050,20 @@ out_flushing:
     GstFlowReturn ret = queue->srcresult;
 
     gst_pad_pause_task (queue->srcpad);
+    if (ret == GST_FLOW_FLUSHING) {
+      gst_queue2_locked_flush (queue, FALSE, FALSE);
+    } else {
+      GST_QUEUE2_SIGNAL_DEL (queue);
+      queue->last_query = FALSE;
+      g_cond_signal (&queue->query_handled);
+    }
     GST_QUEUE2_MUTEX_UNLOCK (queue);
     GST_CAT_LOG_OBJECT (queue_dataflow, queue,
         "pause task, reason:  %s", gst_flow_get_name (queue->srcresult));
     /* let app know about us giving up if upstream is not expected to do so */
-    /* UNEXPECTED is already taken care of elsewhere */
-    if (eos && (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED)) {
-      GST_ELEMENT_ERROR (queue, STREAM, FAILED,
-          (_("Internal data flow error.")),
-          ("streaming task paused, reason %s (%d)",
-              gst_flow_get_name (ret), ret));
+    /* EOS is already taken care of elsewhere */
+    if (eos && (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS)) {
+      GST_ELEMENT_FLOW_ERROR (queue, ret);
       gst_pad_push_event (queue->srcpad, gst_event_new_eos ());
     }
     return;
@@ -2599,15 +3071,11 @@ out_flushing:
 }
 
 static gboolean
-gst_queue2_handle_src_event (GstPad * pad, GstEvent * event)
+gst_queue2_handle_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   gboolean res = TRUE;
-  GstQueue2 *queue = GST_QUEUE2 (gst_pad_get_parent (pad));
+  GstQueue2 *queue = GST_QUEUE2 (parent);
 
-  if (G_UNLIKELY (queue == NULL)) {
-    gst_event_unref (event);
-    return FALSE;
-  }
 #ifndef GST_DISABLE_GST_DEBUG
   GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "got event %p (%s)",
       event, GST_EVENT_TYPE_NAME (event));
@@ -2622,7 +3090,7 @@ gst_queue2_handle_src_event (GstPad * pad, GstEvent * event)
         /* now unblock the getrange function */
         GST_QUEUE2_MUTEX_LOCK (queue);
         GST_DEBUG_OBJECT (queue, "flushing");
-        queue->srcresult = GST_FLOW_WRONG_STATE;
+        queue->srcresult = GST_FLOW_FLUSHING;
         GST_QUEUE2_SIGNAL_ADD (queue);
         GST_QUEUE2_MUTEX_UNLOCK (queue);
 
@@ -2646,36 +3114,35 @@ gst_queue2_handle_src_event (GstPad * pad, GstEvent * event)
         gst_event_unref (event);
       }
       break;
+    case GST_EVENT_RECONFIGURE:
+      GST_QUEUE2_MUTEX_LOCK (queue);
+      /* assume downstream is linked now and try to push again */
+      if (queue->srcresult == GST_FLOW_NOT_LINKED) {
+        queue->srcresult = GST_FLOW_OK;
+        queue->sinkresult = GST_FLOW_OK;
+        if (GST_PAD_MODE (pad) == GST_PAD_MODE_PUSH) {
+          gst_pad_start_task (pad, (GstTaskFunction) gst_queue2_loop, pad,
+              NULL);
+        }
+      }
+      GST_QUEUE2_MUTEX_UNLOCK (queue);
+
+      res = gst_pad_push_event (queue->sinkpad, event);
+      break;
     default:
       res = gst_pad_push_event (queue->sinkpad, event);
       break;
   }
 
-  gst_object_unref (queue);
   return res;
 }
 
 static gboolean
-gst_queue2_peer_query (GstQueue2 * queue, GstPad * pad, GstQuery * query)
-{
-  gboolean ret = FALSE;
-  GstPad *peer;
-
-  if ((peer = gst_pad_get_peer (pad))) {
-    ret = gst_pad_query (peer, query);
-    gst_object_unref (peer);
-  }
-  return ret;
-}
-
-static gboolean
-gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
+gst_queue2_handle_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
 {
   GstQueue2 *queue;
 
-  queue = GST_QUEUE2 (gst_pad_get_parent (pad));
-  if (G_UNLIKELY (queue == NULL))
-    return FALSE;
+  queue = GST_QUEUE2 (parent);
 
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_POSITION:
@@ -2683,7 +3150,7 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
       gint64 peer_pos;
       GstFormat format;
 
-      if (!gst_queue2_peer_query (queue, queue->sinkpad, query))
+      if (!gst_pad_peer_query (queue->sinkpad, query))
         goto peer_failed;
 
       /* get peer position */
@@ -2693,9 +3160,13 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
       switch (format) {
         case GST_FORMAT_BYTES:
           peer_pos -= queue->cur_level.bytes;
+          if (peer_pos < 0)     /* Clamp result to 0 */
+            peer_pos = 0;
           break;
         case GST_FORMAT_TIME:
           peer_pos -= queue->cur_level.time;
+          if (peer_pos < 0)     /* Clamp result to 0 */
+            peer_pos = 0;
           break;
         default:
           GST_WARNING_OBJECT (queue, "dropping query in %s format, don't "
@@ -2710,7 +3181,7 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
     {
       GST_DEBUG_OBJECT (queue, "doing peer query");
 
-      if (!gst_queue2_peer_query (queue, queue->sinkpad, query))
+      if (!gst_pad_peer_query (queue->sinkpad, query))
         goto peer_failed;
 
       GST_DEBUG_OBJECT (queue, "peer query success");
@@ -2718,25 +3189,31 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
     }
     case GST_QUERY_BUFFERING:
     {
-      GstFormat format;
+      gint percent;
+      gboolean is_buffering;
+      GstBufferingMode mode;
+      gint avg_in, avg_out;
+      gint64 buffering_left;
 
       GST_DEBUG_OBJECT (queue, "query buffering");
 
-      /* FIXME - is this condition correct? what should ring buffer do? */
-      if (QUEUE_IS_USING_QUEUE (queue)) {
-        /* no temp file, just forward to the peer */
-        if (!gst_queue2_peer_query (queue, queue->sinkpad, query))
-          goto peer_failed;
-        GST_DEBUG_OBJECT (queue, "buffering forwarded to peer");
-      } else {
+      get_buffering_level (queue, &is_buffering, &percent);
+      percent = convert_to_buffering_percent (queue, percent);
+      gst_query_set_buffering_percent (query, is_buffering, percent);
+
+      get_buffering_stats (queue, percent, &mode, &avg_in, &avg_out,
+          &buffering_left);
+      gst_query_set_buffering_stats (query, mode, avg_in, avg_out,
+          buffering_left);
+
+      if (!QUEUE_IS_USING_QUEUE (queue)) {
+        /* add ranges for download and ringbuffer buffering */
+        GstFormat format;
         gint64 start, stop, range_start, range_stop;
         guint64 writing_pos;
-        gint percent;
-        gint64 estimated_total, buffering_left;
-        GstFormat peer_fmt;
+        gint64 estimated_total;
         gint64 duration;
-        gboolean peer_res, is_buffering, is_eos;
-        gdouble byte_in_rate, byte_out_rate;
+        gboolean peer_res, is_eos;
         GstQueue2Range *queued_ranges;
 
         /* we need a current download region */
@@ -2744,11 +3221,7 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
           return FALSE;
 
         writing_pos = queue->current->writing_pos;
-        byte_in_rate = queue->byte_in_rate;
-        byte_out_rate = queue->byte_out_rate;
-        is_buffering = queue->is_buffering;
         is_eos = queue->is_eos;
-        percent = queue->buffering_percent;
 
         if (is_eos) {
           /* we're EOS, we know the duration in bytes now */
@@ -2756,21 +3229,21 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
           duration = writing_pos;
         } else {
           /* get duration of upstream in bytes */
-          peer_fmt = GST_FORMAT_BYTES;
-          peer_res = gst_pad_query_peer_duration (queue->sinkpad, &peer_fmt,
-              &duration);
+          peer_res = gst_pad_peer_query_duration (queue->sinkpad,
+              GST_FORMAT_BYTES, &duration);
         }
 
+        GST_DEBUG_OBJECT (queue, "percent %d, duration %" G_GINT64_FORMAT
+            ", writing %" G_GINT64_FORMAT, percent, duration, writing_pos);
+
         /* calculate remaining and total download time */
-        if (peer_res && byte_in_rate > 0.0) {
-          estimated_total = (duration * 1000) / byte_in_rate;
-          buffering_left = ((duration - writing_pos) * 1000) / byte_in_rate;
-        } else {
+        if (peer_res && avg_in > 0.0)
+          estimated_total = ((duration - writing_pos) * 1000) / avg_in;
+        else
           estimated_total = -1;
-          buffering_left = -1;
-        }
-        GST_DEBUG_OBJECT (queue, "estimated %" G_GINT64_FORMAT ", left %"
-            G_GINT64_FORMAT, estimated_total, buffering_left);
+
+        GST_DEBUG_OBJECT (queue, "estimated-total %" G_GINT64_FORMAT,
+            estimated_total);
 
         gst_query_parse_buffering_range (query, &format, NULL, NULL, NULL);
 
@@ -2780,14 +3253,12 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
             if (!peer_res)
               goto peer_failed;
 
-            GST_DEBUG_OBJECT (queue,
-                "duration %" G_GINT64_FORMAT ", writing %" G_GINT64_FORMAT,
-                duration, writing_pos);
-
             start = 0;
             /* get our available data relative to the duration */
             if (duration != -1)
-              stop = GST_FORMAT_PERCENT_MAX * writing_pos / duration;
+              stop =
+                  gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX, writing_pos,
+                  duration);
             else
               stop = -1;
             break;
@@ -2811,8 +3282,12 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
                 range_stop = 0;
                 break;
               }
-              range_start = 100 * queued_ranges->offset / duration;
-              range_stop = 100 * queued_ranges->writing_pos / duration;
+              range_start =
+                  gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX,
+                  queued_ranges->offset, duration);
+              range_stop =
+                  gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX,
+                  queued_ranges->writing_pos, duration);
               break;
             case GST_FORMAT_BYTES:
               range_start = queued_ranges->offset;
@@ -2831,29 +3306,45 @@ gst_queue2_handle_src_query (GstPad * pad, GstQuery * query)
           gst_query_add_buffering_range (query, range_start, range_stop);
         }
 
-        gst_query_set_buffering_percent (query, is_buffering, percent);
         gst_query_set_buffering_range (query, format, start, stop,
             estimated_total);
-        gst_query_set_buffering_stats (query, GST_BUFFERING_DOWNLOAD,
-            byte_in_rate, byte_out_rate, buffering_left);
       }
       break;
     }
+    case GST_QUERY_SCHEDULING:
+    {
+      gboolean pull_mode;
+      GstSchedulingFlags flags = 0;
+
+      if (!gst_pad_peer_query (queue->sinkpad, query))
+        goto peer_failed;
+
+      gst_query_parse_scheduling (query, &flags, NULL, NULL, NULL);
+
+      /* we can operate in pull mode when we are using a tempfile */
+      pull_mode = !QUEUE_IS_USING_QUEUE (queue);
+
+      if (pull_mode)
+        flags |= GST_SCHEDULING_FLAG_SEEKABLE;
+      gst_query_set_scheduling (query, flags, 0, -1, 0);
+      if (pull_mode)
+        gst_query_add_scheduling_mode (query, GST_PAD_MODE_PULL);
+      gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
+      break;
+    }
     default:
       /* peer handled other queries */
-      if (!gst_queue2_peer_query (queue, queue->sinkpad, query))
+      if (!gst_pad_query_default (pad, parent, query))
         goto peer_failed;
       break;
   }
 
-  gst_object_unref (queue);
   return TRUE;
 
   /* ERRORS */
 peer_failed:
   {
     GST_DEBUG_OBJECT (queue, "failed peer query");
-    gst_object_unref (queue);
     return FALSE;
   }
 }
@@ -2861,30 +3352,39 @@ peer_failed:
 static gboolean
 gst_queue2_handle_query (GstElement * element, GstQuery * query)
 {
+  GstQueue2 *queue = GST_QUEUE2 (element);
+
   /* simply forward to the srcpad query function */
-  return gst_queue2_handle_src_query (GST_QUEUE2_CAST (element)->srcpad, query);
+  return gst_queue2_handle_src_query (queue->srcpad, GST_OBJECT_CAST (element),
+      query);
 }
 
 static void
 gst_queue2_update_upstream_size (GstQueue2 * queue)
 {
-  GstFormat fmt = GST_FORMAT_BYTES;
   gint64 upstream_size = -1;
 
-  if (gst_pad_query_peer_duration (queue->sinkpad, &fmt, &upstream_size)) {
+  if (gst_pad_peer_query_duration (queue->sinkpad, GST_FORMAT_BYTES,
+          &upstream_size)) {
     GST_INFO_OBJECT (queue, "upstream size: %" G_GINT64_FORMAT, upstream_size);
-    queue->upstream_size = upstream_size;
+
+    /* upstream_size can be negative but queue->upstream_size is unsigned.
+     * Prevent setting negative values to it (the query can return -1) */
+    if (upstream_size >= 0)
+      queue->upstream_size = upstream_size;
+    else
+      queue->upstream_size = 0;
   }
 }
 
 static GstFlowReturn
-gst_queue2_get_range (GstPad * pad, guint64 offset, guint length,
-    GstBuffer ** buffer)
+gst_queue2_get_range (GstPad * pad, GstObject * parent, guint64 offset,
+    guint length, GstBuffer ** buffer)
 {
   GstQueue2 *queue;
   GstFlowReturn ret;
 
-  queue = GST_QUEUE2_CAST (gst_pad_get_parent (pad));
+  queue = GST_QUEUE2_CAST (parent);
 
   length = (length == -1) ? DEFAULT_BUFFER_SIZE : length;
   GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
@@ -2915,8 +3415,7 @@ gst_queue2_get_range (GstPad * pad, guint64 offset, guint length,
   /* FIXME - function will block when the range is not yet available */
   ret = gst_queue2_create_read (queue, offset, length, buffer);
   GST_QUEUE2_MUTEX_UNLOCK (queue);
-
-  gst_object_unref (queue);
+  gst_queue2_post_buffering (queue);
 
   return ret;
 
@@ -2927,76 +3426,71 @@ out_flushing:
 
     GST_DEBUG_OBJECT (queue, "we are flushing");
     GST_QUEUE2_MUTEX_UNLOCK (queue);
-    gst_object_unref (queue);
     return ret;
   }
 out_unexpected:
   {
     GST_DEBUG_OBJECT (queue, "read beyond end of file");
     GST_QUEUE2_MUTEX_UNLOCK (queue);
-    gst_object_unref (queue);
-    return GST_FLOW_UNEXPECTED;
+    return GST_FLOW_EOS;
   }
 }
 
-static gboolean
-gst_queue2_src_checkgetrange_function (GstPad * pad)
-{
-  GstQueue2 *queue;
-  gboolean ret;
-
-  queue = GST_QUEUE2 (gst_pad_get_parent (pad));
-
-  /* we can operate in pull mode when we are using a tempfile */
-  ret = !QUEUE_IS_USING_QUEUE (queue);
-
-  gst_object_unref (GST_OBJECT (queue));
-
-  return ret;
-}
-
 /* sink currently only operates in push mode */
 static gboolean
-gst_queue2_sink_activate_push (GstPad * pad, gboolean active)
+gst_queue2_sink_activate_mode (GstPad * pad, GstObject * parent,
+    GstPadMode mode, gboolean active)
 {
-  gboolean result = TRUE;
+  gboolean result;
   GstQueue2 *queue;
 
-  queue = GST_QUEUE2 (gst_pad_get_parent (pad));
-
-  if (active) {
-    GST_QUEUE2_MUTEX_LOCK (queue);
-    GST_DEBUG_OBJECT (queue, "activating push mode");
-    queue->srcresult = GST_FLOW_OK;
-    queue->sinkresult = GST_FLOW_OK;
-    queue->is_eos = FALSE;
-    queue->unexpected = FALSE;
-    reset_rate_timer (queue);
-    GST_QUEUE2_MUTEX_UNLOCK (queue);
-  } else {
-    /* unblock chain function */
-    GST_QUEUE2_MUTEX_LOCK (queue);
-    GST_DEBUG_OBJECT (queue, "deactivating push mode");
-    queue->srcresult = GST_FLOW_WRONG_STATE;
-    queue->sinkresult = GST_FLOW_WRONG_STATE;
-    gst_queue2_locked_flush (queue);
-    GST_QUEUE2_MUTEX_UNLOCK (queue);
-  }
+  queue = GST_QUEUE2 (parent);
 
-  gst_object_unref (queue);
+  switch (mode) {
+    case GST_PAD_MODE_PUSH:
+      if (active) {
+        GST_QUEUE2_MUTEX_LOCK (queue);
+        GST_DEBUG_OBJECT (queue, "activating push mode");
+        queue->srcresult = GST_FLOW_OK;
+        queue->sinkresult = GST_FLOW_OK;
+        queue->is_eos = FALSE;
+        queue->unexpected = FALSE;
+        reset_rate_timer (queue);
+        GST_QUEUE2_MUTEX_UNLOCK (queue);
+      } else {
+        /* unblock chain function */
+        GST_QUEUE2_MUTEX_LOCK (queue);
+        GST_DEBUG_OBJECT (queue, "deactivating push mode");
+        queue->srcresult = GST_FLOW_FLUSHING;
+        queue->sinkresult = GST_FLOW_FLUSHING;
+        GST_QUEUE2_SIGNAL_DEL (queue);
+        GST_QUEUE2_MUTEX_UNLOCK (queue);
 
+        /* wait until it is unblocked and clean up */
+        GST_PAD_STREAM_LOCK (pad);
+        GST_QUEUE2_MUTEX_LOCK (queue);
+        gst_queue2_locked_flush (queue, TRUE, FALSE);
+        GST_QUEUE2_MUTEX_UNLOCK (queue);
+        GST_PAD_STREAM_UNLOCK (pad);
+      }
+      result = TRUE;
+      break;
+    default:
+      result = FALSE;
+      break;
+  }
   return result;
 }
 
 /* src operating in push mode, we start a task on the source pad that pushes out
  * buffers from the queue */
 static gboolean
-gst_queue2_src_activate_push (GstPad * pad, gboolean active)
+gst_queue2_src_activate_push (GstPad * pad, GstObject * parent, gboolean active)
 {
   gboolean result = FALSE;
   GstQueue2 *queue;
 
-  queue = GST_QUEUE2 (gst_pad_get_parent (pad));
+  queue = GST_QUEUE2 (parent);
 
   if (active) {
     GST_QUEUE2_MUTEX_LOCK (queue);
@@ -3005,35 +3499,38 @@ gst_queue2_src_activate_push (GstPad * pad, gboolean active)
     queue->sinkresult = GST_FLOW_OK;
     queue->is_eos = FALSE;
     queue->unexpected = FALSE;
-    result = gst_pad_start_task (pad, (GstTaskFunction) gst_queue2_loop, pad);
+    result =
+        gst_pad_start_task (pad, (GstTaskFunction) gst_queue2_loop, pad, NULL);
     GST_QUEUE2_MUTEX_UNLOCK (queue);
   } else {
     /* unblock loop function */
     GST_QUEUE2_MUTEX_LOCK (queue);
     GST_DEBUG_OBJECT (queue, "deactivating push mode");
-    queue->srcresult = GST_FLOW_WRONG_STATE;
-    queue->sinkresult = GST_FLOW_WRONG_STATE;
+    queue->srcresult = GST_FLOW_FLUSHING;
+    queue->sinkresult = GST_FLOW_FLUSHING;
     /* the item add signal will unblock */
     GST_QUEUE2_SIGNAL_ADD (queue);
     GST_QUEUE2_MUTEX_UNLOCK (queue);
 
     /* step 2, make sure streaming finishes */
     result = gst_pad_stop_task (pad);
-  }
 
-  gst_object_unref (queue);
+    GST_QUEUE2_MUTEX_LOCK (queue);
+    gst_queue2_locked_flush (queue, FALSE, FALSE);
+    GST_QUEUE2_MUTEX_UNLOCK (queue);
+  }
 
   return result;
 }
 
 /* pull mode, downstream will call our getrange function */
 static gboolean
-gst_queue2_src_activate_pull (GstPad * pad, gboolean active)
+gst_queue2_src_activate_pull (GstPad * pad, GstObject * parent, gboolean active)
 {
   gboolean result;
   GstQueue2 *queue;
 
-  queue = GST_QUEUE2 (gst_pad_get_parent (pad));
+  queue = GST_QUEUE2 (parent);
 
   if (active) {
     GST_QUEUE2_MUTEX_LOCK (queue);
@@ -3059,26 +3556,46 @@ gst_queue2_src_activate_pull (GstPad * pad, gboolean active)
       GST_DEBUG_OBJECT (queue, "no temp file, cannot activate pull mode");
       /* this is not allowed, we cannot operate in pull mode without a temp
        * file. */
-      queue->srcresult = GST_FLOW_WRONG_STATE;
-      queue->sinkresult = GST_FLOW_WRONG_STATE;
+      queue->srcresult = GST_FLOW_FLUSHING;
+      queue->sinkresult = GST_FLOW_FLUSHING;
       result = FALSE;
     }
     GST_QUEUE2_MUTEX_UNLOCK (queue);
   } else {
     GST_QUEUE2_MUTEX_LOCK (queue);
     GST_DEBUG_OBJECT (queue, "deactivating pull mode");
-    queue->srcresult = GST_FLOW_WRONG_STATE;
-    queue->sinkresult = GST_FLOW_WRONG_STATE;
+    queue->srcresult = GST_FLOW_FLUSHING;
+    queue->sinkresult = GST_FLOW_FLUSHING;
     /* this will unlock getrange */
     GST_QUEUE2_SIGNAL_ADD (queue);
     result = TRUE;
     GST_QUEUE2_MUTEX_UNLOCK (queue);
   }
-  gst_object_unref (queue);
 
   return result;
 }
 
+static gboolean
+gst_queue2_src_activate_mode (GstPad * pad, GstObject * parent, GstPadMode mode,
+    gboolean active)
+{
+  gboolean res;
+
+  switch (mode) {
+    case GST_PAD_MODE_PULL:
+      res = gst_queue2_src_activate_pull (pad, parent, active);
+      break;
+    case GST_PAD_MODE_PUSH:
+      res = gst_queue2_src_activate_push (pad, parent, active);
+      break;
+    default:
+      GST_LOG_OBJECT (pad, "unknown activation mode %d", mode);
+      res = FALSE;
+      break;
+  }
+  return res;
+}
+
 static GstStateChangeReturn
 gst_queue2_change_state (GstElement * element, GstStateChange transition)
 {
@@ -3108,6 +3625,7 @@ gst_queue2_change_state (GstElement * element, GstStateChange transition)
       }
       queue->segment_event_received = FALSE;
       queue->starting_segment = NULL;
+      gst_event_replace (&queue->stream_start_event, NULL);
       GST_QUEUE2_MUTEX_UNLOCK (queue);
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
@@ -3142,6 +3660,7 @@ gst_queue2_change_state (GstElement * element, GstStateChange transition)
         gst_event_unref (queue->starting_segment);
         queue->starting_segment = NULL;
       }
+      gst_event_replace (&queue->stream_start_event, NULL);
       GST_QUEUE2_MUTEX_UNLOCK (queue);
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
@@ -3156,8 +3675,10 @@ gst_queue2_change_state (GstElement * element, GstStateChange transition)
 /* changing the capacity of the queue must wake up
  * the _chain function, it might have more room now
  * to store the buffer/event in the queue */
-#define QUEUE_CAPACITY_CHANGE(q)\
-  GST_QUEUE2_SIGNAL_DEL (queue);
+#define QUEUE_CAPACITY_CHANGE(q) \
+  GST_QUEUE2_SIGNAL_DEL (queue); \
+  if (queue->use_buffering)      \
+    update_buffering (queue);
 
 /* Changing the minimum required fill level must
  * wake up the _loop function as it might now
@@ -3220,26 +3741,48 @@ gst_queue2_set_property (GObject * object,
       break;
     case PROP_USE_BUFFERING:
       queue->use_buffering = g_value_get_boolean (value);
+      if (!queue->use_buffering && queue->is_buffering) {
+        GST_DEBUG_OBJECT (queue, "Disabled buffering while buffering, "
+            "posting 100%% message");
+        SET_PERCENT (queue, 100);
+        queue->is_buffering = FALSE;
+      }
+
+      if (queue->use_buffering) {
+        queue->is_buffering = TRUE;
+        update_buffering (queue);
+      }
+      break;
+    case PROP_USE_TAGS_BITRATE:
+      queue->use_tags_bitrate = g_value_get_boolean (value);
       break;
     case PROP_USE_RATE_ESTIMATE:
       queue->use_rate_estimate = g_value_get_boolean (value);
       break;
     case PROP_LOW_PERCENT:
-      queue->low_percent = g_value_get_int (value);
+      queue->low_watermark = g_value_get_int (value) * BUF_LEVEL_PERCENT_FACTOR;
+      if (queue->is_buffering)
+        update_buffering (queue);
       break;
     case PROP_HIGH_PERCENT:
-      queue->high_percent = g_value_get_int (value);
+      queue->high_watermark =
+          g_value_get_int (value) * BUF_LEVEL_PERCENT_FACTOR;
+      if (queue->is_buffering)
+        update_buffering (queue);
+      break;
+    case PROP_LOW_WATERMARK:
+      queue->low_watermark = g_value_get_double (value) * MAX_BUFFERING_LEVEL;
+      if (queue->is_buffering)
+        update_buffering (queue);
+      break;
+    case PROP_HIGH_WATERMARK:
+      queue->high_watermark = g_value_get_double (value) * MAX_BUFFERING_LEVEL;
+      if (queue->is_buffering)
+        update_buffering (queue);
       break;
     case PROP_TEMP_TEMPLATE:
       gst_queue2_set_temp_template (queue, g_value_get_string (value));
       break;
-    case PROP_TEMP_LOCATION:
-      g_free (queue->temp_location);
-      queue->temp_location = g_value_dup_string (value);
-      /* you can set the property back to NULL to make it use the temp-tmpl
-       * property. */
-      queue->temp_location_set = queue->temp_location != NULL;
-      break;
     case PROP_TEMP_REMOVE:
       queue->temp_remove = g_value_get_boolean (value);
       break;
@@ -3252,6 +3795,7 @@ gst_queue2_set_property (GObject * object,
   }
 
   GST_QUEUE2_MUTEX_UNLOCK (queue);
+  gst_queue2_post_buffering (queue);
 }
 
 static void
@@ -3284,14 +3828,25 @@ gst_queue2_get_property (GObject * object,
     case PROP_USE_BUFFERING:
       g_value_set_boolean (value, queue->use_buffering);
       break;
+    case PROP_USE_TAGS_BITRATE:
+      g_value_set_boolean (value, queue->use_tags_bitrate);
+      break;
     case PROP_USE_RATE_ESTIMATE:
       g_value_set_boolean (value, queue->use_rate_estimate);
       break;
     case PROP_LOW_PERCENT:
-      g_value_set_int (value, queue->low_percent);
+      g_value_set_int (value, queue->low_watermark / BUF_LEVEL_PERCENT_FACTOR);
       break;
     case PROP_HIGH_PERCENT:
-      g_value_set_int (value, queue->high_percent);
+      g_value_set_int (value, queue->high_watermark / BUF_LEVEL_PERCENT_FACTOR);
+      break;
+    case PROP_LOW_WATERMARK:
+      g_value_set_double (value, queue->low_watermark /
+          (gdouble) MAX_BUFFERING_LEVEL);
+      break;
+    case PROP_HIGH_WATERMARK:
+      g_value_set_double (value, queue->high_watermark /
+          (gdouble) MAX_BUFFERING_LEVEL);
       break;
     case PROP_TEMP_TEMPLATE:
       g_value_set_string (value, queue->temp_template);
@@ -3305,6 +3860,19 @@ gst_queue2_get_property (GObject * object,
     case PROP_RING_BUFFER_MAX_SIZE:
       g_value_set_uint64 (value, queue->ring_buffer_max_size);
       break;
+    case PROP_AVG_IN_RATE:
+    {
+      gdouble in_rate = queue->byte_in_rate;
+
+      /* During the first RATE_INTERVAL, byte_in_rate will not have been
+       * calculated, so calculate it here. */
+      if (in_rate == 0.0 && queue->bytes_in
+          && queue->last_update_in_rates_elapsed > 0.0)
+        in_rate = queue->bytes_in / queue->last_update_in_rates_elapsed;
+
+      g_value_set_int64 (value, (gint64) in_rate);
+      break;
+    }
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;