value: add structure intersect/union/is_subset/fixate implementations

Allows proper usage of structures in structures in caps.  Subtraction
is not implemented due to complications with empty fields representing
all possible values.

The only implementation that doesn't delegate to the already existing
GstStructure functions is the union function.

https://bugzilla.gnome.org/show_bug.cgi?id=775796
This commit is contained in:
Matthew Waters 2016-12-08 21:01:52 +11:00
parent 865247d32a
commit ad40c5cd36
2 changed files with 311 additions and 4 deletions

View file

@ -327,7 +327,8 @@ gst_type_is_fixed (GType type)
/* our fundamental types that are certainly not fixed */
if (type == GST_TYPE_INT_RANGE || type == GST_TYPE_DOUBLE_RANGE ||
type == GST_TYPE_INT64_RANGE ||
type == GST_TYPE_LIST || type == GST_TYPE_FRACTION_RANGE) {
type == GST_TYPE_LIST || type == GST_TYPE_FRACTION_RANGE ||
type == GST_TYPE_STRUCTURE) {
return FALSE;
}
/* other (boxed) types that are fixed */
@ -3550,6 +3551,21 @@ gst_value_is_subset_flagset_flagset (const GValue * value1,
return TRUE;
}
static gboolean
gst_value_is_subset_structure_structure (const GValue * value1,
const GValue * value2)
{
const GstStructure *s1, *s2;
g_return_val_if_fail (GST_VALUE_HOLDS_STRUCTURE (value1), FALSE);
g_return_val_if_fail (GST_VALUE_HOLDS_STRUCTURE (value2), FALSE);
s1 = gst_value_get_structure (value1);
s2 = gst_value_get_structure (value2);
return gst_structure_is_subset (s1, s2);
}
/**
* gst_value_is_subset:
* @value1: a #GValue
@ -3574,6 +3590,9 @@ gst_value_is_subset (const GValue * value1, const GValue * value2)
} else if (GST_VALUE_HOLDS_FLAG_SET (value1) &&
GST_VALUE_HOLDS_FLAG_SET (value2)) {
return gst_value_is_subset_flagset_flagset (value1, value2);
} else if (GST_VALUE_HOLDS_STRUCTURE (value1)
&& GST_VALUE_HOLDS_STRUCTURE (value2)) {
return gst_value_is_subset_structure_structure (value1, value2);
}
/*
@ -3773,6 +3792,81 @@ gst_value_union_flagset_flagset (GValue * dest, const GValue * src1,
return TRUE;
}
/* iterating over the result taking the union with the other structure's value */
static gboolean
structure_field_union_into (GQuark field_id, GValue * val, gpointer user_data)
{
GstStructure *other = user_data;
const GValue *other_value;
GValue res_value = G_VALUE_INIT;
other_value = gst_structure_id_get_value (other, field_id);
/* no value in the other struct, just keep this value */
if (!other_value)
return TRUE;
if (!gst_value_union (&res_value, val, other_value))
return FALSE;
g_value_unset (val);
gst_value_init_and_copy (val, &res_value);
return TRUE;
}
/* iterating over the other source structure adding missing values */
static gboolean
structure_field_union_from (GQuark field_id, const GValue * other_val,
gpointer user_data)
{
GstStructure *result = user_data;
const GValue *result_value;
result_value = gst_structure_id_get_value (result, field_id);
if (!result_value)
gst_structure_id_set_value (result, field_id, other_val);
return TRUE;
}
static gboolean
gst_value_union_structure_structure (GValue * dest, const GValue * src1,
const GValue * src2)
{
const GstStructure *s1, *s2;
GstStructure *result;
gboolean ret;
g_return_val_if_fail (GST_VALUE_HOLDS_STRUCTURE (src1), FALSE);
g_return_val_if_fail (GST_VALUE_HOLDS_STRUCTURE (src2), FALSE);
s1 = gst_value_get_structure (src1);
s2 = gst_value_get_structure (src2);
/* Can't join two structures with different names into a single structure */
if (!gst_structure_has_name (s1, gst_structure_get_name (s2))) {
gst_value_list_concat (dest, src1, src2);
return TRUE;
}
result = gst_structure_copy (s1);
ret =
gst_structure_map_in_place (result, structure_field_union_into,
(gpointer) s2);
if (!ret)
goto out;
ret =
gst_structure_foreach (s2, structure_field_union_from, (gpointer) result);
if (ret) {
g_value_init (dest, GST_TYPE_STRUCTURE);
gst_value_set_structure (dest, result);
}
out:
gst_structure_free (result);
return ret;
}
/****************
* intersection *
****************/
@ -4152,6 +4246,29 @@ gst_value_intersect_flagset_flagset (GValue * dest,
return TRUE;
}
static gboolean
gst_value_intersect_structure_structure (GValue * dest,
const GValue * src1, const GValue * src2)
{
const GstStructure *s1, *s2;
GstStructure *d1;
s1 = gst_value_get_structure (src1);
s2 = gst_value_get_structure (src2);
d1 = gst_structure_intersect (s1, s2);
if (!d1)
return FALSE;
if (dest) {
g_value_init (dest, GST_TYPE_STRUCTURE);
gst_value_set_structure (dest, d1);
}
gst_structure_free (d1);
return TRUE;
}
/***************
* subtraction *
***************/
@ -4696,7 +4813,6 @@ gst_value_subtract_fraction_range_fraction_range (GValue * dest,
return TRUE;
}
/**************
* comparison *
**************/
@ -5305,6 +5421,8 @@ gst_value_can_subtract (const GValue * minuend, const GValue * subtrahend)
/* special cases */
if (mtype == GST_TYPE_LIST || stype == GST_TYPE_LIST)
return TRUE;
if (mtype == GST_TYPE_STRUCTURE || stype == GST_TYPE_STRUCTURE)
return FALSE;
len = gst_value_subtract_funcs->len;
for (i = 0; i < len; i++) {
@ -5491,6 +5609,13 @@ gst_value_deserialize (GValue * dest, const gchar * src)
return FALSE;
}
static gboolean
structure_field_is_fixed (GQuark field_id, const GValue * val,
gpointer user_data)
{
return gst_value_is_fixed (val);
}
/**
* gst_value_is_fixed:
* @value: the #GValue to check
@ -5532,6 +5657,9 @@ gst_value_is_fixed (const GValue * value)
} else if (GST_VALUE_HOLDS_FLAG_SET (value)) {
/* Flagsets are only fixed if there are no 'don't care' bits */
return (gst_value_get_flagset_mask (value) == GST_FLAG_SET_MASK_EXACT);
} else if (GST_VALUE_HOLDS_STRUCTURE (value)) {
return gst_structure_foreach (gst_value_get_structure (value),
structure_field_is_fixed, NULL);
}
return gst_type_is_fixed (type);
}
@ -5607,6 +5735,19 @@ gst_value_fixate (GValue * dest, const GValue * src)
g_value_init (dest, G_VALUE_TYPE (src));
gst_value_set_flagset (dest, flags, GST_FLAG_SET_MASK_EXACT);
return TRUE;
} else if (GST_VALUE_HOLDS_STRUCTURE (src)) {
const GstStructure *str = (GstStructure *) gst_value_get_structure (src);
GstStructure *kid;
if (!str)
return FALSE;
kid = gst_structure_copy (str);
gst_structure_fixate (kid);
g_value_init (dest, GST_TYPE_STRUCTURE);
gst_value_set_structure (dest, kid);
gst_structure_free (kid);
return TRUE;
} else {
return FALSE;
}
@ -6771,9 +6912,10 @@ G_STMT_START { \
/* These initial sizes are used for the tables
* below, and save a couple of reallocs at startup */
static const gint GST_VALUE_TABLE_DEFAULT_SIZE = 35;
static const gint GST_VALUE_UNION_TABLE_DEFAULT_SIZE = 3;
static const gint GST_VALUE_INTERSECT_TABLE_DEFAULT_SIZE = 10;
static const gint GST_VALUE_UNION_TABLE_DEFAULT_SIZE = 4;
static const gint GST_VALUE_INTERSECT_TABLE_DEFAULT_SIZE = 11;
static const gint GST_VALUE_SUBTRACT_TABLE_DEFAULT_SIZE = 12;
void
@ -6905,6 +7047,8 @@ _priv_gst_value_initialize (void)
gst_value_intersect_fraction_range_fraction_range);
gst_value_register_intersect_func (GST_TYPE_FLAG_SET, GST_TYPE_FLAG_SET,
gst_value_intersect_flagset_flagset);
gst_value_register_intersect_func (GST_TYPE_STRUCTURE, GST_TYPE_STRUCTURE,
gst_value_intersect_structure_structure);
gst_value_register_subtract_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
gst_value_subtract_int_int_range);
@ -6945,6 +7089,8 @@ _priv_gst_value_initialize (void)
gst_value_union_int_range_int_range);
gst_value_register_union_func (GST_TYPE_FLAG_SET, GST_TYPE_FLAG_SET,
gst_value_union_flagset_flagset);
gst_value_register_union_func (GST_TYPE_STRUCTURE, GST_TYPE_STRUCTURE,
gst_value_union_structure_structure);
#if GST_VERSION_NANO == 1
/* If building from git master, check starting array sizes matched actual size

View file

@ -3115,6 +3115,164 @@ GST_START_TEST (test_stepped_int_range_ops)
GST_END_TEST;
GST_START_TEST (test_structure_basic)
{
GstStructure *s1, *s2;
GValue v1 = G_VALUE_INIT, v2 = G_VALUE_INIT;
/* sanity test */
s1 = gst_structure_from_string ("foo,bar=1", NULL);
g_value_init (&v1, GST_TYPE_STRUCTURE);
gst_value_set_structure (&v1, s1);
fail_unless (gst_structure_is_equal (s1, gst_value_get_structure (&v1)));
s2 = gst_structure_copy (s1);
g_value_init (&v2, GST_TYPE_STRUCTURE);
gst_value_set_structure (&v2, s2);
/* can do everything but subtract */
fail_unless (gst_value_can_compare (&v1, &v2));
fail_unless (gst_value_can_intersect (&v1, &v2));
fail_unless (!gst_value_can_subtract (&v1, &v2));
fail_unless (gst_value_can_union (&v1, &v2));
gst_structure_free (s1);
gst_structure_free (s2);
g_value_unset (&v1);
g_value_unset (&v2);
}
GST_END_TEST;
GST_START_TEST (test_structure_single_ops)
{
static const struct
{
const gchar *str1;
gboolean is_fixed;
gboolean can_fixate;
} single_struct[] = {
{
"foo,bar=(int)1", TRUE, TRUE}, {
"foo,bar=(int)[1,2]", FALSE, TRUE},};
gint i;
for (i = 0; i < G_N_ELEMENTS (single_struct); i++) {
GstStructure *s1 = gst_structure_from_string (single_struct[i].str1, NULL);
GValue v1 = G_VALUE_INIT;
GValue v2 = G_VALUE_INIT;
fail_unless (s1 != NULL);
GST_DEBUG ("checking structure %" GST_PTR_FORMAT, s1);
g_value_init (&v1, GST_TYPE_STRUCTURE);
gst_value_set_structure (&v1, s1);
fail_unless (gst_value_is_fixed (&v1) == single_struct[i].is_fixed);
fail_unless (gst_value_fixate (&v2, &v1) == single_struct[i].can_fixate);
if (single_struct[i].can_fixate)
g_value_unset (&v2);
g_value_unset (&v1);
gst_structure_free (s1);
}
}
GST_END_TEST;
GST_START_TEST (test_structure_ops)
{
struct
{
const gchar *str1;
const gchar *str2;
const gchar *op;
gint ret;
GType str_type;
const gchar *str_result;
} comparisons[] = {
/* *INDENT-OFF* */
{"foo,bar=(int)1", "foo,bar=(int)1", "compare", GST_VALUE_EQUAL, 0, NULL},
{"foo,bar=(int)1", "foo,bar=(int)1", "is_subset", TRUE, 0, NULL},
{"foo,bar=(int)1", "foo,bar=(int)1", "intersect", TRUE, GST_TYPE_STRUCTURE, "foo,bar=(int)1"},
{"foo,bar=(int)1", "foo,bar=(int)1", "union", TRUE, GST_TYPE_STRUCTURE, "foo,bar=(int)1"},
{"foo,bar=(int)[1,2]", "foo,bar=(int)1", "compare", GST_VALUE_UNORDERED, 0, NULL},
{"foo,bar=(int)[1,2]", "foo,bar=(int)1", "is_subset", FALSE, 0, NULL},
{"foo,bar=(int)[1,2]", "foo,bar=(int)1", "intersect", TRUE, GST_TYPE_STRUCTURE, "foo,bar=(int)1"},
{"foo,bar=(int)[1,2]", "foo,bar=(int)1", "union", TRUE, GST_TYPE_STRUCTURE, "foo,bar=(int)[1,2]"},
{"foo,bar=(int)1", "foo,bar=(int)[1,2]", "compare", GST_VALUE_UNORDERED, 0, NULL},
{"foo,bar=(int)1", "foo,bar=(int)[1,2]", "is_subset", TRUE, 0, NULL},
{"foo,bar=(int)1", "foo,bar=(int)[1,2]", "intersect", TRUE, GST_TYPE_STRUCTURE, "foo,bar=(int)1"},
{"foo,bar=(int)1", "foo,bar=(int)[1,2]", "union", TRUE, GST_TYPE_STRUCTURE, "foo,bar=(int)[1,2]"},
{"foo,bar=(int)1", "foo,bar=(int)2", "compare", GST_VALUE_UNORDERED, 0, NULL},
{"foo,bar=(int)1", "foo,bar=(int)2", "is_subset", FALSE, 0, NULL},
{"foo,bar=(int)1", "foo,bar=(int)2", "intersect", FALSE, 0, NULL},
{"foo,bar=(int)1", "foo,bar=(int)2", "union", TRUE, GST_TYPE_STRUCTURE, "foo,bar=(int)[1,2]"},
{"foo,bar=(int)1", "baz,bar=(int)1", "compare", GST_VALUE_UNORDERED, 0, NULL},
{"foo,bar=(int)1", "baz,bar=(int)1", "is_subset", FALSE, 0, NULL},
{"foo,bar=(int)1", "baz,bar=(int)1", "intersect", FALSE, 0, NULL},
#if 0
/* deserializing lists is not implemented (but this should still work!) */
{"foo,bar=(int)1", "baz,bar=(int)1", "union", TRUE, G_TYPE_LIST, "{foo,bar=(int)1;, baz,bar=(int)1;}"},
#endif
/* *INDENT-ON* */
};
gint i;
for (i = 0; i < G_N_ELEMENTS (comparisons); i++) {
GstStructure *s1 = gst_structure_from_string (comparisons[i].str1, NULL);
GstStructure *s2 = gst_structure_from_string (comparisons[i].str2, NULL);
GValue v1 = G_VALUE_INIT, v2 = G_VALUE_INIT, v3 = G_VALUE_INIT;
fail_unless (s1 != NULL);
fail_unless (s2 != NULL);
GST_DEBUG ("checking %s with structure1 %" GST_PTR_FORMAT " structure2 %"
GST_PTR_FORMAT " is %d, %s", comparisons[i].op, s1, s2,
comparisons[i].ret, comparisons[i].str_result);
g_value_init (&v1, GST_TYPE_STRUCTURE);
gst_value_set_structure (&v1, s1);
g_value_init (&v2, GST_TYPE_STRUCTURE);
gst_value_set_structure (&v2, s2);
if (g_strcmp0 (comparisons[i].op, "compare") == 0) {
fail_unless (gst_value_compare (&v1, &v2) == comparisons[i].ret);
} else if (g_strcmp0 (comparisons[i].op, "is_subset") == 0) {
fail_unless (gst_value_is_subset (&v1, &v2) == comparisons[i].ret);
} else {
if (g_strcmp0 (comparisons[i].op, "intersect") == 0) {
fail_unless (gst_value_intersect (&v3, &v1, &v2) == comparisons[i].ret);
} else if (g_strcmp0 (comparisons[i].op, "union") == 0) {
fail_unless (gst_value_union (&v3, &v1, &v2) == comparisons[i].ret);
}
if (comparisons[i].ret) {
GValue result = G_VALUE_INIT;
gchar *str;
str = gst_value_serialize (&v3);
GST_LOG ("result %s", str);
g_free (str);
g_value_init (&result, comparisons[i].str_type);
fail_unless (gst_value_deserialize (&result,
comparisons[i].str_result));
fail_unless (gst_value_compare (&result, &v3) == GST_VALUE_EQUAL);
g_value_unset (&v3);
g_value_unset (&result);
}
}
gst_structure_free (s1);
gst_structure_free (s2);
g_value_unset (&v1);
g_value_unset (&v2);
}
}
GST_END_TEST;
static Suite *
gst_value_suite (void)
{
@ -3160,6 +3318,9 @@ gst_value_suite (void)
tcase_add_test (tc_chain, test_stepped_int_range_parsing);
tcase_add_test (tc_chain, test_stepped_int_range_ops);
tcase_add_test (tc_chain, test_flagset);
tcase_add_test (tc_chain, test_structure_basic);
tcase_add_test (tc_chain, test_structure_single_ops);
tcase_add_test (tc_chain, test_structure_ops);
return s;
}