#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
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,
* 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; \
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,
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);
{
gint buflevel, buflevel2;
- if (queue->high_percent <= 0) {
+ if (queue->high_watermark <= 0) {
if (buffering_level)
*buffering_level = MAX_BUFFERING_LEVEL;
if (is_buffering)
/* 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;
} 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);
}
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));
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);
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)
{
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);