downloadbuffer: unlock mutex in error case
[platform/upstream/gstreamer.git] / plugins / elements / gstqueue2.c
index 67e3f27..51db828 100644 (file)
@@ -25,6 +25,7 @@
 
 /**
  * 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
@@ -117,6 +118,8 @@ enum
 #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
 
@@ -134,6 +137,8 @@ enum
   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,
@@ -142,6 +147,32 @@ enum
   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;                                          \
@@ -248,8 +279,8 @@ static GstFlowReturn gst_queue2_chain_list (GstPad * pad, GstObject * parent,
 static GstFlowReturn gst_queue2_push_one (GstQueue2 * queue);
 static void gst_queue2_loop (GstPad * pad);
 
-static gboolean gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
-    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);
 
@@ -274,7 +305,8 @@ 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);
+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
@@ -353,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,
@@ -410,10 +454,8 @@ gst_queue2_class_init (GstQueue2Class * klass)
   /* set several parent class virtual functions */
   gobject_class->finalize = gst_queue2_finalize;
 
-  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));
+  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",
@@ -436,7 +478,7 @@ gst_queue2_init (GstQueue2 * queue)
       GST_DEBUG_FUNCPTR (gst_queue2_chain_list));
   gst_pad_set_activatemode_function (queue->sinkpad,
       GST_DEBUG_FUNCPTR (gst_queue2_sink_activate_mode));
-  gst_pad_set_event_function (queue->sinkpad,
+  gst_pad_set_event_full_function (queue->sinkpad,
       GST_DEBUG_FUNCPTR (gst_queue2_handle_sink_event));
   gst_pad_set_query_function (queue->sinkpad,
       GST_DEBUG_FUNCPTR (gst_queue2_handle_sink_query));
@@ -464,8 +506,8 @@ gst_queue2_init (GstQueue2 * queue)
   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);
@@ -883,7 +925,8 @@ apply_buffer_list (GstQueue2 * queue, GstBufferList * buffer_list,
 }
 
 static inline gint
-get_percent (guint64 cur_level, guint64 max_level, guint64 alt_max)
+normalize_to_buffering_level (guint64 cur_level, guint64 max_level,
+    guint64 alt_max)
 {
   guint64 p;
 
@@ -891,33 +934,34 @@ get_percent (guint64 cur_level, guint64 max_level, guint64 alt_max)
     return 0;
 
   if (alt_max > 0)
-    p = gst_util_uint64_scale (cur_level, 100, MIN (max_level, alt_max));
+    p = gst_util_uint64_scale (cur_level, MAX_BUFFERING_LEVEL,
+        MIN (max_level, alt_max));
   else
-    p = gst_util_uint64_scale (cur_level, 100, max_level);
+    p = gst_util_uint64_scale (cur_level, MAX_BUFFERING_LEVEL, max_level);
 
-  return MIN (p, 100);
+  return MIN (p, MAX_BUFFERING_LEVEL);
 }
 
 static gboolean
