queue2: Add higher-resolution low/high-watermark properties
authorCarlos Rafael Giani <dv@pseudoterminal.org>
Wed, 3 Aug 2016 13:20:20 +0000 (15:20 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Thu, 25 Aug 2016 08:54:33 +0000 (11:54 +0300)
low/high-watermark are of type double, and given in range 0.0-1.0. This
makes it possible to set low/high watermarks with greater resolution,
which is useful with large queue2 max sizes and watermarks like 0.5%.

Also adding a test to check the fill and watermark level behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=769449

plugins/elements/gstqueue2.c
plugins/elements/gstqueue2.h
tests/check/elements/queue2.c

index 9300b21..ec72936 100644 (file)
@@ -117,6 +117,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 +136,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,
@@ -161,7 +165,12 @@ enum
  * range. Whenever "buffering_percent" is mentioned, it refers to the
  * percentage value that is relative to the low/high watermark. */
 
-#define MAX_BUFFERING_LEVEL 100
+/* 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;                                        \
@@ -375,13 +384,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,
@@ -484,8 +505,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);
@@ -926,7 +947,7 @@ get_buffering_level (GstQueue2 * queue, gboolean * is_buffering,
 {
   gint buflevel, buflevel2;
 
-  if (queue->high_percent <= 0) {
+  if (queue->high_watermark <= 0) {
     if (buffering_level)
       *buffering_level = MAX_BUFFERING_LEVEL;
     if (is_buffering)
@@ -992,7 +1013,7 @@ convert_to_buffering_percent (GstQueue2 * queue, gint buffering_level)
 
   /* scale so that if buffering_level equals the high watermark,
    * the percentage is 100% */
-  percent = buffering_level * 100 / queue->high_percent;
+  percent = buffering_level * 100 / queue->high_watermark;
   /* clip */
   if (percent > 100)
     percent = 100;
@@ -1096,7 +1117,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 (buffering_level < queue->low_percent) {
+    if (buffering_level < queue->low_watermark) {
       queue->is_buffering = TRUE;
       SET_PERCENT (queue, percent);
     }
@@ -3730,10 +3751,17 @@ 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;
       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;
+      break;
+    case PROP_LOW_WATERMARK:
+      queue->low_watermark = g_value_get_double (value) * MAX_BUFFERING_LEVEL;
+      break;
+    case PROP_HIGH_WATERMARK:
+      queue->high_watermark = g_value_get_double (value) * MAX_BUFFERING_LEVEL;
       break;
     case PROP_TEMP_TEMPLATE:
       gst_queue2_set_temp_template (queue, g_value_get_string (value));
@@ -3790,10 +3818,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);
index 670953f..a1b94a7 100644 (file)
@@ -109,8 +109,10 @@ struct _GstQueue2
   gboolean use_tags_bitrate;
   gboolean use_rate_estimate;
   GstClockTime buffering_interval;
-  gint low_percent;             /* low/high watermarks for buffering */
-  gint high_percent;
+
+  /* low/high watermarks for buffering */
+  gint low_watermark;
+  gint high_watermark;
 
   /* current buffering state */
   gboolean is_buffering;
index a32cdd0..40395b5 100644 (file)
@@ -207,6 +207,74 @@ GST_START_TEST (test_simple_create_destroy)
 
 GST_END_TEST;
 
+#define CHECK_FOR_BUFFERING_MSG(PIPELINE, EXPECTED_PERC) \
+  G_STMT_START { \
+    gint buf_perc; \
+    GstMessage *msg; \
+    GST_LOG ("waiting for %d%% buffering message", (EXPECTED_PERC)); \
+    msg = gst_bus_poll (GST_ELEMENT_BUS (PIPELINE), \
+        GST_MESSAGE_BUFFERING | GST_MESSAGE_ERROR, -1); \
+    fail_if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR, \
+        "Expected BUFFERING message, got ERROR message"); \
+    gst_message_parse_buffering (msg, &buf_perc); \
+    gst_message_unref (msg); \
+    fail_unless (buf_perc == (EXPECTED_PERC), \
+        "Got incorrect percentage: %d%% expected: %d%%", buf_perc, \
+        (EXPECTED_PERC)); \
+  } G_STMT_END
+
+GST_START_TEST (test_watermark_and_fill_level)
+{
+  /* This test checks the behavior of the fill level and
+   * the low/high watermarks. It also checks if the
+   * low/high-percent and low/high-watermark properties
+   * are coupled together properly. */
+
+  GstElement *pipe, *input, *output, *queue2;
+  gint low_perc, high_perc;
+
+  pipe = gst_pipeline_new ("pipeline");
+
+  input = gst_element_factory_make ("fakesrc", NULL);
+  fail_unless (input != NULL, "failed to create 'fakesrc' element");
+  /* Configure fakesrc to send one single buffer with 50000 bytes,
+   * which makes 50000 / 1000000 = 50% of the max queue2 size. */
+  g_object_set (input, "num-buffers", 1, "sizetype", 2, "sizemax", 50000, NULL);
+
+  output = gst_element_factory_make ("fakesink", NULL);
+  fail_unless (output != NULL, "failed to create 'fakesink' element");
+
+  queue2 = setup_queue2 (pipe, input, output);
+  g_object_set (queue2,
+      "use-buffering", (gboolean) TRUE,
+      "max-size-bytes", (guint) 1000000,
+      "max-size-buffers", (guint) 0,
+      "max-size-time", (guint64) 0,
+      "low-watermark", (gdouble) 0.01, "high-watermark", (gdouble) 0.10, NULL);
+
+  g_object_get (queue2, "low-percent", &low_perc,
+      "high-percent", &high_perc, NULL);
+
+  /* Check that low/high-watermark and low/high-percent are
+   * coupled properly. (low/high-percent are deprecated and
+   * exist for backwards compatibility.) */
+  fail_unless_equals_int (low_perc, 1);
+  fail_unless_equals_int (high_perc, 10);
+
+  gst_element_set_state (pipe, GST_STATE_PLAYING);
+
+  /* First buffering message will contain 0% (the initial state).
+   * Second buffering message contain 50% after the single
+   * buffer from fakesrc is pushed downstream. */
+  CHECK_FOR_BUFFERING_MSG (pipe, 0);
+  CHECK_FOR_BUFFERING_MSG (pipe, 50);
+
+  gst_element_set_state (pipe, GST_STATE_NULL);
+  gst_object_unref (pipe);
+}
+
+GST_END_TEST;
+
 static gpointer
 push_buffer (GstPad * sinkpad)
 {
@@ -384,6 +452,7 @@ queue2_suite (void)
   tcase_add_test (tc_chain, test_simple_pipeline_ringbuffer);
   tcase_add_test (tc_chain, test_simple_shutdown_while_running);
   tcase_add_test (tc_chain, test_simple_shutdown_while_running_ringbuffer);
+  tcase_add_test (tc_chain, test_watermark_and_fill_level);
   tcase_add_test (tc_chain, test_filled_read);
   tcase_add_test (tc_chain, test_percent_overflow);
   tcase_add_test (tc_chain, test_small_ring_buffer);