gstvalue: Add GstFlagSet type
authorJan Schmidt <jan@centricular.com>
Mon, 25 May 2015 06:23:33 +0000 (16:23 +1000)
committerJan Schmidt <jan@centricular.com>
Mon, 25 May 2015 06:23:33 +0000 (16:23 +1000)
GstFlagSet is a new type designed for negotiating sets
of boolean capabilities flags, consisting of a 32-bit
flags bitfield and 32-bit mask field. The mask field
indicates which of the flags bits an element needs to have
as specific values, and which it doesn't care about.

This allows efficient negotiation of arrays of boolean
capabilities.

The standard serialisation format is FLAGS:MASK, with
flags and mask fields expressed in hexadecimal, however
GstFlagSet has a gst_register_flagset() function, which
associates a new GstFlagSet derived type with an existing
GFlags gtype. When serializing a GstFlagSet with an
associated set of GFlags, it also serializes a human-readable
form of the flags for easier debugging.

It is possible to parse a GFlags style serialisation of a
flagset, without the hex portion on the front. ie,
+flag1/flag2/flag3+flag4, to indicate that
flag1 & flag4 must be set, and flag2/flag3 must be unset,
and any other flags are don't-care.

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

docs/gst/gstreamer-sections.txt
gst/gststructure.c
gst/gststructure.h
gst/gstvalue.c
gst/gstvalue.h
tests/check/gst/capslist.h
tests/check/gst/gstcaps.c
tests/check/gst/gststructure.c
tests/check/gst/gstvalue.c
win32/common/libgstreamer.def

index dba70f5..204296e 100644 (file)
@@ -3285,6 +3285,15 @@ GST_TYPE_BITMASK
 gst_value_set_bitmask
 gst_value_get_bitmask
 
+<SUBSECTION flagset>
+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
+
 <SUBSECTION int64range>
 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
 </SECTION>
 
 <SECTION>
index 8b0b54b..90e7cbe 100644 (file)
@@ -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;
 
index 014ef37..a2b7926 100644 (file)
@@ -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,
index dea3ba4..0c7c3c3 100644 (file)
@@ -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,44 +3231,94 @@ 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;
-        }
-      }
+  /* 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 = '+';
+  }
+
+  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 */
     }
-    if (fl) {
-      flags |= fl->value;
+    g_free (cur_str);
+
+    if (val) {
+      mask |= val;
+      if (delimiter == '+')
+        flags |= val;
     }
-    i++;
+
+    /* 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_strfreev (split);
+
   g_type_class_unref (klass);
-  g_value_set_flags (dest, flags);
 
-  return TRUE;
+  return res;
 }
 
 /****************
@@ -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;
+}
index 2e15642..d45d5f8 100644 (file)
@@ -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
index 20c1409..0991fa8 100644 (file)
@@ -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 ]",
 
index 6645361..dbf933b 100644 (file)
@@ -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);
index 18c58f6..ae1ef53 100644 (file)
@@ -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;
 }
 
index 3be87c4..554866e 100644 (file)
@@ -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;
 }
index 04df312..c130747 100644 (file)
@@ -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