-get_buffering_percent (GstQueue2 * queue, gboolean * is_buffering,
-    gint * percent)
+get_buffering_level (GstQueue2 * queue, gboolean * is_buffering,
+    gint * buffering_level)
 {
-  gint perc, perc2;
+  gint buflevel, buflevel2;
 
-  if (queue->high_percent <= 0) {
-    if (percent)
-      *percent = 100;
+  if (queue->high_watermark <= 0) {
+    if (buffering_level)
+      *buffering_level = MAX_BUFFERING_LEVEL;
     if (is_buffering)
       *is_buffering = FALSE;
     return FALSE;
   }
-#define GET_PERCENT(format,alt_max) \
-    get_percent(queue->cur_level.format,queue->max_level.format,(alt_max))
+#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 */
-    perc = 100;
+    buflevel = MAX_BUFFERING_LEVEL;
     GST_LOG_OBJECT (queue, "we are EOS");
   } else {
     GST_LOG_OBJECT (queue,
@@ -925,50 +969,59 @@ get_buffering_percent (GstQueue2 * queue, gboolean * is_buffering,
         queue->cur_level.bytes, GST_TIME_ARGS (queue->cur_level.time),
         queue->cur_level.buffers);
 
-    /* figure out the percent we are filled, we take the max of all formats. */
+    /* figure out the buffering level we are filled, we take the max of all formats. */
     if (!QUEUE_IS_USING_RING_BUFFER (queue)) {
-      perc = GET_PERCENT (bytes, 0);
+      buflevel = GET_BUFFER_LEVEL_FOR_QUANTITY (bytes, 0);
     } else {
       guint64 rb_size = queue->ring_buffer_max_size;
-      perc = GET_PERCENT (bytes, rb_size);
+      buflevel = GET_BUFFER_LEVEL_FOR_QUANTITY (bytes, rb_size);
     }
 
-    perc2 = GET_PERCENT (time, 0);
-    perc = MAX (perc, perc2);
+    buflevel2 = GET_BUFFER_LEVEL_FOR_QUANTITY (time, 0);
+    buflevel = MAX (buflevel, buflevel2);
 
-    perc2 = GET_PERCENT (buffers, 0);
-    perc = MAX (perc, perc2);
+    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) {
-      perc2 = GET_PERCENT (rate_time, 0);
-      perc = MAX (perc, perc2);
+      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)
-      perc = MAX (1, perc);
+      buflevel = MAX (1, buflevel);
   }
-#undef GET_PERCENT
+#undef GET_BUFFER_LEVEL_FOR_QUANTITY
 
   if (is_buffering)
     *is_buffering = queue->is_buffering;
 
-  /* scale to high percent so that it becomes the 100% mark */
-  perc = perc * 100 / queue->high_percent;
-  /* clip */
-  if (perc > 100)
-    perc = 100;
+  if (buffering_level)
+    *buffering_level = buflevel;
 
-  if (percent)
-    *percent = perc;
-
-  GST_DEBUG_OBJECT (queue, "buffering %d, percent %d", queue->is_buffering,
-      perc);
+  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)
@@ -1004,13 +1057,12 @@ get_buffering_stats (GstQueue2 * queue, gint percent, GstBufferingMode * mode,
   }
 }
 
-static void
-gst_queue2_post_buffering (GstQueue2 * queue)
+/* Called with the lock taken */
+static GstMessage *
+gst_queue2_get_buffering_message (GstQueue2 * queue)
 {
   GstMessage *msg = NULL;
 
-  g_mutex_lock (&queue->buffering_post_lock);
-  GST_QUEUE2_MUTEX_LOCK (queue);
   if (queue->percent_changed) {
     gint percent = queue->buffering_percent;
 
@@ -1022,6 +1074,18 @@ gst_queue2_post_buffering (GstQueue2 * queue)
     gst_message_set_buffering_stats (msg, queue->mode, queue->avg_in,
         queue->avg_out, queue->buffering_left);
   }
+
+  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)
@@ -1033,16 +1097,18 @@ gst_queue2_post_buffering (GstQueue2 * queue)
 static void
 update_buffering (GstQueue2 * queue)
 {
-  gint percent;
+  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);
+  update_in_rates (queue, FALSE);
 
-  if (!get_buffering_percent (queue, NULL, &percent))
+  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)
@@ -1052,7 +1118,7 @@ update_buffering (GstQueue2 * queue)
   } else {
     /* we were not buffering, check if we need to start buffering if we drop
      * below the low threshold */
-    if (percent < queue->low_percent) {
+    if (buffering_level < queue->low_watermark) {
       queue->is_buffering = TRUE;
       SET_PERCENT (queue, percent);
     }
@@ -1087,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;
@@ -1102,7 +1168,7 @@ update_in_rates (GstQueue2 * queue)
       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,
@@ -1846,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;
 
@@ -2027,8 +2096,18 @@ gst_queue2_create_write (GstQueue2 * queue, GstBuffer * buffer)
     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));
@@ -2128,7 +2207,7 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
     /* apply new buffer to segment stats */
     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? */
@@ -2154,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);
@@ -2170,6 +2249,8 @@ 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_SEGMENT:
         apply_segment (queue, event, &queue->sink_segment, TRUE);
@@ -2373,7 +2454,7 @@ no_item:
   }
 }
 
