libs/gst/controller/: Protect against values larger or smaller than the minimum or maximum allowed value for the prop...

Original commit message from CVS:
* libs/gst/controller/gstcontroller.c:
(gst_controlled_property_new):
* libs/gst/controller/gstcontrollerprivate.h:
* libs/gst/controller/gstinterpolation.c:
(gst_controlled_property_find_control_point_node),
(interpolate_none_get), (interpolate_none_get_enum_value_array),
(interpolate_none_get_string_value_array),
(interpolate_trigger_get),
(interpolate_trigger_get_enum_value_array),
(interpolate_trigger_get_string_value_array):
Protect against values larger or smaller than the minimum or maximum
allowed value for the property when using values that can be compared.
Optimize trigger interpolator a bit by taking the last requested value
into account instead of always looping through the complete list.
Fix coding style a bit, everywhere else we use "return foo" instead
of "return (foo)".
* tests/check/libs/controller.c: (GST_START_TEST),
(gst_controller_suite):
Add unit test for the protection against too large or too small
values.
This commit is contained in:
Sebastian Dröge 2007-06-09 16:58:30 +00:00
parent 4ae4b8e75f
commit bdcc0329ef
5 changed files with 232 additions and 48 deletions

View file

@ -1,3 +1,29 @@
2007-06-09 Sebastian Dröge <slomo@circular-chaos.org>
* libs/gst/controller/gstcontroller.c:
(gst_controlled_property_new):
* libs/gst/controller/gstcontrollerprivate.h:
* libs/gst/controller/gstinterpolation.c:
(gst_controlled_property_find_control_point_node),
(interpolate_none_get), (interpolate_none_get_enum_value_array),
(interpolate_none_get_string_value_array),
(interpolate_trigger_get),
(interpolate_trigger_get_enum_value_array),
(interpolate_trigger_get_string_value_array):
Protect against values larger or smaller than the minimum or maximum
allowed value for the property when using values that can be compared.
Optimize trigger interpolator a bit by taking the last requested value
into account instead of always looping through the complete list.
Fix coding style a bit, everywhere else we use "return foo" instead
of "return (foo)".
* tests/check/libs/controller.c: (GST_START_TEST),
(gst_controller_suite):
Add unit test for the protection against too large or too small
values.
2007-06-08 Sebastian Dröge <slomo@circular-chaos.org>
* docs/random/slomo/controller.txt:

View file

