queue: Ignore thresholds if a query is queued

The queue gets filled by the tail, so a query will always be the tail
object, not the head object. Also add a _peek_tail_struct() method to the
GstQueueArray to enable looking at the tail.

With unit test to prevent future regression.

https://bugzilla.gnome.org/show_bug.cgi?id=762875
diff --git a/libs/gst/base/gstqueuearray.c b/libs/gst/base/gstqueuearray.c
index 0f8ed57..ebec179 100644
--- a/libs/gst/base/gstqueuearray.c
+++ b/libs/gst/base/gstqueuearray.c
@@ -339,6 +339,34 @@
 }
 
 /**
+ * gst_queue_array_peek_tail_struct: (skip)
+ * @array: a #GstQueueArray object
+ *
+ * Returns the tail of the queue @array, but does not remove it from the queue.
+ *
+ * Returns: The tail of the queue
+ *
+ * Since: 1.14
+ */
+gpointer
+gst_queue_array_peek_tail_struct (GstQueueArray * array)
+{
+  guint len, idx;
+
+  g_return_val_if_fail (array != NULL, NULL);
+
+  len = array->length;
+
+  /* empty array */
+  if (len == 0)
+    return NULL;
+
+  idx = (array->head + (len - 1)) % array->size;
+
+  return array->array + (array->elt_size * idx);
+}
+
+/**
  * gst_queue_array_pop_tail: (skip)
  * @array: a #GstQueueArray object
  *
diff --git a/libs/gst/base/gstqueuearray.h b/libs/gst/base/gstqueuearray.h
index c05780e..5752320 100644
--- a/libs/gst/base/gstqueuearray.h
+++ b/libs/gst/base/gstqueuearray.h
@@ -83,6 +83,8 @@
 gboolean        gst_queue_array_drop_struct      (GstQueueArray * array,
                                                   guint           idx,
                                                   gpointer        p_struct);
+GST_EXPORT
+gpointer        gst_queue_array_peek_tail_struct (GstQueueArray * array);
 
 G_END_DECLS
 
diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c
index 431dcdc..a8753ea 100644
--- a/plugins/elements/gstqueue.c
+++ b/plugins/elements/gstqueue.c
@@ -1094,18 +1094,18 @@
 static gboolean
 gst_queue_is_empty (GstQueue * queue)
 {
-  GstQueueItem *head;
+  GstQueueItem *tail;
 
-  head = gst_queue_array_peek_head_struct (queue->queue);
+  tail = gst_queue_array_peek_tail_struct (queue->queue);
 
-  if (head == NULL)
+  if (tail == NULL)
     return TRUE;
 
   /* Only consider the queue empty if the minimum thresholds
-   * are not reached and data is at the queue head. Otherwise
+   * are not reached and data is at the queue tail. Otherwise
    * we would block forever on serialized queries.
    */
-  if (!GST_IS_BUFFER (head->item) && !GST_IS_BUFFER_LIST (head->item))
+  if (!GST_IS_BUFFER (tail->item) && !GST_IS_BUFFER_LIST (tail->item))
     return FALSE;
 
   /* It is possible that a max size is reached before all min thresholds are.
diff --git a/tests/check/elements/queue.c b/tests/check/elements/queue.c
index 50f2896..a41ef64 100644
--- a/tests/check/elements/queue.c
+++ b/tests/check/elements/queue.c
@@ -906,6 +906,40 @@
 
 GST_END_TEST;
 
+
+GST_START_TEST (test_serialized_query_with_threshold)
+{
+  GstQuery *query;
+  GstSegment segment;
+
+  gst_segment_init (&segment, GST_FORMAT_BYTES);
+
+  mysinkpad = gst_check_setup_sink_pad (queue, &sinktemplate);
+  gst_pad_set_event_function (mysinkpad, event_func);
+  gst_pad_set_active (mysinkpad, TRUE);
+
+  g_object_set (queue, "min-threshold-buffers", 10, NULL);
+
+  fail_unless (gst_element_set_state (queue,
+          GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to playing");
+
+  gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
+  gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment));
+
+  gst_pad_push (mysrcpad, gst_buffer_new ());
+
+  query = gst_query_new_drain ();
+  gst_pad_peer_query (mysrcpad, query);
+  gst_query_unref (query);
+
+  fail_unless (gst_element_set_state (queue,
+          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+}
+
+GST_END_TEST;
+
+
 static gpointer
 push_event_thread_func (gpointer data)
 {
@@ -1162,6 +1196,7 @@
   tcase_add_test (tc_chain, test_time_level);
   tcase_add_test (tc_chain, test_time_level_task_not_started);
   tcase_add_test (tc_chain, test_queries_while_flushing);
+  tcase_add_test (tc_chain, test_serialized_query_with_threshold);
   tcase_add_test (tc_chain, test_state_change_when_flushing);
 #if 0
   tcase_add_test (tc_chain, test_newsegment);