-static gboolean
+static GstFlowReturn
 gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
     GstEvent * event)
 {
@@ -2397,14 +2478,17 @@ gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
         /* unblock the loop and chain functions */
         GST_QUEUE2_SIGNAL_ADD (queue);
         GST_QUEUE2_SIGNAL_DEL (queue);
-        queue->last_query = FALSE;
-        g_cond_signal (&queue->query_handled);
         GST_QUEUE2_MUTEX_UNLOCK (queue);
 
         /* make sure it pauses, this should happen since we sent
          * 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 */
@@ -2484,10 +2568,7 @@ gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
           } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
             if (queue->srcresult == GST_FLOW_NOT_LINKED
                 || queue->srcresult < GST_FLOW_EOS) {
-              GST_ELEMENT_ERROR (queue, STREAM, FAILED,
-                  (_("Internal data flow error.")),
-                  ("streaming task paused, reason %s (%d)",
-                      gst_flow_get_name (queue->srcresult), queue->srcresult));
+              GST_ELEMENT_FLOW_ERROR (queue, queue->srcresult);
             }
             goto out_flow_error;
           }
@@ -2504,7 +2585,9 @@ gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
       }
       break;
   }
-  return ret;
+  if (ret == FALSE)
+    return GST_FLOW_ERROR;
+  return GST_FLOW_OK;
 
   /* ERRORS */
 out_flushing:
@@ -2512,14 +2595,14 @@ 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 FALSE;
+    return GST_FLOW_EOS;
   }
 out_flow_error:
   {
@@ -2528,7 +2611,7 @@ out_flow_error:
         gst_flow_get_name (queue->srcresult));
     GST_QUEUE2_MUTEX_UNLOCK (queue);
     gst_event_unref (event);
-    return FALSE;
+    return queue->srcresult;
   }
 }
 
@@ -2560,7 +2643,10 @@ gst_queue2_handle_sink_query (GstPad * pad, GstObject * parent,
                 GST_QUEUE2_ITEM_TYPE_QUERY);
 
             STATUS (queue, queue->sinkpad, "wait for QUERY");
-            g_cond_wait (&queue->query_handled, &queue->qlock);
+            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;
@@ -2888,6 +2974,7 @@ next:
     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,
@@ -2976,10 +3063,7 @@ out_flushing:
     /* let app know about us giving up if upstream is not expected to do so */
     /* EOS is already taken care of elsewhere */
     if (eos && (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS)) {
-      GST_ELEMENT_ERROR (queue, STREAM, FAILED,
-          (_("Internal data flow error.")),
-          ("streaming task paused, reason %s (%d)",
-              gst_flow_get_name (ret), ret));
+      GST_ELEMENT_FLOW_ERROR (queue, ret);
       gst_pad_push_event (queue->srcpad, gst_event_new_eos ());
     }
     return;
@@ -3076,9 +3160,13 @@ gst_queue2_handle_src_query (GstPad * pad, GstObject * parent, 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 "
@@ -3109,7 +3197,8 @@ gst_queue2_handle_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
 
       GST_DEBUG_OBJECT (queue, "query buffering");
 
-      get_buffering_percent (queue, &is_buffering, &percent);
+      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,
@@ -3375,9 +3464,6 @@ gst_queue2_sink_activate_mode (GstPad * pad, GstObject * parent,
         queue->srcresult = GST_FLOW_FLUSHING;
         queue->sinkresult = GST_FLOW_FLUSHING;
         GST_QUEUE2_SIGNAL_DEL (queue);
-        /* Unblock query handler */
-        queue->last_query = FALSE;
-        g_cond_signal (&queue->query_handled);
         GST_QUEUE2_MUTEX_UNLOCK (queue);
 
         /* wait until it is unblocked and clean up */
@@ -3428,6 +3514,10 @@ gst_queue2_src_activate_push (GstPad * pad, GstObject * parent, gboolean active)
 
     /* step 2, make sure streaming finishes */
     result = gst_pad_stop_task (pad);
+
+    GST_QUEUE2_MUTEX_LOCK (queue);
+    gst_queue2_locked_flush (queue, FALSE, FALSE);
+    GST_QUEUE2_MUTEX_UNLOCK (queue);
   }
 
   return result;
@@ -3670,10 +3760,25 @@ gst_queue2_set_property (GObject * object,
       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));
@@ -3730,10 +3835,18 @@ gst_queue2_get_property (GObject * object,
       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);