@ -340,36 +340,60 @@ gst_controlled_property_new (GObject * object, const gchar * name)
GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
g_value_set_int (&prop->default_value, tpspec->default_value);
g_value_init (&prop->min_value, prop->type);
g_value_set_int (&prop->min_value, tpspec->minimum);
g_value_init (&prop->max_value, prop->type);
g_value_set_int (&prop->max_value, tpspec->maximum);
}
break;
case G_TYPE_UINT:{
GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
g_value_set_uint (&prop->default_value, tpspec->default_value);
g_value_init (&prop->min_value, prop->type);
g_value_set_uint (&prop->min_value, tpspec->minimum);
g_value_init (&prop->max_value, prop->type);
g_value_set_uint (&prop->max_value, tpspec->maximum);
}
break;
case G_TYPE_LONG:{
GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
g_value_set_long (&prop->default_value, tpspec->default_value);
g_value_init (&prop->min_value, prop->type);
g_value_set_long (&prop->min_value, tpspec->minimum);
g_value_init (&prop->max_value, prop->type);
g_value_set_long (&prop->max_value, tpspec->maximum);
}
break;
case G_TYPE_ULONG:{
GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
g_value_set_ulong (&prop->default_value, tpspec->default_value);
g_value_init (&prop->min_value, prop->type);
g_value_set_ulong (&prop->min_value, tpspec->minimum);
g_value_init (&prop->max_value, prop->type);
g_value_set_ulong (&prop->max_value, tpspec->maximum);
}
break;
case G_TYPE_FLOAT:{
GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
g_value_set_float (&prop->default_value, tpspec->default_value);
g_value_init (&prop->min_value, prop->type);
g_value_set_float (&prop->min_value, tpspec->minimum);
g_value_init (&prop->max_value, prop->type);
g_value_set_float (&prop->max_value, tpspec->maximum);
}
break;
case G_TYPE_DOUBLE:{
GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
g_value_set_double (&prop->default_value, tpspec->default_value);
g_value_init (&prop->min_value, prop->type);
g_value_set_double (&prop->min_value, tpspec->minimum);
g_value_init (&prop->max_value, prop->type);
g_value_set_double (&prop->max_value, tpspec->maximum);
}
break;
case G_TYPE_BOOLEAN:{

View file

@ -100,6 +100,8 @@ typedef struct _GstControlledProperty
GType type; /* type of the handled property */
GType base; /* base-type of the handled property */
GValue default_value; /* default value for the handled property */
GValue min_value; /* min value for the handled property */
GValue max_value; /* max value for the handled property */
GValue result_value; /* result value location for the interpolation method */
GstControlPoint last_value; /* the last value a _sink call wrote */
GstControlPoint live_value; /* temporary value override for live input */

View file

@ -71,7 +71,7 @@ gst_controlled_property_find_control_point_node (GstControlledProperty * prop,
if (prev_node)
prop->last_requested_value = prev_node;
return (prev_node);
return prev_node;
}
/* steps-like (no-)interpolation, default */
@ -86,26 +86,30 @@ interpolate_none_get (GstControlledProperty * prop, GstClockTime timestamp)
gst_controlled_property_find_control_point_node (prop, timestamp))) {
GstControlPoint *cp = node->data;
return (&cp->value);
return &cp->value;
}
return (&prop->default_value);
return &prop->default_value;
}
#define interpolate_none_get_boolean interpolate_none_get
#define DEFINE_NONE_GET(type) \
static gboolean \
interpolate_none_get_##type##_value_array (GstControlledProperty * prop, \
GstClockTime timestamp, GstValueArray * value_array) \
static GValue * \
interpolate_none_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
{ \
gint i; \
GstClockTime ts = timestamp; \
g##type *values = (g##type *) value_array->values; \
GList *node; \
\
for(i = 0; i < value_array->nbsamples; i++) { \
*values = g_value_get_##type (interpolate_none_get (prop,ts)); \
ts += value_array->sample_interval; \
values++; \
if ((node = \
gst_controlled_property_find_control_point_node (prop, timestamp))) { \
GstControlPoint *cp = node->data; \
g##type ret = g_value_get_##type (&cp->value); \
if (g_value_get_##type (&prop->min_value) > ret) \
return &prop->min_value; \
else if (g_value_get_##type (&prop->max_value) < ret) \
return &prop->max_value; \
return &cp->value; \
} \
return (TRUE); \
return &prop->default_value; \
}
DEFINE_NONE_GET (int);
@ -116,7 +120,33 @@ DEFINE_NONE_GET (ulong);
DEFINE_NONE_GET (float);
DEFINE_NONE_GET (double);
DEFINE_NONE_GET (boolean);
#define DEFINE_NONE_GET_VALUE_ARRAY(type) \
static gboolean \
interpolate_none_get_##type##_value_array (GstControlledProperty * prop, \
GstClockTime timestamp, GstValueArray * value_array) \
{ \
gint i; \
GstClockTime ts = timestamp; \
g##type *values = (g##type *) value_array->values; \
\
for(i = 0; i < value_array->nbsamples; i++) { \
*values = g_value_get_##type (interpolate_none_get_##type (prop,ts)); \
ts += value_array->sample_interval; \
values++; \
} \
return TRUE; \
}
DEFINE_NONE_GET_VALUE_ARRAY (int);
DEFINE_NONE_GET_VALUE_ARRAY (uint);
DEFINE_NONE_GET_VALUE_ARRAY (long);
DEFINE_NONE_GET_VALUE_ARRAY (ulong);
DEFINE_NONE_GET_VALUE_ARRAY (float);
DEFINE_NONE_GET_VALUE_ARRAY (double);
DEFINE_NONE_GET_VALUE_ARRAY (boolean);
static gboolean
interpolate_none_get_enum_value_array (GstControlledProperty * prop,
@ -131,7 +161,7 @@ interpolate_none_get_enum_value_array (GstControlledProperty * prop,
ts += value_array->sample_interval;
values++;
}
return (TRUE);
return TRUE;
}
static gboolean
@ -147,7 +177,7 @@ interpolate_none_get_string_value_array (GstControlledProperty * prop,
ts += value_array->sample_interval;
values++;
}
return (TRUE);
return TRUE;
}
static GstInterpolateMethod interpolate_none = {
@ -181,31 +211,41 @@ interpolate_trigger_get (GstControlledProperty * prop, GstClockTime timestamp)
GstControlPoint *cp;
/* check if there is a value at the registered timestamp */
for (node = prop->values; node; node = g_list_next (node)) {
if ((node =
gst_controlled_property_find_control_point_node (prop, timestamp))) {
cp = node->data;
if (timestamp == cp->timestamp) {
return (&cp->value);
return &cp->value;
}
}
return (&prop->default_value);
return &prop->default_value;
}
#define interpolate_trigger_get_boolean interpolate_trigger_get
#define DEFINE_TRIGGER_GET(type) \
static gboolean \
interpolate_trigger_get_##type##_value_array (GstControlledProperty * prop, \
GstClockTime timestamp, GstValueArray * value_array) \
static GValue * \
interpolate_trigger_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
{ \
gint i; \
GstClockTime ts = timestamp; \
g##type *values = (g##type *) value_array->values; \
GList *node; \
GstControlPoint *cp; \
\
for(i = 0; i < value_array->nbsamples; i++) { \
*values = g_value_get_##type (interpolate_trigger_get (prop,ts)); \
ts += value_array->sample_interval; \
values++; \
/* check if there is a value at the registered timestamp */ \
if ((node = \
gst_controlled_property_find_control_point_node (prop, timestamp))) { \
cp = node->data; \
if (timestamp == cp->timestamp) { \
g##type ret = g_value_get_##type (&cp->value); \
if (g_value_get_##type (&prop->min_value) > ret) \
return &prop->min_value; \
else if (g_value_get_##type (&prop->max_value) < ret) \
return &prop->max_value; \
return &cp->value; \
} \
} \
return (TRUE); \
\
return &prop->default_value; \
}
DEFINE_TRIGGER_GET (int);
@ -216,7 +256,33 @@ DEFINE_TRIGGER_GET (ulong);
DEFINE_TRIGGER_GET (float);
DEFINE_TRIGGER_GET (double);
DEFINE_TRIGGER_GET (boolean);
#define DEFINE_TRIGGER_GET_VALUE_ARRAY(type) \
static gboolean \
interpolate_trigger_get_##type##_value_array (GstControlledProperty * prop, \
GstClockTime timestamp, GstValueArray * value_array) \
{ \
gint i; \
GstClockTime ts = timestamp; \
g##type *values = (g##type *) value_array->values; \
\
for(i = 0; i < value_array->nbsamples; i++) { \
*values = g_value_get_##type (interpolate_trigger_get_##type (prop,ts)); \
ts += value_array->sample_interval; \
values++; \
} \
return TRUE; \
}
DEFINE_TRIGGER_GET_VALUE_ARRAY (int);
DEFINE_TRIGGER_GET_VALUE_ARRAY (uint);
DEFINE_TRIGGER_GET_VALUE_ARRAY (long);
DEFINE_TRIGGER_GET_VALUE_ARRAY (ulong);
DEFINE_TRIGGER_GET_VALUE_ARRAY (float);
DEFINE_TRIGGER_GET_VALUE_ARRAY (double);
DEFINE_TRIGGER_GET_VALUE_ARRAY (boolean);
static gboolean
interpolate_trigger_get_enum_value_array (GstControlledProperty * prop,
@ -231,7 +297,7 @@ interpolate_trigger_get_enum_value_array (GstControlledProperty * prop,
ts += value_array->sample_interval;
values++;
}
return (TRUE);
return TRUE;
}
static gboolean
@ -247,7 +313,7 @@ interpolate_trigger_get_string_value_array (GstControlledProperty * prop,
ts += value_array->sample_interval;
values++;
}
return (TRUE);
return TRUE;
}
static GstInterpolateMethod interpolate_trigger = {
@ -279,6 +345,7 @@ static g##type \
_interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
{ \
GList *node; \
g##type ret; \
\
if ((node = gst_controlled_property_find_control_point_node (prop, timestamp))) { \
GstControlPoint *cp1, *cp2; \
@ -294,20 +361,23 @@ _interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime times
value2 = g_value_get_##type (&cp2->value); \
slope = (gdouble) (value2 - value1) / gst_guint64_to_gdouble (cp2->timestamp - cp1->timestamp); \
\
return ((g##type) (value1 + gst_guint64_to_gdouble (timestamp - cp1->timestamp) * slope)); \
ret = (g##type) (value1 + gst_guint64_to_gdouble (timestamp - cp1->timestamp) * slope); \
} \
else { \
return (g_value_get_##type (&cp1->value)); \
ret = g_value_get_##type (&cp1->value); \
} \
} else { \
ret = g_value_get_##type (&prop->default_value); \
} \
return (g_value_get_##type (&prop->default_value)); \
ret = CLAMP (ret, g_value_get_##type (&prop->min_value), g_value_get_##type (&prop->max_value)); \
return ret; \
} \
\
static GValue * \
interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
{ \
g_value_set_##type (&prop->result_value,_interpolate_linear_get_##type (prop,timestamp)); \
return (&prop->result_value); \
return &prop->result_value; \
} \
\
static gboolean \
@ -323,7 +393,7 @@ interpolate_linear_get_##type##_value_array (GstControlledProperty * prop, \
ts += value_array->sample_interval; \
values++; \
} \
return (TRUE); \
return TRUE; \
}
DEFINE_LINEAR_GET (int);
@ -459,6 +529,7 @@ static g##type \
_interpolate_cubic_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
{ \
GList *node; \
g##type ret; \
\
if (prop->nvalues <= 2) \
return _interpolate_linear_get_##type (prop, timestamp); \
@ -475,7 +546,7 @@ _interpolate_cubic_get_##type (GstControlledProperty * prop, GstClockTime timest
if ((node = g_list_next (node))) { \
gdouble diff1, diff2; \
g##type value1,value2; \
gdouble ret; \
gdouble out; \
\
cp2 = node->data; \
\
@ -485,24 +556,27 @@ _interpolate_cubic_get_##type (GstControlledProperty * prop, GstClockTime timest
diff1 = gst_guint64_to_gdouble (timestamp - cp1->timestamp); \
diff2 = gst_guint64_to_gdouble (cp2->timestamp - timestamp); \
\
ret = (cp2->cache.cubic.z * diff1 * diff1 * diff1 + cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h; \
ret += (value2 / cp1->cache.cubic.h - cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1; \
ret += (value1 / cp1->cache.cubic.h - cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2; \
out = (cp2->cache.cubic.z * diff1 * diff1 * diff1 + cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h; \
out += (value2 / cp1->cache.cubic.h - cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1; \
out += (value1 / cp1->cache.cubic.h - cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2; \
\
return (g##type) ret; \
ret = (g##type) out; \
} \
else { \
return (g_value_get_##type (&cp1->value)); \
ret = g_value_get_##type (&cp1->value); \
} \
} else {\
ret = g_value_get_##type (&prop->default_value); \
} \
return (g_value_get_##type (&prop->default_value)); \
ret = CLAMP (ret, g_value_get_##type (&prop->min_value), g_value_get_##type (&prop->max_value)); \
return ret; \
} \
\
static GValue * \
interpolate_cubic_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
{ \
g_value_set_##type (&prop->result_value,_interpolate_cubic_get_##type (prop,timestamp)); \
return (&prop->result_value); \
return &prop->result_value; \
} \
\
static gboolean \
@ -518,7 +592,7 @@ interpolate_cubic_get_##type##_value_array (GstControlledProperty * prop, \
ts += value_array->sample_interval; \
values++; \
} \
return (TRUE); \
return TRUE; \
}
DEFINE_CUBIC_GET (int);

View file

@ -1076,6 +1076,63 @@ GST_START_TEST (controller_interpolate_linear_value_array)
GST_END_TEST;
/* test if values below minimum and above maximum are clipped */
GST_START_TEST (controller_linear_invalid_values)
{
GstController *ctrl;
GstElement *elem;
gboolean res;
GValue val_float = { 0, };
gst_controller_init (NULL, NULL);
elem = gst_element_factory_make ("testmonosource", "test_source");
/* that property should exist and should be controllable */
ctrl = gst_controller_new (G_OBJECT (elem), "float", NULL);
fail_unless (ctrl != NULL, NULL);
/* set interpolation mode */
fail_unless (gst_controller_set_interpolation_mode (ctrl, "float",
GST_INTERPOLATE_LINEAR));
/* set control values */
g_value_init (&val_float, G_TYPE_FLOAT);
g_value_set_float (&val_float, 200.0);
res = gst_controller_set (ctrl, "float", 0 * GST_SECOND, &val_float);
fail_unless (res, NULL);
g_value_set_float (&val_float, -200.0);
res = gst_controller_set (ctrl, "float", 4 * GST_SECOND, &val_float);
fail_unless (res, NULL);
/* now pull in values for some timestamps and see if clipping works */
/* 200.0 */
gst_controller_sync_values (ctrl, 0 * GST_SECOND);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_float == 100.0, NULL);
/* 100.0 */
gst_controller_sync_values (ctrl, 1 * GST_SECOND);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_float == 100.0, NULL);
/* 50.0 */
gst_controller_sync_values (ctrl, 1 * GST_SECOND + 500 * GST_MSECOND);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_float == 50.0, NULL);
/* 0.0 */
gst_controller_sync_values (ctrl, 2 * GST_SECOND);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_float == 0.0, NULL);
/* -100.0 */
gst_controller_sync_values (ctrl, 3 * GST_SECOND);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_float == 0.0, NULL);
/* -200.0 */
gst_controller_sync_values (ctrl, 4 * GST_SECOND);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_float == 0.0, NULL);
GST_INFO ("controller->ref_count=%d", G_OBJECT (ctrl)->ref_count);
g_object_unref (ctrl);
gst_object_unref (elem);
}
GST_END_TEST;
static Suite *
gst_controller_suite (void)
{
@ -1106,6 +1163,7 @@ gst_controller_suite (void)
tcase_add_test (tc, controller_helper_any_gobject);
tcase_add_test (tc, controller_misc);
tcase_add_test (tc, controller_interpolate_linear_value_array);
tcase_add_test (tc, controller_linear_invalid_values);
return s;
}