diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index dba70f5522..204296e080 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -3285,6 +3285,15 @@ GST_TYPE_BITMASK gst_value_set_bitmask gst_value_get_bitmask + +GST_VALUE_HOLDS_FLAG_SET +GST_TYPE_FLAG_SET +gst_structure_get_flagset +gst_value_get_flagset_flags +gst_value_get_flagset_mask +gst_value_set_flagset +GST_FLAG_SET_MASK_EXACT + GST_VALUE_HOLDS_INT64_RANGE GST_TYPE_INT64_RANGE @@ -3408,6 +3417,7 @@ gst_int64_range_get_type gst_value_array_get_type gst_value_list_get_type gst_bitmask_get_type +gst_flagset_get_type
diff --git a/gst/gststructure.c b/gst/gststructure.c index 8b0b54b929..90e7cbe123 100644 --- a/gst/gststructure.c +++ b/gst/gststructure.c @@ -1720,6 +1720,44 @@ gst_structure_get_fraction (const GstStructure * structure, return TRUE; } +/** + * gst_structure_get_flagset: + * @structure: a #GstStructure + * @fieldname: the name of a field + * @value_flags: (out) (allow-none): a pointer to a guint for the flags field + * @value_mask: (out) (allow-none): a pointer to a guint for the mask field + * + * Read the GstFlagSet flags and mask out of the structure into the + * provided pointers. + * + * Returns: %TRUE if the values could be set correctly. If there was no field + * with @fieldname or the existing field did not contain a GstFlagSet, this + * function returns %FALSE. + * + * Since: 1.6 + */ +gboolean +gst_structure_get_flagset (const GstStructure * structure, + const gchar * fieldname, guint * value_flags, guint * value_mask) +{ + GstStructureField *field; + + g_return_val_if_fail (structure != NULL, FALSE); + g_return_val_if_fail (fieldname != NULL, FALSE); + + field = gst_structure_get_field (structure, fieldname); + + if (field == NULL || !GST_VALUE_HOLDS_FLAG_SET (&field->value)) + return FALSE; + + if (value_flags) + *value_flags = gst_value_get_flagset_flags (&field->value); + if (value_mask) + *value_mask = gst_value_get_flagset_mask (&field->value); + + return TRUE; +} + typedef struct _GstStructureAbbreviation { const gchar *type_name; @@ -2296,8 +2334,8 @@ gst_structure_parse_value (gchar * str, if (G_UNLIKELY (type == G_TYPE_INVALID)) { GType try_types[] = - { G_TYPE_INT, G_TYPE_DOUBLE, GST_TYPE_FRACTION, G_TYPE_BOOLEAN, - G_TYPE_STRING + { G_TYPE_INT, G_TYPE_DOUBLE, GST_TYPE_FRACTION, GST_TYPE_FLAG_SET, + G_TYPE_BOOLEAN, G_TYPE_STRING }; int i; diff --git a/gst/gststructure.h b/gst/gststructure.h index 014ef37e53..a2b7926050 100644 --- a/gst/gststructure.h +++ b/gst/gststructure.h @@ -288,6 +288,11 @@ gboolean gst_structure_get_fraction (const GstStructure * gint * value_numerator, gint * value_denominator); +gboolean gst_structure_get_flagset (const GstStructure * structure, + const gchar * fieldname, + guint * value_flags, + guint * value_mask); + gchar * gst_structure_to_string (const GstStructure * structure) G_GNUC_MALLOC; GstStructure * gst_structure_from_string (const gchar * string, diff --git a/gst/gstvalue.c b/gst/gstvalue.c index dea3ba4e18..0c7c3c3fa3 100644 --- a/gst/gstvalue.c +++ b/gst/gstvalue.c @@ -116,6 +116,14 @@ struct _GstValueSubtractInfo GstValueSubtractFunc func; }; +struct _GstFlagSetClass +{ + GTypeClass parent; + GType flags_type; /* Type of the GFlags this flagset carries (can be 0) */ +}; + +typedef struct _GstFlagSetClass GstFlagSetClass; + #define FUNDAMENTAL_TYPE_ID_MAX \ (G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) #define FUNDAMENTAL_TYPE_ID(type) \ @@ -871,7 +879,7 @@ gst_value_compare_value_list (const GValue * value1, const GValue * value2) continue; v2 = &g_array_index (array2, GValue, j); if (gst_value_compare_with_func (v1, v2, compare) == GST_VALUE_EQUAL) { - /* mark item as removed now that we found it in array2 and + /* mark item as removed now that we found it in array2 and * decrement the number of remaining items in array2. */ removed[j] = 1; to_remove--; @@ -3158,7 +3166,7 @@ gst_value_deserialize_enum (GValue * dest, const gchar * s) /* we just compare the value here */ static gint -gst_value_compare_flags (const GValue * value1, const GValue * value2) +gst_value_compare_gflags (const GValue * value1, const GValue * value2) { guint fl1, fl2; GFlagsClass *klass1 = @@ -3182,7 +3190,7 @@ gst_value_compare_flags (const GValue * value1, const GValue * value2) /* the different flags are serialized separated with a + */ static gchar * -gst_value_serialize_flags (const GValue * value) +gst_value_serialize_gflags (const GValue * value) { guint flags; GFlagsValue *fl; @@ -3223,46 +3231,96 @@ gst_value_serialize_flags (const GValue * value) } static gboolean -gst_value_deserialize_flags (GValue * dest, const gchar * s) +gst_value_gflags_str_to_flags (GFlagsClass * klass, const gchar * s, + guint * out_flags, guint * out_mask) { GFlagsValue *fl; - gchar *endptr = NULL; - GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (dest)); - gchar **split; - guint flags; - gint i; + gchar delimiter; + const gchar *pos = NULL; + const gchar *next; + gchar *cur_str, *endptr; + + guint flags = 0; + guint mask = 0; + guint val; g_return_val_if_fail (klass, FALSE); - /* split into parts delimited with + */ - split = g_strsplit (s, "+", 0); + /* split into parts delimited with + or / and + * compose the set of flags and mask. */ + pos = s; - flags = 0; - i = 0; - /* loop over each part */ - while (split[i]) { - if (!(fl = g_flags_get_value_by_name (klass, split[i]))) { - if (!(fl = g_flags_get_value_by_nick (klass, split[i]))) { - gint val = strtol (split[i], &endptr, 0); + if (*pos == '\0') + goto done; /* Empty string, nothing to do */ - /* just or numeric value */ - if (endptr && *endptr == '\0') { - flags |= val; - } - } - } - if (fl) { - flags |= fl->value; - } - i++; + /* As a special case if the first char isn't a delimiter, assume + * it's a '+' - for GFlags strings, which don't start with a + * delimiter, while GFlagSet always will */ + if (*pos == '/' || *pos == '+') { + delimiter = *pos; + pos++; + } else { + delimiter = '+'; } - g_strfreev (split); - g_type_class_unref (klass); - g_value_set_flags (dest, flags); + + do { + /* Find the next delimiter */ + next = pos; + while (*next != '\0' && *next != '+' && *next != '/') + next++; + cur_str = g_strndup (pos, next - pos); + + if ((fl = g_flags_get_value_by_name (klass, cur_str))) + val = fl->value; + else if ((fl = g_flags_get_value_by_nick (klass, cur_str))) + val = fl->value; + else { + val = strtoul (cur_str, &endptr, 0); + /* direct numeric value */ + if (endptr == NULL || *endptr != '\0') + val = 0; /* Invalid numeric, ignore it */ + } + g_free (cur_str); + + if (val) { + mask |= val; + if (delimiter == '+') + flags |= val; + } + + /* Advance to the next delimiter */ + pos = next; + delimiter = *pos; + pos++; + } while (delimiter != '\0'); + +done: + if (out_flags) + *out_flags = flags; + if (out_mask) + *out_mask = mask; return TRUE; } + +static gboolean +gst_value_deserialize_gflags (GValue * dest, const gchar * s) +{ + GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (dest)); + gboolean res = FALSE; + guint flags = 0; + + if (gst_value_gflags_str_to_flags (klass, s, &flags, NULL)) { + g_value_set_flags (dest, flags); + res = TRUE; + } + + g_type_class_unref (klass); + + return res; +} + /**************** * subset * ****************/ @@ -3515,6 +3573,40 @@ gst_value_union_int_range_int_range (GValue * dest, const GValue * src1, return FALSE; } +static gboolean +gst_value_union_flagset_flagset (GValue * dest, const GValue * src1, + const GValue * src2) +{ + /* We can union 2 flag sets where they do not disagree on + * required (masked) flag bits */ + guint64 f1, f2; + guint64 m1, m2; + + g_return_val_if_fail (GST_VALUE_HOLDS_FLAG_SET (src1), FALSE); + g_return_val_if_fail (GST_VALUE_HOLDS_FLAG_SET (src2), FALSE); + + f1 = src1->data[0].v_uint; + f2 = src2->data[0].v_uint; + + m1 = src1->data[1].v_uint; + m2 = src2->data[1].v_uint; + + /* Can't union if masked bits disagree */ + if ((f1 & (m1 & m2)) != (f2 & (m1 & m2))) + return FALSE; + + if (dest) { + g_value_init (dest, GST_TYPE_FLAG_SET); + /* Copy masked bits from src2 to src1 */ + f1 &= ~m2; + f1 |= (f2 & m2); + m1 |= m2; + gst_value_set_flagset (dest, f1, m1); + } + + return TRUE; +} + /**************** * intersection * ****************/ @@ -3840,6 +3932,60 @@ gst_value_intersect_fraction_range_fraction_range (GValue * dest, return FALSE; } +/* Two flagsets intersect if the masked bits in both + * flagsets are exactly equal */ +static gboolean +gst_value_intersect_flagset_flagset (GValue * dest, + const GValue * src1, const GValue * src2) +{ + guint f1, f2; + guint m1, m2; + GType type1, type2, flagset_type; + + g_return_val_if_fail (GST_VALUE_HOLDS_FLAG_SET (src1), FALSE); + g_return_val_if_fail (GST_VALUE_HOLDS_FLAG_SET (src2), FALSE); + + f1 = src1->data[0].v_uint; + f2 = src2->data[0].v_uint; + + m1 = src1->data[1].v_uint; + m2 = src2->data[1].v_uint; + + /* Don't intersect if masked bits disagree */ + if ((f1 & (m1 & m2)) != (f2 & (m1 & m2))) + return FALSE; + + /* Allow intersection with the generic FlagSet type, on one + * side, but not 2 different subtypes - that makes no sense */ + type1 = G_VALUE_TYPE (src1); + type2 = G_VALUE_TYPE (src2); + flagset_type = GST_TYPE_FLAG_SET; + + if (type1 != type2 && type1 != flagset_type && type2 != flagset_type) + return FALSE; + + if (dest) { + GType dest_type; + + /* Prefer an output type that matches a sub-type, + * rather than the generic type */ + if (type1 != flagset_type) + dest_type = type1; + else + dest_type = type2; + + g_value_init (dest, dest_type); + + /* The compatible set is all the bits from src1 that it + * cares about and all the bits from src2 that it cares + * about. */ + dest->data[0].v_uint = (f1 & m1) | (f2 & m2); + dest->data[1].v_uint = m1 | m2; + } + + return TRUE; +} + /*************** * subtraction * ***************/ @@ -4623,7 +4769,7 @@ gst_value_compare (const GValue * value1, const GValue * value2) * * Compares @value1 and @value2 using the @compare function. Works like * gst_value_compare() but allows to save time determining the compare function - * a multiple times. + * a multiple times. * * Returns: comparison result */ @@ -4847,6 +4993,14 @@ gst_value_intersect (GValue * dest, const GValue * value1, return intersect_info->func (dest, value2, value1); } } + + /* Failed to find a direct intersection, check if these are + * GstFlagSet sub-types. */ + if (G_UNLIKELY (GST_VALUE_HOLDS_FLAG_SET (value1) && + GST_VALUE_HOLDS_FLAG_SET (value2))) { + return gst_value_intersect_flagset_flagset (dest, value1, value2); + } + return FALSE; } @@ -5195,6 +5349,9 @@ gst_value_is_fixed (const GValue * value) return FALSE; } return TRUE; + } 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); } return gst_type_is_fixed (type); } @@ -5260,6 +5417,16 @@ gst_value_fixate (GValue * dest, const GValue * src) g_value_unset (dest); return res; + } else if (GST_VALUE_HOLDS_FLAG_SET (src)) { + guint flags; + + if (gst_value_get_flagset_mask (src) == GST_FLAG_SET_MASK_EXACT) + return FALSE; /* Already fixed */ + + flags = gst_value_get_flagset_flags (src); + g_value_init (dest, G_VALUE_TYPE (src)); + gst_value_set_flagset (dest, flags, GST_FLAG_SET_MASK_EXACT); + return TRUE; } else { return FALSE; } @@ -5906,6 +6073,272 @@ gst_value_compare_bitmask (const GValue * value1, const GValue * value2) return GST_VALUE_UNORDERED; } +/************ + * flagset * + ************/ + +/* helper functions */ +static void +gst_value_init_flagset (GValue * value) +{ + value->data[0].v_uint = 0; + value->data[1].v_uint = 0; +} + +static void +gst_value_copy_flagset (const GValue * src_value, GValue * dest_value) +{ + dest_value->data[0].v_uint = src_value->data[0].v_uint; + dest_value->data[1].v_uint = src_value->data[1].v_uint; +} + +static gchar * +gst_value_collect_flagset (GValue * value, guint n_collect_values, + GTypeCValue * collect_values, guint collect_flags) +{ + if (n_collect_values != 2) + return g_strdup_printf ("not enough value locations for `%s' passed", + G_VALUE_TYPE_NAME (value)); + + gst_value_set_flagset (value, + (guint) collect_values[0].v_int, (guint) collect_values[1].v_int); + + return NULL; +} + +static gchar * +gst_value_lcopy_flagset (const GValue * value, guint n_collect_values, + GTypeCValue * collect_values, guint collect_flags) +{ + guint *flags = collect_values[0].v_pointer; + guint *mask = collect_values[1].v_pointer; + + *flags = value->data[0].v_uint; + *mask = value->data[1].v_uint; + + return NULL; +} + +/** + * gst_value_set_flagset: + * @value: a GValue initialized to #GST_TYPE_FLAGS + * @flags: The value of the flags set or unset + * @mask: The mask indicate which flags bits must match for comparisons + * + * Sets @value to the flags and mask values provided in @flags and @mask. + * The @flags value indicates the values of flags, the @mask represents + * which bits in the flag value have been set, and which are "don't care" + * + * Since: 1.6 + */ +void +gst_value_set_flagset (GValue * value, guint flags, guint mask) +{ + g_return_if_fail (GST_VALUE_HOLDS_FLAG_SET (value)); + + /* Normalise and only keep flags mentioned in the mask */ + value->data[0].v_uint = flags & mask; + value->data[1].v_uint = mask; +} + +/** + * gst_value_get_flagset_flags: + * @value: a GValue initialized to #GST_TYPE_FLAG_SET + * + * Retrieve the flags field of a GstFlagSet @value. + * + * Returns: the flags field of the flagset instance. + * + * Since: 1.6 + */ +guint +gst_value_get_flagset_flags (const GValue * value) +{ + g_return_val_if_fail (GST_VALUE_HOLDS_FLAG_SET (value), 0); + + return value->data[0].v_uint; +} + +/** + * gst_value_get_flagset_mask: + * @value: a GValue initialized to #GST_TYPE_FLAG_SET + * + * Retrieve the mask field of a GstFlagSet @value. + * + * Returns: the mask field of the flagset instance. + * + * Since: 1.6 + */ +guint +gst_value_get_flagset_mask (const GValue * value) +{ + g_return_val_if_fail (GST_VALUE_HOLDS_FLAG_SET (value), 1); + + return value->data[1].v_uint; +} + +static gchar * +gst_value_serialize_flagset (const GValue * value) +{ + guint flags = value->data[0].v_uint; + guint mask = value->data[1].v_uint; + GstFlagSetClass *set_klass = + (GstFlagSetClass *) g_type_class_ref (G_VALUE_TYPE (value)); + gchar *result; + + result = g_strdup_printf ("%x:%x", flags, mask); + + /* If this flag set class has an associated GFlags GType, and some + * bits in the mask, serialize the bits in human-readable form to + * aid debugging */ + if (mask && set_klass->flags_type) { + GFlagsClass *flags_klass = + (GFlagsClass *) (g_type_class_ref (set_klass->flags_type)); + GFlagsValue *fl; + gchar *tmp; + gboolean first = TRUE; + + g_return_val_if_fail (flags_klass, NULL); + + /* some bits in the mask are set, so serialize one by one, according + * to whether that bit is set or cleared in the flags value */ + while (mask) { + fl = g_flags_get_first_value (flags_klass, mask); + if (fl == NULL) { + /* No more bits match in the flags mask - time to stop */ + mask = 0; + break; + } + + tmp = g_strconcat (result, + first ? ":" : "", + (flags & fl->value) ? "+" : "/", fl->value_nick, NULL); + g_free (result); + result = tmp; + first = FALSE; + + /* clear flag */ + mask &= ~fl->value; + } + g_type_class_unref (flags_klass); + + } + g_type_class_unref (set_klass); + + return result; +} + +static gboolean +gst_value_deserialize_flagset (GValue * dest, const gchar * s) +{ + gboolean res = FALSE; + guint flags, mask; + gchar *cur, *next; + + if (G_UNLIKELY (s == NULL)) + return FALSE; + + if (G_UNLIKELY (dest == NULL || !GST_VALUE_HOLDS_FLAG_SET (dest))) + return FALSE; + + /* Flagset strings look like %x:%x - hex flags : hex bitmask, + * 32-bit each, or like a concatenated list of flag nicks, + * with either '+' or '/' in front. The first form + * may optionally be followed by ':' and a set of text flag descriptions + * for easier debugging */ + + /* Try and interpret as hex form first, as it's the most efficient */ + /* Read the flags first */ + flags = strtoul (s, &next, 16); + if (G_UNLIKELY ((flags == 0 && errno == EINVAL) || s == next)) + goto try_as_flags_string; + /* Next char should be a colon */ + if (next[0] == ':') + next++; + + /* Read the mask */ + cur = next; + mask = strtoul (cur, &next, 16); + if (G_UNLIKELY ((mask == 0 && errno == EINVAL) || cur == next)) + goto try_as_flags_string; + + /* Next char should be NULL terminator, or a ':' */ + if (G_UNLIKELY (next[0] != 0 && next[0] != ':')) + goto try_as_flags_string; + + res = TRUE; + +try_as_flags_string: + + if (!res) { + const gchar *set_class = g_type_name (G_VALUE_TYPE (dest)); + GFlagsClass *flags_klass = NULL; + const gchar *end; + + if (g_str_equal (set_class, "GstFlagSet")) + goto done; /* There's no hope to parse a generic flag set */ + + /* Flags class is the FlagSet class with 'Set' removed from the end */ + end = g_strrstr (set_class, "Set"); + + if (end != NULL) { + gchar *class_name = g_strndup (set_class, end - set_class); + GType flags_type = g_type_from_name (class_name); + + g_free (class_name); + + if (flags_type != 0) + flags_klass = g_type_class_ref (flags_type); + } + + if (flags_klass) { + res = gst_value_gflags_str_to_flags (flags_klass, s, &flags, &mask); + g_type_class_unref (flags_klass); + } + } + + if (res) + gst_value_set_flagset (dest, flags, mask); +done: + return res; + +} + +static void +gst_value_transform_flagset_string (const GValue * src_value, + GValue * dest_value) +{ + dest_value->data[0].v_pointer = gst_value_serialize_flagset (src_value); +} + +static void +gst_value_transform_string_flagset (const GValue * src_value, + GValue * dest_value) +{ + if (!gst_value_deserialize_flagset (dest_value, src_value->data[0].v_pointer)) { + /* If the deserialize fails, ensure we leave the flags in a + * valid, if incorrect, state */ + gst_value_set_flagset (dest_value, 0, 0); + } +} + +static gint +gst_value_compare_flagset (const GValue * value1, const GValue * value2) +{ + guint v1, v2; + guint m1, m2; + + v1 = value1->data[0].v_uint; + v2 = value2->data[0].v_uint; + + m1 = value1->data[1].v_uint; + m2 = value2->data[1].v_uint; + + if (v1 == v2 && m1 == m2) + return GST_VALUE_EQUAL; + + return GST_VALUE_UNORDERED; +} /*********************** * GstAllocationParams * @@ -5971,23 +6404,14 @@ gst_value_transform_object_string (const GValue * src_value, } static GTypeInfo _info = { - 0, - NULL, - NULL, - NULL, - NULL, - NULL, - 0, - 0, - NULL, - NULL, + 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, }; static GTypeFundamentalInfo _finfo = { 0 }; -#define FUNC_VALUE_GET_TYPE(type, name) \ +#define FUNC_VALUE_GET_TYPE_CLASSED(type, name, csize, flags) \ GType _gst_ ## type ## _type = 0; \ \ GType gst_ ## type ## _get_type (void) \ @@ -5996,26 +6420,29 @@ GType gst_ ## type ## _get_type (void) \ \ if (g_once_init_enter (&gst_ ## type ## _type)) { \ GType _type; \ + _info.class_size = csize; \ + _finfo.type_flags = flags; \ _info.value_table = & _gst_ ## type ## _value_table; \ _type = g_type_register_fundamental ( \ g_type_fundamental_next (), \ name, &_info, &_finfo, 0); \ - _gst_ ## type ## _type = _type; \ + _gst_ ## type ## _type = _type; \ g_once_init_leave(&gst_ ## type ## _type, _type); \ } \ \ return gst_ ## type ## _type; \ } +#define FUNC_VALUE_GET_TYPE(type, name) \ + FUNC_VALUE_GET_TYPE_CLASSED(type, name, 0, 0) + static const GTypeValueTable _gst_int_range_value_table = { gst_value_init_int_range, NULL, gst_value_copy_int_range, NULL, (char *) "ii", - gst_value_collect_int_range, - (char *) "pp", - gst_value_lcopy_int_range + gst_value_collect_int_range, (char *) "pp", gst_value_lcopy_int_range }; FUNC_VALUE_GET_TYPE (int_range, "GstIntRange"); @@ -6027,8 +6454,7 @@ static const GTypeValueTable _gst_int64_range_value_table = { NULL, (char *) "qq", gst_value_collect_int64_range, - (char *) "pp", - gst_value_lcopy_int64_range + (char *) "pp", gst_value_lcopy_int64_range }; FUNC_VALUE_GET_TYPE (int64_range, "GstInt64Range"); @@ -6040,8 +6466,7 @@ static const GTypeValueTable _gst_double_range_value_table = { NULL, (char *) "dd", gst_value_collect_double_range, - (char *) "pp", - gst_value_lcopy_double_range + (char *) "pp", gst_value_lcopy_double_range }; FUNC_VALUE_GET_TYPE (double_range, "GstDoubleRange"); @@ -6053,8 +6478,7 @@ static const GTypeValueTable _gst_fraction_range_value_table = { NULL, (char *) "iiii", gst_value_collect_fraction_range, - (char *) "pppp", - gst_value_lcopy_fraction_range + (char *) "pppp", gst_value_lcopy_fraction_range }; FUNC_VALUE_GET_TYPE (fraction_range, "GstFractionRange"); @@ -6066,8 +6490,7 @@ static const GTypeValueTable _gst_value_list_value_table = { gst_value_list_or_array_peek_pointer, (char *) "p", gst_value_collect_list_or_array, - (char *) "p", - gst_value_lcopy_list_or_array + (char *) "p", gst_value_lcopy_list_or_array }; FUNC_VALUE_GET_TYPE (value_list, "GstValueList"); @@ -6079,8 +6502,7 @@ static const GTypeValueTable _gst_value_array_value_table = { gst_value_list_or_array_peek_pointer, (char *) "p", gst_value_collect_list_or_array, - (char *) "p", - gst_value_lcopy_list_or_array + (char *) "p", gst_value_lcopy_list_or_array }; FUNC_VALUE_GET_TYPE (value_array, "GstValueArray"); @@ -6091,9 +6513,7 @@ static const GTypeValueTable _gst_fraction_value_table = { gst_value_copy_fraction, NULL, (char *) "ii", - gst_value_collect_fraction, - (char *) "pp", - gst_value_lcopy_fraction + gst_value_collect_fraction, (char *) "pp", gst_value_lcopy_fraction }; FUNC_VALUE_GET_TYPE (fraction, "GstFraction"); @@ -6104,13 +6524,23 @@ static const GTypeValueTable _gst_bitmask_value_table = { gst_value_copy_bitmask, NULL, (char *) "q", - gst_value_collect_bitmask, - (char *) "p", - gst_value_lcopy_bitmask + gst_value_collect_bitmask, (char *) "p", gst_value_lcopy_bitmask }; FUNC_VALUE_GET_TYPE (bitmask, "GstBitmask"); +static const GTypeValueTable _gst_flagset_value_table = { + gst_value_init_flagset, + NULL, + gst_value_copy_flagset, + NULL, + (char *) "ii", + gst_value_collect_flagset, (char *) "pp", gst_value_lcopy_flagset +}; + +FUNC_VALUE_GET_TYPE_CLASSED (flagset, "GstFlagSet", + sizeof (GstFlagSetClass), G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_DERIVABLE); + GType gst_g_thread_get_type (void) { @@ -6122,8 +6552,7 @@ gst_g_thread_get_type (void) if (g_once_init_enter (&type_id)) { GType tmp = g_boxed_type_register_static (g_intern_static_string ("GstGThread"), - (GBoxedCopyFunc) g_thread_ref, - (GBoxedFreeFunc) g_thread_unref); + (GBoxedCopyFunc) g_thread_ref, (GBoxedFreeFunc) g_thread_unref); g_once_init_leave (&type_id, tmp); } @@ -6168,9 +6597,11 @@ G_STMT_START { \ gst_value_register (&gst_value); \ } G_STMT_END -static const gint GST_VALUE_TABLE_DEFAULT_SIZE = 32; -static const gint GST_VALUE_UNION_TABLE_DEFAULT_SIZE = 2; -static const gint GST_VALUE_INTERSECT_TABLE_DEFAULT_SIZE = 9; +/* 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 = 33; +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_SUBTRACT_TABLE_DEFAULT_SIZE = 12; void @@ -6202,6 +6633,7 @@ _priv_gst_value_initialize (void) REGISTER_SERIALIZATION (gst_date_time_get_type (), date_time); REGISTER_SERIALIZATION (gst_bitmask_get_type (), bitmask); REGISTER_SERIALIZATION (gst_structure_get_type (), structure); + REGISTER_SERIALIZATION (gst_flagset_get_type (), flagset); REGISTER_SERIALIZATION_NO_COMPARE (gst_segment_get_type (), segment); REGISTER_SERIALIZATION_NO_COMPARE (gst_caps_features_get_type (), @@ -6218,7 +6650,7 @@ _priv_gst_value_initialize (void) REGISTER_SERIALIZATION_CONST (G_TYPE_BOOLEAN, boolean); REGISTER_SERIALIZATION_CONST (G_TYPE_ENUM, enum); - REGISTER_SERIALIZATION_CONST (G_TYPE_FLAGS, flags); + REGISTER_SERIALIZATION_CONST (G_TYPE_FLAGS, gflags); REGISTER_SERIALIZATION_CONST (G_TYPE_INT, int); @@ -6270,25 +6702,32 @@ _priv_gst_value_initialize (void) g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_BITMASK, gst_value_transform_string_bitmask); + g_value_register_transform_func (GST_TYPE_FLAG_SET, G_TYPE_STRING, + gst_value_transform_flagset_string); + g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_FLAG_SET, + gst_value_transform_string_flagset); + gst_value_register_intersect_func (G_TYPE_INT, GST_TYPE_INT_RANGE, gst_value_intersect_int_int_range); gst_value_register_intersect_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE, gst_value_intersect_int_range_int_range); gst_value_register_intersect_func (G_TYPE_INT64, GST_TYPE_INT64_RANGE, gst_value_intersect_int64_int64_range); - gst_value_register_intersect_func (GST_TYPE_INT64_RANGE, GST_TYPE_INT64_RANGE, - gst_value_intersect_int64_range_int64_range); + gst_value_register_intersect_func (GST_TYPE_INT64_RANGE, + GST_TYPE_INT64_RANGE, gst_value_intersect_int64_range_int64_range); gst_value_register_intersect_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE, gst_value_intersect_double_double_range); gst_value_register_intersect_func (GST_TYPE_DOUBLE_RANGE, GST_TYPE_DOUBLE_RANGE, gst_value_intersect_double_range_double_range); - gst_value_register_intersect_func (GST_TYPE_ARRAY, - GST_TYPE_ARRAY, gst_value_intersect_array); - gst_value_register_intersect_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE, - gst_value_intersect_fraction_fraction_range); + gst_value_register_intersect_func (GST_TYPE_ARRAY, GST_TYPE_ARRAY, + gst_value_intersect_array); + gst_value_register_intersect_func (GST_TYPE_FRACTION, + GST_TYPE_FRACTION_RANGE, gst_value_intersect_fraction_fraction_range); gst_value_register_intersect_func (GST_TYPE_FRACTION_RANGE, GST_TYPE_FRACTION_RANGE, 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_subtract_func (G_TYPE_INT, GST_TYPE_INT_RANGE, gst_value_subtract_int_int_range); @@ -6300,18 +6739,18 @@ _priv_gst_value_initialize (void) gst_value_subtract_int64_int64_range); gst_value_register_subtract_func (GST_TYPE_INT64_RANGE, G_TYPE_INT64, gst_value_subtract_int64_range_int64); - gst_value_register_subtract_func (GST_TYPE_INT64_RANGE, GST_TYPE_INT64_RANGE, - gst_value_subtract_int64_range_int64_range); + gst_value_register_subtract_func (GST_TYPE_INT64_RANGE, + GST_TYPE_INT64_RANGE, gst_value_subtract_int64_range_int64_range); gst_value_register_subtract_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE, gst_value_subtract_double_double_range); gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE, G_TYPE_DOUBLE, gst_value_subtract_double_range_double); gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE, GST_TYPE_DOUBLE_RANGE, gst_value_subtract_double_range_double_range); - gst_value_register_subtract_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE, - gst_value_subtract_fraction_fraction_range); - gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE, GST_TYPE_FRACTION, - gst_value_subtract_fraction_range_fraction); + gst_value_register_subtract_func (GST_TYPE_FRACTION, + GST_TYPE_FRACTION_RANGE, gst_value_subtract_fraction_fraction_range); + gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE, + GST_TYPE_FRACTION, gst_value_subtract_fraction_range_fraction); gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE, GST_TYPE_FRACTION_RANGE, gst_value_subtract_fraction_range_fraction_range); @@ -6327,6 +6766,8 @@ _priv_gst_value_initialize (void) gst_value_union_int_int_range); gst_value_register_union_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE, 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); #if GST_VERSION_NANO == 1 /* If building from git master, check starting array sizes matched actual size @@ -6361,3 +6802,43 @@ _priv_gst_value_initialize (void) GST_TYPE_FRACTION_RANGE, gst_value_union_fraction_range_fraction_range); #endif } + +static void +gst_flagset_class_init (gpointer g_class, gpointer class_data) +{ + GstFlagSetClass *f_class = (GstFlagSetClass *) (g_class); + f_class->flags_type = (GType) GPOINTER_TO_SIZE (class_data); +} + +/** + * gst_flagset_register: + * @flags_type: a #GType of a #G_TYPE_FLAGS type. + * + * Create a new sub-class of #GST_TYPE_FLAG_SET + * which will pretty-print the human-readable flags + * when serializing, for easier debugging. + * + * Since: 1.6 + */ +GType +gst_flagset_register (GType flags_type) +{ + GTypeInfo info = { + sizeof (GstFlagSetClass), + NULL, NULL, + (GClassInitFunc) gst_flagset_class_init, + NULL, GSIZE_TO_POINTER (flags_type), 0, 0, NULL, NULL + }; + GType t; + gchar *class_name; + + g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), 0); + + class_name = g_strdup_printf ("%sSet", g_type_name (flags_type)); + + t = g_type_register_static (GST_TYPE_FLAG_SET, + g_intern_string (class_name), &info, 0); + g_free (class_name); + + return t; +} diff --git a/gst/gstvalue.h b/gst/gstvalue.h index 2e15642e97..d45d5f86e3 100644 --- a/gst/gstvalue.h +++ b/gst/gstvalue.h @@ -200,6 +200,26 @@ G_BEGIN_DECLS */ #define GST_VALUE_HOLDS_BITMASK(x) ((x) != NULL && G_VALUE_TYPE(x) == _gst_bitmask_type) +/** + * GST_VALUE_HOLDS_FLAG_SET: + * @x: the #GValue to check + * + * Checks if the given #GValue contains a #GST_TYPE_FLAG_SET value. + * + * Since: 1.6 + */ +#define GST_VALUE_HOLDS_FLAG_SET(x) (G_TYPE_CHECK_VALUE_TYPE ((x), GST_TYPE_FLAG_SET)) + +/* + * GST_FLAG_SET_MASK_EXACT: + * A mask value with all bits set, for use as a + * #GstFlagSet mask where all flag bits must match + * exactly + * + * Since: 1.6 + */ +#define GST_FLAG_SET_MASK_EXACT ((guint)(-1)) + GST_EXPORT GType _gst_int_range_type; /** @@ -300,6 +320,21 @@ GST_EXPORT GType _gst_bitmask_type; #define GST_TYPE_BITMASK (_gst_bitmask_type) +GST_EXPORT GType _gst_flagset_type; + +/** + * GST_TYPE_FLAG_SET: + * + * a #GValue type that represents a 32-bit flag bitfield, with 32-bit + * mask indicating which of the bits in the field are explicitly set. + * Useful for negotiation. + * + * Returns: the #GType of GstFlags (which is not explicitly typed) + * + * Since: 1.6 + */ +#define GST_TYPE_FLAG_SET (_gst_flagset_type) + /** * GST_TYPE_G_THREAD: * @@ -407,6 +442,7 @@ GType gst_fraction_get_type (void); GType gst_value_list_get_type (void); GType gst_value_array_get_type (void); GType gst_bitmask_get_type (void); +GType gst_flagset_get_type (void); /* Hide this compatibility type from introspection */ #ifndef __GI_SCANNER__ @@ -526,6 +562,10 @@ const GValue *gst_value_get_fraction_range_max (const GValue *value); guint64 gst_value_get_bitmask (const GValue *value); void gst_value_set_bitmask (GValue *value, guint64 bitmask); +/* flagset */ +void gst_value_set_flagset (GValue * value, guint flags, guint mask); +guint gst_value_get_flagset_flags (const GValue * value); +guint gst_value_get_flagset_mask (const GValue * value); /* compare */ gint gst_value_compare (const GValue *value1, @@ -561,6 +601,9 @@ gboolean gst_value_is_fixed (const GValue *value); gboolean gst_value_fixate (GValue *dest, const GValue *src); +/* Flagset registration wrapper */ +GType gst_flagset_register (GType flags_type); + G_END_DECLS #endif diff --git a/tests/check/gst/capslist.h b/tests/check/gst/capslist.h index 20c1409b8c..0991fa89fd 100644 --- a/tests/check/gst/capslist.h +++ b/tests/check/gst/capslist.h @@ -26,7 +26,9 @@ static const gchar *caps_list[] = { /* Test lists of fractions and fraction ranges */ "test/gst-fraction-range, fraction = (fraction) { [ 1/4, 1/3 ], 1/8 }", "test/gst-fraction-range, fraction = (fraction) { [ 1/4, 1/3 ], [ 1/8, 2/8 ] }", - + /* FlagSet */ + "test/gst-flags,thingy=1f394:00ff8ff", + "test/gst-flags,thingy=101ffff1f394:fff00ff00ff8ff", /* Some random checks */ "video/x-raw, format = (string) { I420, Y42B, Y444 }, framerate = (fraction) [1/MAX, MAX], width = (int) [ 1, MAX ], height = (int) [ 1, MAX ]", diff --git a/tests/check/gst/gstcaps.c b/tests/check/gst/gstcaps.c index 66453610e3..dbf933b5e8 100644 --- a/tests/check/gst/gstcaps.c +++ b/tests/check/gst/gstcaps.c @@ -39,7 +39,7 @@ GST_START_TEST (test_from_string) to_str = gst_caps_to_string (caps); fail_if (to_str == NULL, "Could not convert caps back to string %s\n", caps_list[i]); - caps2 = gst_caps_from_string (caps_list[i]); + caps2 = gst_caps_from_string (to_str); fail_if (caps2 == NULL, "Could not create caps from string %s\n", to_str); fail_unless (gst_caps_is_equal (caps, caps)); @@ -934,6 +934,104 @@ GST_START_TEST (test_intersect_duplication) GST_END_TEST; +GST_START_TEST (test_intersect_flagset) +{ + GstCaps *c1, *c2, *test; + GType test_flagset_type; + GstSeekFlags test_flags, test_mask; + gchar *test_string; + + /* Test that matching bits inside the mask intersect, + * and bits outside the mask don't matter */ + c1 = gst_caps_from_string ("test/x-caps,field=ffd81d:fffff0"); + c2 = gst_caps_from_string ("test/x-caps,field=0fd81f:0ffff0"); + + test = gst_caps_intersect_full (c1, c2, GST_CAPS_INTERSECT_FIRST); + fail_unless_equals_int (gst_caps_get_size (test), 1); + fail_unless (gst_caps_is_equal (c1, test)); + gst_caps_unref (c1); + gst_caps_unref (c2); + gst_caps_unref (test); + + /* Test that non-matching bits in the mask don't intersect */ + c1 = gst_caps_from_string ("test/x-caps,field=ff001d:0ffff0"); + c2 = gst_caps_from_string ("test/x-caps,field=0fd81f:0ffff0"); + + test = gst_caps_intersect_full (c1, c2, GST_CAPS_INTERSECT_FIRST); + fail_unless (gst_caps_is_empty (test)); + gst_caps_unref (c1); + gst_caps_unref (c2); + gst_caps_unref (test); + + /* Check custom flags type serialisation and de-serialisation */ + test_flagset_type = gst_flagset_register (GST_TYPE_SEEK_FLAGS); + fail_unless (g_type_is_a (test_flagset_type, GST_TYPE_FLAG_SET)); + + test_flags = + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_TRICKMODE | + GST_SEEK_FLAG_TRICKMODE_KEY_UNITS; + test_mask = + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_TRICKMODE | + GST_SEEK_FLAG_TRICKMODE_NO_AUDIO; + + c1 = gst_caps_new_simple ("test/x-caps", "field", test_flagset_type, + (guint64) (test_flags), (guint64) (test_mask), NULL); + + test_string = gst_caps_to_string (c1); + fail_if (test_string == NULL); + + GST_DEBUG ("Serialised caps to %s", test_string); + c2 = gst_caps_from_string (test_string); + + fail_unless (gst_caps_is_equal (c1, c2)); + + gst_caps_unref (c1); + gst_caps_unref (c2); +} + +GST_END_TEST; + +GST_START_TEST (test_union) +{ + GstCaps *c1, *c2, *test, *expect; + + /* Test that matching bits inside the masks union OK, */ + c1 = gst_caps_from_string ("test/x-caps,field=ffd81d:0ffff0"); + c2 = gst_caps_from_string ("test/x-caps,field=0fd81f:0ffff0"); + + test = gst_caps_merge (c1, c2); + test = gst_caps_simplify (test); + /* c1, c2 now invalid */ + fail_unless_equals_int (gst_caps_get_size (test), 1); + gst_caps_unref (test); + + /* Test that non-intersecting sets of masked bits are OK */ + c1 = gst_caps_from_string ("test/x-caps,field=ff001d:0ffff0"); + c2 = gst_caps_from_string ("test/x-caps,field=4fd81f:f00000"); + expect = gst_caps_from_string ("test/x-caps,field=4f001d:fffff0"); + test = gst_caps_simplify (gst_caps_merge (c1, c2)); + /* c1, c2 now invalid */ + GST_LOG ("Expected caps %" GST_PTR_FORMAT " got %" GST_PTR_FORMAT "\n", + expect, test); + fail_unless (gst_caps_is_equal (test, expect)); + gst_caps_unref (test); + gst_caps_unref (expect); + + /* Test that partially-intersecting sets of masked bits that match are OK */ + c1 = gst_caps_from_string ("test/x-caps,field=ff001d:0ffff0"); + c2 = gst_caps_from_string ("test/x-caps,field=4fd81f:ff0000"); + expect = gst_caps_from_string ("test/x-caps,field=4f001d:fffff0"); + test = gst_caps_simplify (gst_caps_merge (c1, c2)); + /* c1, c2 now invalid */ + GST_LOG ("Expected caps %" GST_PTR_FORMAT " got %" GST_PTR_FORMAT "\n", + expect, test); + fail_unless (gst_caps_is_equal (test, expect)); + gst_caps_unref (test); + gst_caps_unref (expect); +} + +GST_END_TEST; + static gboolean _caps_is_fixed_foreach (GQuark field_id, const GValue * value, gpointer unused) { @@ -1311,6 +1409,8 @@ gst_caps_suite (void) tcase_add_test (tc_chain, test_intersect_first); tcase_add_test (tc_chain, test_intersect_first2); tcase_add_test (tc_chain, test_intersect_duplication); + tcase_add_test (tc_chain, test_intersect_flagset); + tcase_add_test (tc_chain, test_union); tcase_add_test (tc_chain, test_normalize); tcase_add_test (tc_chain, test_broken); tcase_add_test (tc_chain, test_features); diff --git a/tests/check/gst/gststructure.c b/tests/check/gst/gststructure.c index 18c58f6fa6..ae1ef5328a 100644 --- a/tests/check/gst/gststructure.c +++ b/tests/check/gst/gststructure.c @@ -749,6 +749,30 @@ GST_START_TEST (test_filter_and_map_in_place) GST_END_TEST; +GST_START_TEST (test_flagset) +{ + GstStructure *s; + GType test_flagset_type; + guint test_flags = + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP | GST_SEEK_FLAG_SNAP_AFTER; + guint test_mask = GST_FLAG_SET_MASK_EXACT; + guint out_flags, out_mask; + + test_flagset_type = gst_flagset_register (GST_TYPE_SEEK_FLAGS); + fail_unless (g_type_is_a (test_flagset_type, GST_TYPE_FLAG_SET)); + + /* Check that we can retrieve a non-standard flagset from the structure */ + s = gst_structure_new ("test-struct", "test-flagset", test_flagset_type, + test_flags, test_mask, NULL); + fail_unless (gst_structure_get_flagset (s, "test-flagset", &out_flags, + &out_mask)); + + fail_unless (out_flags == test_flags); + fail_unless (out_mask == test_mask); +} + +GST_END_TEST; + static Suite * gst_structure_suite (void) { @@ -774,6 +798,7 @@ gst_structure_suite (void) tcase_add_test (tc_chain, test_foreach); tcase_add_test (tc_chain, test_map_in_place); tcase_add_test (tc_chain, test_filter_and_map_in_place); + tcase_add_test (tc_chain, test_flagset); return s; } diff --git a/tests/check/gst/gstvalue.c b/tests/check/gst/gstvalue.c index 3be87c40b1..554866ee33 100644 --- a/tests/check/gst/gstvalue.c +++ b/tests/check/gst/gstvalue.c @@ -497,6 +497,180 @@ GST_START_TEST (test_deserialize_bitmask) GST_END_TEST; +static void +check_flagset_mask_serialisation (GValue * value, guint test_flags, + guint test_mask) +{ + gchar *string; + gst_value_set_flagset (value, test_flags, test_mask); + + /* Normalise our test flags against the mask now for easier testing, + * as that's what we expect to get back from the flagset after it + * normalises internally */ + test_flags &= test_mask; + + /* Check the values got stored correctly */ + fail_unless (gst_value_get_flagset_flags (value) == test_flags, + "resulting flags value is 0x%u, not 0x%x", + gst_value_get_flagset_flags (value), test_flags); + fail_unless (gst_value_get_flagset_mask (value) == test_mask, + "resulting mask is 0x%u, not 0x%x", + gst_value_get_flagset_mask (value), test_mask); + + string = gst_value_serialize (value); + fail_if (string == NULL, "could not serialize flagset"); + + GST_DEBUG ("Serialized flagset to: %s", string); + + fail_unless (gst_value_deserialize (value, string), + "could not deserialize %s", string); + + fail_unless (gst_value_get_flagset_flags (value) == test_flags, + "resulting flags value is 0x%u, not 0x%x, for string %s", + gst_value_get_flagset_flags (value), test_flags, string); + + fail_unless (gst_value_get_flagset_mask (value) == test_mask, + "resulting mask is 0x%u, not 0x%x, for string %s", + gst_value_get_flagset_mask (value), test_mask, string); + + g_free (string); +} + +GST_START_TEST (test_flagset) +{ + GValue value = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + GValue dest = G_VALUE_INIT; + gchar *string; + GType test_flagset_type; + guint test_flags, test_mask; + + /* Test serialisation of abstract type */ + g_value_init (&value, GST_TYPE_FLAG_SET); + + test_flags = 0xf1f1; + test_mask = 0xffff; + + gst_value_set_flagset (&value, test_flags, test_mask); + string = gst_value_serialize (&value); + fail_if (string == NULL, "could not serialize flagset"); + + fail_unless (gst_value_deserialize (&value, string), + "could not deserialize %s", string); + + fail_unless (gst_value_get_flagset_flags (&value) == test_flags, + "resulting value is 0x%u, not 0x%x, for string %s", + gst_value_get_flagset_flags (&value), test_flags, string); + + fail_unless (gst_value_get_flagset_mask (&value) == test_mask, + "resulting value is 0x%u, not 0x%x, for string %s", + gst_value_get_flagset_mask (&value), test_mask, string); + + g_free (string); + g_value_unset (&value); + + /* Check we can't wrap a random non-flags type */ + ASSERT_CRITICAL (gst_flagset_register (GST_TYPE_OBJECT)); + + test_flagset_type = gst_flagset_register (GST_TYPE_SEEK_FLAGS); + + fail_unless (g_type_is_a (test_flagset_type, GST_TYPE_FLAG_SET)); + + g_value_init (&value, test_flagset_type); + + test_flags = + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_TRICKMODE | + GST_SEEK_FLAG_TRICKMODE_KEY_UNITS; + test_mask = + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_TRICKMODE | + GST_SEEK_FLAG_TRICKMODE_NO_AUDIO; + + check_flagset_mask_serialisation (&value, test_flags, test_mask); + /* Check serialisation works with the generic 'exact' flag */ + check_flagset_mask_serialisation (&value, test_flags, + GST_FLAG_SET_MASK_EXACT); + + /* Check deserialisation of flagset in 'flags' form, without + * the hex strings at the start */ + test_flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_TRICKMODE; + test_mask = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_TRICKMODE | + GST_SEEK_FLAG_TRICKMODE_NO_AUDIO; + string = g_strdup ("+flush+trickmode/trickmode-no-audio"); + + fail_unless (gst_value_deserialize (&value, string), + "could not deserialize %s", string); + + GST_DEBUG ("Deserialized %s to 0x%x:0x%x", string, + gst_value_get_flagset_flags (&value), + gst_value_get_flagset_mask (&value)); + + fail_unless (gst_value_get_flagset_flags (&value) == test_flags, + "resulting flags value is 0x%u, not 0x%x, for string %s", + gst_value_get_flagset_flags (&value), (test_flags & test_mask), string); + + fail_unless (gst_value_get_flagset_mask (&value) == test_mask, + "resulting mask is 0x%u, not 0x%x, for string %s", + gst_value_get_flagset_mask (&value), test_mask, string); + + g_free (string); + g_value_unset (&value); + + /* Test that fixating don't-care fields works, using our + * sub-type flagset for good measure */ + g_value_init (&value, test_flagset_type); + gst_value_set_flagset (&value, test_flags, test_mask); + + fail_unless (gst_value_fixate (&dest, &value)); + fail_unless (gst_value_get_flagset_flags (&dest) == test_flags); + fail_unless (gst_value_get_flagset_mask (&dest) == GST_FLAG_SET_MASK_EXACT); + + g_value_unset (&value); + + /* Intersection tests */ + g_value_init (&value, GST_TYPE_FLAG_SET); + g_value_init (&value2, test_flagset_type); + + /* We want Accurate, but not Snap-Before */ + gst_value_set_flagset (&value, GST_SEEK_FLAG_ACCURATE, + GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_SNAP_BEFORE); + + /* This only cares that things are flushing */ + gst_value_set_flagset (&value2, GST_SEEK_FLAG_FLUSH, GST_SEEK_FLAG_FLUSH); + + test_flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH; + test_mask = + GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SNAP_BEFORE; + + /* GstFlagSet should always intersect with itself */ + g_value_unset (&dest); + fail_unless (gst_value_intersect (&dest, &value, &value)); + + /* GstFlagSet subtype should intersect with itself */ + g_value_unset (&dest); + fail_unless (gst_value_intersect (&dest, &value2, &value2)); + + /* Check we can intersect custom flagset subtype with flagset */ + g_value_unset (&dest); + fail_unless (gst_value_intersect (&dest, &value2, &value)); + + g_value_unset (&dest); + fail_unless (gst_value_intersect (&dest, &value, &value2)); + + fail_unless (gst_value_get_flagset_flags (&dest) == test_flags, + "resulting flags value is 0x%u, not 0x%x", + gst_value_get_flagset_flags (&dest), test_flags); + + fail_unless (gst_value_get_flagset_mask (&dest) == test_mask, + "resulting mask is 0x%u, not 0x%x", + gst_value_get_flagset_mask (&dest), test_mask); + + g_value_unset (&dest); + g_value_unset (&value); +} + +GST_END_TEST; + + GST_START_TEST (test_string) { const gchar *try[] = { @@ -2904,6 +3078,7 @@ gst_value_suite (void) tcase_add_test (tc_chain, test_stepped_range_collection); 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); return s; } diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index 04df312a5a..c130747821 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -53,6 +53,7 @@ EXPORTS _gst_double_range_type DATA _gst_element_error_printf _gst_event_type DATA + _gst_flagset_type DATA _gst_fraction_range_type DATA _gst_fraction_type DATA _gst_int64_range_type DATA @@ -601,6 +602,8 @@ EXPORTS gst_event_type_to_quark gst_event_writable_structure gst_filename_to_uri + gst_flagset_get_type + gst_flagset_register gst_flow_get_name gst_flow_return_get_type gst_flow_to_quark @@ -1175,6 +1178,7 @@ EXPORTS gst_structure_get_double gst_structure_get_enum gst_structure_get_field_type + gst_structure_get_flagset gst_structure_get_fraction gst_structure_get_int gst_structure_get_int64 @@ -1465,6 +1469,8 @@ EXPORTS gst_value_get_caps_features gst_value_get_double_range_max gst_value_get_double_range_min + gst_value_get_flagset_flags + gst_value_get_flagset_mask gst_value_get_fraction_denominator gst_value_get_fraction_numerator gst_value_get_fraction_range_max @@ -1494,6 +1500,7 @@ EXPORTS gst_value_set_caps gst_value_set_caps_features gst_value_set_double_range + gst_value_set_flagset gst_value_set_fraction gst_value_set_fraction_range gst_value_set_fraction_range_full