gstreamer_core/gst/gststructure.c
changeset 16 8e837d1bf446
parent 0 0e761a78d257
child 30 7e817e7e631c
--- a/gstreamer_core/gst/gststructure.c	Wed Mar 24 17:58:42 2010 -0500
+++ b/gstreamer_core/gst/gststructure.c	Wed Mar 24 18:04:17 2010 -0500
@@ -49,7 +49,10 @@
  * Fields can be removed with gst_structure_remove_field() or
  * gst_structure_remove_fields().
  *
- * Last reviewed on 2007-10-16 (0.10.15)
+ * Strings in structures must be ASCII or UTF-8 encoded. Other encodings are
+ * not allowed. Strings must not be empty either, but may be NULL.
+ *
+ * Last reviewed on 2009-06-08 (0.10.23)
  */
 
 #ifdef HAVE_CONFIG_H
@@ -59,6 +62,7 @@
 #include <string.h>
 
 #include "gst_private.h"
+#include "gstquark.h"
 #include <gst/gst.h>
 #include <gobject/gvaluecollector.h>
 
@@ -81,6 +85,9 @@
     (!(structure)->parent_refcount || \
      g_atomic_int_get ((structure)->parent_refcount) == 1)
 
+#define IS_TAGLIST(structure) \
+    (structure->name == GST_QUARK (TAGLIST))
+
 static void gst_structure_set_field (GstStructure * structure,
     GstStructureField * field);
 static GstStructureField *gst_structure_get_field (const GstStructure *
@@ -121,11 +128,12 @@
 {
   GstStructure *structure;
 
-  structure = g_new0 (GstStructure, 1);
+  structure = g_slice_new (GstStructure);
   structure->type = gst_structure_get_type ();
   structure->name = quark;
+  structure->parent_refcount = NULL;
   structure->fields =
-      g_array_sized_new (FALSE, TRUE, sizeof (GstStructureField), prealloc);
+      g_array_sized_new (FALSE, FALSE, sizeof (GstStructureField), prealloc);
 
   return structure;
 }
@@ -150,6 +158,7 @@
   return gst_structure_id_empty_new_with_size (quark, 0);
 }
 
+#ifndef G_DISABLE_CHECKS
 static gboolean
 gst_structure_validate_name (const gchar * name)
 {
@@ -158,7 +167,7 @@
   g_return_val_if_fail (name != NULL, FALSE);
 
   /* FIXME 0.11: use g_ascii_isalpha() */
-  if (!g_ascii_isalnum (*name)) {
+  if (G_UNLIKELY (!g_ascii_isalnum (*name))) {
     GST_WARNING ("Invalid character '%c' at offset 0 in structure name: %s",
         *name, name);
     return FALSE;
@@ -169,7 +178,7 @@
   s = &name[1];
   while (*s && (g_ascii_isalnum (*s) || strchr ("/-_.:+ ", *s) != NULL))
     s++;
-  if (*s != '\0') {
+  if (G_UNLIKELY (*s != '\0')) {
     GST_WARNING ("Invalid character '%c' at offset %lu in structure name: %s",
         *s, ((gulong) s - (gulong) name), name);
     return FALSE;
@@ -177,6 +186,7 @@
 
   return TRUE;
 }
+#endif
 
 /**
  * gst_structure_empty_new:
@@ -282,7 +292,7 @@
 #endif
 
 void
-gst_structure_set_parent_refcount (GstStructure * structure, int *refcount)
+gst_structure_set_parent_refcount (GstStructure * structure, gint * refcount)
 {
   g_return_if_fail (structure != NULL);
 
@@ -313,7 +323,7 @@
 {
   GstStructure *new_structure;
   GstStructureField *field;
-  guint i;
+  guint i, len;
 
   g_return_val_if_fail (structure != NULL, NULL);
 
@@ -321,7 +331,8 @@
       gst_structure_id_empty_new_with_size (structure->name,
       structure->fields->len);
 
-  for (i = 0; i < structure->fields->len; i++) {
+  len = structure->fields->len;
+  for (i = 0; i < len; i++) {
     GstStructureField new_field = { 0 };
 
     field = GST_STRUCTURE_FIELD (structure, i);
@@ -349,12 +360,13 @@
 gst_structure_free (GstStructure * structure)
 {
   GstStructureField *field;
-  guint i;
+  guint i, len;
 
   g_return_if_fail (structure != NULL);
   g_return_if_fail (structure->parent_refcount == NULL);
 
-  for (i = 0; i < structure->fields->len; i++) {
+  len = structure->fields->len;
+  for (i = 0; i < len; i++) {
     field = GST_STRUCTURE_FIELD (structure, i);
 
     if (G_IS_VALUE (&field->value)) {
@@ -365,7 +377,7 @@
 #ifdef USE_POISONING
   memset (structure, 0xff, sizeof (GstStructure));
 #endif
-  g_free (structure);
+  g_slice_free (GstStructure, structure);
 }
 
 /**
@@ -484,17 +496,6 @@
   g_return_if_fail (G_IS_VALUE (value));
   g_return_if_fail (IS_MUTABLE (structure));
 
-  if (G_VALUE_HOLDS_STRING (value)) {
-    const gchar *s;
-
-    s = g_value_get_string (value);
-    if (G_UNLIKELY (s != NULL && !g_utf8_validate (s, -1, NULL))) {
-      g_warning ("Trying to set string field '%s' on structure, but string is "
-          "not valid UTF-8. Please file a bug.", g_quark_to_string (field));
-      return;
-    }
-  }
-
   gsfield.name = field;
   gst_value_init_and_copy (&gsfield.value, value);
 
@@ -540,7 +541,7 @@
  */
 #ifdef __SYMBIAN32__
 EXPORT_C
-#endif 
+#endif
 void
 gst_structure_set (GstStructure * structure, const gchar * field, ...)
 {
@@ -584,14 +585,14 @@
 
     type = va_arg (varargs, GType);
 
-    if (type == G_TYPE_DATE) {
+    if (G_UNLIKELY (type == G_TYPE_DATE)) {
       g_warning ("Don't use G_TYPE_DATE, use GST_TYPE_DATE instead\n");
       type = GST_TYPE_DATE;
     }
 
     g_value_init (&field.value, type);
     G_VALUE_COLLECT (&field.value, varargs, 0, &err);
-    if (err) {
+    if (G_UNLIKELY (err)) {
       g_critical ("%s", err);
       return;
     }
@@ -662,14 +663,14 @@
 
     type = va_arg (varargs, GType);
 
-    if (type == G_TYPE_DATE) {
+    if (G_UNLIKELY (type == G_TYPE_DATE)) {
       g_warning ("Don't use G_TYPE_DATE, use GST_TYPE_DATE instead\n");
       type = GST_TYPE_DATE;
     }
 
     g_value_init (&field.value, type);
     G_VALUE_COLLECT (&field.value, varargs, 0, &err);
-    if (err) {
+    if (G_UNLIKELY (err)) {
       g_critical ("%s", err);
       return;
     }
@@ -679,6 +680,51 @@
   }
 }
 
+/**
+ * gst_structure_id_new:
+ * @name_quark: name of new structure
+ * @field_quark: the GQuark for the name of the field to set
+ * @...: variable arguments
+ *
+ * Creates a new #GstStructure with the given name as a GQuark, followed by
+ * fieldname quark, GType, argument(s) "triplets" in the same format as
+ * gst_structure_id_set(). Basically a convenience wrapper around
+ * gst_structure_id_empty_new() and gst_structure_id_set().
+ *
+ * The last variable argument must be NULL (or 0).
+ *
+ * Returns: a new #GstStructure
+ *
+ * Since: 0.10.24
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstStructure *
+gst_structure_id_new (GQuark name_quark, GQuark field_quark, ...)
+{
+  GstStructure *s;
+  va_list varargs;
+
+  g_return_val_if_fail (name_quark != 0, NULL);
+  g_return_val_if_fail (field_quark != 0, NULL);
+
+  s = gst_structure_id_empty_new (name_quark);
+
+  va_start (varargs, field_quark);
+  gst_structure_id_set_valist (s, field_quark, varargs);
+  va_end (varargs);
+
+  return s;
+}
+
+#if GST_VERSION_NANO == 1
+#define GIT_G_WARNING g_warning
+#else
+#define GIT_G_WARNING GST_WARNING
+#endif
+
 /* If the structure currently contains a field with the same name, it is
  * replaced with the provided field. Otherwise, the field is added to the
  * structure. The field's value is not deeply copied.
@@ -687,12 +733,41 @@
 gst_structure_set_field (GstStructure * structure, GstStructureField * field)
 {
   GstStructureField *f;
-  guint i;
-
-  for (i = 0; i < structure->fields->len; i++) {
+  guint i, len = structure->fields->len;
+
+  if (G_UNLIKELY (G_VALUE_HOLDS_STRING (&field->value))) {
+    const gchar *s;
+
+    s = g_value_get_string (&field->value);
+    /* only check for NULL strings in taglists, as they are allowed in message
+     * structs, e.g. error message debug strings */
+    if (G_UNLIKELY (IS_TAGLIST (structure) && (s == NULL || *s == '\0'))) {
+      if (s == NULL) {
+        GIT_G_WARNING ("Trying to set NULL string on field '%s' on taglist. "
+            "Please file a bug.", g_quark_to_string (field->name));
+        g_value_unset (&field->value);
+        return;
+      } else {
+        /* empty strings never make sense */
+        GIT_G_WARNING ("Trying to set empty string on taglist field '%s'. "
+            "Please file a bug.", g_quark_to_string (field->name));
+        g_value_unset (&field->value);
+        return;
+      }
+    } else if (G_UNLIKELY (s != NULL && !g_utf8_validate (s, -1, NULL))) {
+      g_warning ("Trying to set string on %s field '%s', but string is not "
+          "valid UTF-8. Please file a bug.",
+          IS_TAGLIST (structure) ? "taglist" : "structure",
+          g_quark_to_string (field->name));
+      g_value_unset (&field->value);
+      return;
+    }
+  }
+
+  for (i = 0; i < len; i++) {
     f = GST_STRUCTURE_FIELD (structure, i);
 
-    if (f->name == field->name) {
+    if (G_UNLIKELY (f->name == field->name)) {
       g_value_unset (&f->value);
       memcpy (f, field, sizeof (GstStructureField));
       return;
@@ -708,14 +783,15 @@
 gst_structure_id_get_field (const GstStructure * structure, GQuark field_id)
 {
   GstStructureField *field;
-  guint i;
-
+  guint i, len;
+
+  len = structure->fields->len;
   g_return_val_if_fail (structure != NULL, NULL);
 
-  for (i = 0; i < structure->fields->len; i++) {
+  for (i = 0; i < len; i++) {
     field = GST_STRUCTURE_FIELD (structure, i);
 
-    if (field->name == field_id)
+    if (G_UNLIKELY (field->name == field_id))
       return field;
   }
 
@@ -809,15 +885,16 @@
 {
   GstStructureField *field;
   GQuark id;
-  guint i;
+  guint i, len;
 
   g_return_if_fail (structure != NULL);
   g_return_if_fail (fieldname != NULL);
   g_return_if_fail (IS_MUTABLE (structure));
 
   id = g_quark_from_string (fieldname);
-
-  for (i = 0; i < structure->fields->len; i++) {
+  len = structure->fields->len;
+
+  for (i = 0; i < len; i++) {
     field = GST_STRUCTURE_FIELD (structure, i);
 
     if (field->name == id) {
@@ -1014,18 +1091,20 @@
 gst_structure_foreach (const GstStructure * structure,
     GstStructureForeachFunc func, gpointer user_data)
 {
-  guint i;
+  guint i, len;
   GstStructureField *field;
   gboolean ret;
 
   g_return_val_if_fail (structure != NULL, FALSE);
   g_return_val_if_fail (func != NULL, FALSE);
 
-  for (i = 0; i < structure->fields->len; i++) {
+  len = structure->fields->len;
+
+  for (i = 0; i < len; i++) {
     field = GST_STRUCTURE_FIELD (structure, i);
 
     ret = func (field->name, &field->value, user_data);
-    if (!ret)
+    if (G_UNLIKELY (!ret))
       return FALSE;
   }
 
@@ -1053,15 +1132,16 @@
 gst_structure_map_in_place (GstStructure * structure,
     GstStructureMapFunc func, gpointer user_data)
 {
-  guint i;
+  guint i, len;
   GstStructureField *field;
   gboolean ret;
 
   g_return_val_if_fail (structure != NULL, FALSE);
   g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
   g_return_val_if_fail (func != NULL, FALSE);
-
-  for (i = 0; i < structure->fields->len; i++) {
+  len = structure->fields->len;
+
+  for (i = 0; i < len; i++) {
     field = GST_STRUCTURE_FIELD (structure, i);
 
     ret = func (field->name, &field->value, user_data);
@@ -1303,6 +1383,11 @@
  * given field.  Caller is responsible for making sure the field exists
  * and has the correct type.
  *
+ * On success @value will point to a newly-allocated copy of the date which
+ * should be freed with g_date_free() when no longer needed (note: this is
+ * inconsistent with e.g. gst_structure_get_string() which doesn't return a
+ * copy of the string).
+ *
  * Returns: TRUE if the value could be set correctly. If there was no field
  * with @fieldname or the existing field did not contain a data, this function
  * returns FALSE.
@@ -1328,6 +1413,7 @@
   if (!GST_VALUE_HOLDS_DATE (&field->value))
     return FALSE;
 
+  /* FIXME: 0.11 g_value_dup_boxed() -> g_value_get_boxed() */
   *value = g_value_dup_boxed (&field->value);
 
   return TRUE;
@@ -1540,7 +1626,7 @@
 
 typedef struct _GstStructureAbbreviation
 {
-  char *type_name;
+  gchar *type_name;
   GType type;
 }
 GstStructureAbbreviation;
@@ -1555,7 +1641,7 @@
 
   if (abbrs == NULL) {
     /* dynamically generate the array */
-    #ifdef __SYMBIAN32__
+#ifdef __SYMBIAN32__
 
       GstStructureAbbreviation dyn_abbrs[] = {
 
@@ -1715,27 +1801,23 @@
   }
   return G_VALUE_TYPE (val);
 }
-
-/* keep in sync with gstvalue.c */
-#define GST_ASCII_IS_STRING(c) (g_ascii_isalnum((c)) || ((c) == '_') || \
-      ((c) == '-') || ((c) == '+') || ((c) == '/') || ((c) == ':') || \
-      ((c) == '.'))
-
 #ifdef __SYMBIAN32__
 EXPORT_C
 #endif
 
+
 gboolean
 priv_gst_structure_append_to_gstring (const GstStructure * structure,
     GString * s)
 {
   GstStructureField *field;
-  guint i;
+  guint i, len;
 
   g_return_val_if_fail (s != NULL, FALSE);
 
   g_string_append (s, g_quark_to_string (structure->name));
-  for (i = 0; i < structure->fields->len; i++) {
+  len = structure->fields->len;
+  for (i = 0; i < len; i++) {
     char *t;
     GType type;
 
@@ -1750,7 +1832,7 @@
     g_string_append_len (s, "=(", 2);
     g_string_append (s, gst_structure_to_abbr (type));
     g_string_append_c (s, ')');
-    g_string_append (s, GST_STR_NULL (t));
+    g_string_append (s, t == NULL ? "NULL" : t);
     g_free (t);
   }
 
@@ -1764,13 +1846,18 @@
  *
  * Converts @structure to a human-readable string representation.
  *
+ * For debugging purposes its easier to do something like this:
+ * |[
+ * GST_LOG ("structure is %" GST_PTR_FORMAT, structure);
+ * ]|
+ * This prints the structure in human readble form.
+ *
  * Returns: a pointer to string allocated by g_malloc(). g_free() after
  * usage.
  */
 #ifdef __SYMBIAN32__
 EXPORT_C
 #endif
-
 gchar *
 gst_structure_to_string (const GstStructure * structure)
 {
@@ -1799,7 +1886,8 @@
  * THIS FUNCTION MODIFIES THE STRING AND DETECTS INSIDE A NONTERMINATED STRING
  */
 static gboolean
-gst_structure_parse_string (gchar * s, gchar ** end, gchar ** next)
+gst_structure_parse_string (gchar * s, gchar ** end, gchar ** next,
+    gboolean unescape)
 {
   gchar *w;
 
@@ -1815,21 +1903,32 @@
     return ret;
   }
 
-  w = s;
-  s++;
-  while (*s != '"') {
-    if (*s == 0)
-      return FALSE;
-
-    if (*s == '\\') {
+  if (unescape) {
+    w = s;
+    s++;
+    while (*s != '"') {
+      if (G_UNLIKELY (*s == 0))
+        return FALSE;
+      if (G_UNLIKELY (*s == '\\'))
+        s++;
+      *w = *s;
+      w++;
       s++;
     }
-
-    *w = *s;
-    w++;
+    s++;
+  } else {
+    /* Find the closing quotes */
     s++;
+    while (*s != '"') {
+      if (G_UNLIKELY (*s == 0))
+        return FALSE;
+      if (G_UNLIKELY (*s == '\\'))
+        s++;
+      s++;
+    }
+    s++;
+    w = s;
   }
-  s++;
 
   *end = w;
   *next = s;
@@ -1976,7 +2075,7 @@
 {
   char *s = str;
 
-  while (GST_ASCII_IS_STRING (*s)) {
+  while (G_LIKELY (GST_ASCII_IS_STRING (*s))) {
     s++;
   }
 
@@ -1999,23 +2098,24 @@
   while (g_ascii_isspace (*s) || (s[0] == '\\' && g_ascii_isspace (s[1])))
     s++;
   name = s;
-  if (!gst_structure_parse_simple_string (s, &name_end))
+  if (G_UNLIKELY (!gst_structure_parse_simple_string (s, &name_end)))
     return FALSE;
 
   s = name_end;
   while (g_ascii_isspace (*s) || (s[0] == '\\' && g_ascii_isspace (s[1])))
     s++;
 
-  if (*s != '=')
+  if (G_UNLIKELY (*s != '='))
     return FALSE;
   s++;
 
   c = *name_end;
-  *name_end = 0;
+  *name_end = '\0';
   field->name = g_quark_from_string (name);
   *name_end = c;
 
-  if (!gst_structure_parse_value (s, &s, &field->value, G_TYPE_INVALID))
+  if (G_UNLIKELY (!gst_structure_parse_value (s, &s, &field->value,
+              G_TYPE_INVALID)))
     return FALSE;
 
   *after = s;
@@ -2038,7 +2138,6 @@
   #endif
   GType type = default_type;
 
-
   s = str;
   while (g_ascii_isspace (*s))
     s++;
@@ -2046,18 +2145,16 @@
   /* check if there's a (type_name) 'cast' */
   type_name = NULL;
   if (*s == '(') {
-    type = G_TYPE_INVALID;
-
     s++;
     while (g_ascii_isspace (*s))
       s++;
     type_name = s;
-    if (!gst_structure_parse_simple_string (s, &type_end))
+    if (G_UNLIKELY (!gst_structure_parse_simple_string (s, &type_end)))
       return FALSE;
     s = type_end;
     while (g_ascii_isspace (*s))
       s++;
-    if (*s != ')')
+    if (G_UNLIKELY (*s != ')'))
       return FALSE;
     s++;
     while (g_ascii_isspace (*s))
@@ -2068,7 +2165,7 @@
     type = gst_structure_gtype_from_abbr (type_name);
     *type_end = c;
 
-    if (type == G_TYPE_INVALID)
+    if (G_UNLIKELY (type == G_TYPE_INVALID))
       return FALSE;
   }
 
@@ -2082,12 +2179,8 @@
     ret = gst_structure_parse_array (s, &s, value, type);
   } else {
     value_s = s;
-    if (!gst_structure_parse_string (s, &value_end, &s))
-      return FALSE;
-
-    c = *value_end;
-    *value_end = 0;
-    if (type == G_TYPE_INVALID) {
+
+    if (G_UNLIKELY (type == G_TYPE_INVALID)) {
     #ifdef __SYMBIAN32__
          GType try_types[] =
          { G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_INVALID, G_TYPE_STRING };
@@ -2099,6 +2192,12 @@
 
          int i;
         #endif
+      if (G_UNLIKELY (!gst_structure_parse_string (s, &value_end, &s, TRUE)))
+        return FALSE;
+      /* Set NULL terminator for deserialization */
+      c = *value_end;
+      *value_end = '\0';
+
       for (i = 0; i < G_N_ELEMENTS (try_types); i++) {
         g_value_init (value, try_types[i]);
         ret = gst_value_deserialize (value, value_s);
@@ -2109,7 +2208,16 @@
     } else {
       g_value_init (value, type);
 
+      if (G_UNLIKELY (!gst_structure_parse_string (s, &value_end, &s,
+                  (type != G_TYPE_STRING))))
+        return FALSE;
+      /* Set NULL terminator for deserialization */
+      c = *value_end;
+      *value_end = '\0';
+
       ret = gst_value_deserialize (value, value_s);
+      if (G_UNLIKELY (!ret))
+        g_value_unset (value);
     }
     *value_end = c;
   }
@@ -2144,7 +2252,7 @@
   char *r;
   char save;
   GstStructure *structure = NULL;
-  GstStructureField field = { 0 };
+  GstStructureField field;
 
   g_return_val_if_fail (string != NULL, NULL);
 
@@ -2157,17 +2265,17 @@
     r++;
 
   name = r;
-  if (!gst_structure_parse_string (r, &w, &r)) {
+  if (G_UNLIKELY (!gst_structure_parse_string (r, &w, &r, TRUE))) {
     GST_WARNING ("Failed to parse structure string");
     goto error;
   }
 
   save = *w;
-  *w = 0;
+  *w = '\0';
   structure = gst_structure_empty_new (name);
   *w = save;
 
-  if (structure == NULL)
+  if (G_UNLIKELY (structure == NULL))
     goto error;
 
   do {
@@ -2183,7 +2291,7 @@
       /* accept \0 as end delimiter */
       break;
     }
-    if (*r != ',') {
+    if (G_UNLIKELY (*r != ',')) {
       GST_WARNING ("Failed to find delimiter, r=%s", r);
       goto error;
     }
@@ -2193,10 +2301,9 @@
       r++;
 
     memset (&field, 0, sizeof (field));
-    if (!gst_structure_parse_field (r, &r, &field))
+    if (G_UNLIKELY (!gst_structure_parse_field (r, &r, &field)))
       goto error;
     gst_structure_set_field (structure, &field);
-
   } while (TRUE);
 
   if (end)
@@ -2483,16 +2590,13 @@
     const GValue *list_value;
     int i, n;
     const GValue *best = NULL;
-    GValue best_diff = { 0 };
-    GValue cur_diff = { 0 };
-    GValue target = { 0 };
-    gboolean res = FALSE;
-
-    g_value_init (&best_diff, GST_TYPE_FRACTION);
-    g_value_init (&cur_diff, GST_TYPE_FRACTION);
-    g_value_init (&target, GST_TYPE_FRACTION);
-
-    gst_value_set_fraction (&target, target_numerator, target_denominator);
+    gdouble target;
+    gdouble cur_diff;
+    gdouble best_diff = G_MAXDOUBLE;
+
+    target = (gdouble) target_numerator / (gdouble) target_denominator;
+
+    GST_DEBUG ("target %g, best %g", target, best_diff);
 
     best = NULL;
 
@@ -2500,28 +2604,295 @@
     for (i = 0; i < n; i++) {
       list_value = gst_value_list_get_value (value, i);
       if (G_VALUE_TYPE (list_value) == GST_TYPE_FRACTION) {
-
-        if (gst_value_compare (list_value, &target) == GST_VALUE_LESS_THAN)
-          gst_value_fraction_subtract (&cur_diff, &target, list_value);
-        else
-          gst_value_fraction_subtract (&cur_diff, list_value, &target);
-
-        if (!best
-            || gst_value_compare (&cur_diff,
-                &best_diff) == GST_VALUE_LESS_THAN) {
+        gint num, denom;
+        gdouble list_double;
+
+        num = gst_value_get_fraction_numerator (list_value);
+        denom = gst_value_get_fraction_denominator (list_value);
+
+        list_double = ((gdouble) num / (gdouble) denom);
+        cur_diff = target - list_double;
+
+        GST_DEBUG ("curr diff %g, list %g", cur_diff, list_double);
+
+        if (cur_diff < 0)
+          cur_diff = -cur_diff;
+
+        if (!best || cur_diff < best_diff) {
+          GST_DEBUG ("new best %g", list_double);
           best = list_value;
-          g_value_copy (&cur_diff, &best_diff);
+          best_diff = cur_diff;
         }
       }
     }
     if (best != NULL) {
       gst_structure_set_value (structure, field_name, best);
-      res = TRUE;
+      return TRUE;
     }
-    g_value_unset (&best_diff);
-    g_value_unset (&cur_diff);
-    g_value_unset (&target);
-    return res;
   }
+
   return FALSE;
 }
+
+/* our very own version of G_VALUE_LCOPY that allows NULL return locations
+ * (useful for message parsing functions where the return location is user
+ * supplied and the user may pass NULL if the value isn't of interest) */
+#define GST_VALUE_LCOPY(value, var_args, flags, __error, fieldname)           \
+G_STMT_START {                                                                \
+  const GValue *_value = (value);                                             \
+  guint _flags = (flags);                                                     \
+  GType _value_type = G_VALUE_TYPE (_value);                                  \
+  GTypeValueTable *_vtable = g_type_value_table_peek (_value_type);           \
+  gchar *_lcopy_format = _vtable->lcopy_format;                               \
+  GTypeCValue _cvalues[G_VALUE_COLLECT_FORMAT_MAX_LENGTH] = { { 0, }, };      \
+  guint _n_values = 0;                                                        \
+                                                                              \
+  while (*_lcopy_format != '\0') {                                            \
+    g_assert (*_lcopy_format == G_VALUE_COLLECT_POINTER);                     \
+    _cvalues[_n_values++].v_pointer = va_arg ((var_args), gpointer);          \
+    _lcopy_format++;                                                          \
+  }                                                                           \
+  if (_n_values == 2 && !!_cvalues[0].v_pointer != !!_cvalues[1].v_pointer) { \
+    *(__error) = g_strdup_printf ("either all or none of the return "         \
+        "locations for field '%s' need to be NULL", fieldname);               \
+  } else if (_cvalues[0].v_pointer != NULL) {                                 \
+    *(__error) = _vtable->lcopy_value (_value, _n_values, _cvalues, _flags);  \
+  }                                                                           \
+} G_STMT_END
+
+/**
+ * gst_structure_get_valist:
+ * @structure: a #GstStructure
+ * @first_fieldname: the name of the first field to read
+ * @args: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * valist-variant of gst_structure_get(). Look at the documentation of
+ * gst_structure_get() for more details.
+ *
+ * Returns: TRUE, or FALSE if there was a problem reading any of the fields
+ *
+ * Since: 0.10.24
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_structure_get_valist (GstStructure * structure,
+    const char *first_fieldname, va_list args)
+{
+  const char *field_name;
+  GType expected_type = G_TYPE_INVALID;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_fieldname != NULL, FALSE);
+
+  field_name = first_fieldname;
+  while (field_name) {
+    const GValue *val = NULL;
+    gchar *err = NULL;
+
+    expected_type = va_arg (args, GType);
+
+    val = gst_structure_get_value (structure, field_name);
+
+    if (val == NULL)
+      goto no_such_field;
+
+    if (G_VALUE_TYPE (val) != expected_type)
+      goto wrong_type;
+
+    GST_VALUE_LCOPY (val, args, 0, &err, field_name);
+    if (err) {
+      g_warning ("%s: %s", G_STRFUNC, err);
+      g_free (err);
+      return FALSE;
+    }
+
+    field_name = va_arg (args, const gchar *);
+  }
+
+  return TRUE;
+
+/* ERRORS */
+no_such_field:
+  {
+    GST_WARNING ("Expected field '%s' in structure: %" GST_PTR_FORMAT,
+        field_name, structure);
+    return FALSE;
+  }
+wrong_type:
+  {
+    GST_WARNING ("Expected field '%s' in structure to be of type '%s', but "
+        "field was of type '%s': %" GST_PTR_FORMAT, field_name,
+        GST_STR_NULL (g_type_name (expected_type)),
+        G_VALUE_TYPE_NAME (gst_structure_get_value (structure, field_name)),
+        structure);
+    return FALSE;
+  }
+}
+
+/**
+ * gst_structure_id_get_valist:
+ * @structure: a #GstStructure
+ * @first_field_id: the quark of the first field to read
+ * @args: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * valist-variant of gst_structure_id_get(). Look at the documentation of
+ * gst_structure_id_get() for more details.
+ *
+ * Returns: TRUE, or FALSE if there was a problem reading any of the fields
+ *
+ * Since: 0.10.24
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_structure_id_get_valist (GstStructure * structure, GQuark first_field_id,
+    va_list args)
+{
+  GQuark field_id;
+  GType expected_type = G_TYPE_INVALID;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_field_id != 0, FALSE);
+
+  field_id = first_field_id;
+  while (field_id) {
+    const GValue *val = NULL;
+    gchar *err = NULL;
+
+    expected_type = va_arg (args, GType);
+
+    val = gst_structure_id_get_value (structure, field_id);
+
+    if (val == NULL)
+      goto no_such_field;
+
+    if (G_VALUE_TYPE (val) != expected_type)
+      goto wrong_type;
+
+    GST_VALUE_LCOPY (val, args, 0, &err, g_quark_to_string (field_id));
+    if (err) {
+      g_warning ("%s: %s", G_STRFUNC, err);
+      g_free (err);
+      return FALSE;
+    }
+
+    field_id = va_arg (args, GQuark);
+  }
+
+  return TRUE;
+
+/* ERRORS */
+no_such_field:
+  {
+    GST_WARNING ("Expected field '%s' in structure: %" GST_PTR_FORMAT,
+        GST_STR_NULL (g_quark_to_string (field_id)), structure);
+    return FALSE;
+  }
+wrong_type:
+  {
+    GST_WARNING ("Expected field '%s' in structure to be of type '%s', but "
+        "field was of type '%s': %" GST_PTR_FORMAT,
+        g_quark_to_string (field_id),
+        GST_STR_NULL (g_type_name (expected_type)),
+        G_VALUE_TYPE_NAME (gst_structure_id_get_value (structure, field_id)),
+        structure);
+    return FALSE;
+  }
+}
+
+/**
+ * gst_structure_get:
+ * @structure: a #GstStructure
+ * @first_fieldname: the name of the first field to read
+ * @...: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * Variable arguments should be in the form field name, field type
+ * (as a GType), pointer(s) to a variable(s) to hold the return value(s).
+ * The last variable argument should be NULL.
+ *
+ * For refcounted (mini)objects you will acquire your own reference which
+ * you must release with a suitable _unref() when no longer needed. For
+ * strings and boxed types you will acquire a copy which you will need to
+ * release with either g_free() or the suiteable function for the boxed type.
+ *
+ * Returns: FALSE if there was a problem reading any of the fields (e.g.
+ *     because the field requested did not exist, or was of a type other
+ *     than the type specified), otherwise TRUE.
+ *
+ * Since: 0.10.24
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_structure_get (GstStructure * structure, const char *first_fieldname, ...)
+{
+  gboolean ret;
+  va_list args;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_fieldname != NULL, FALSE);
+
+  va_start (args, first_fieldname);
+  ret = gst_structure_get_valist (structure, first_fieldname, args);
+  va_end (args);
+
+  return ret;
+}
+
+/**
+ * gst_structure_id_get:
+ * @structure: a #GstStructure
+ * @first_field_id: the quark of the first field to read
+ * @...: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * Variable arguments should be in the form field id quark, field type
+ * (as a GType), pointer(s) to a variable(s) to hold the return value(s).
+ * The last variable argument should be NULL (technically it should be a
+ * 0 quark, but we require NULL so compilers that support it can check for
+ * the NULL terminator and warn if it's not there).
+ *
+ * This function is just like gst_structure_get() only that it is slightly
+ * more efficient since it saves the string-to-quark lookup in the global
+ * quark hashtable.
+ *
+ * For refcounted (mini)objects you will acquire your own reference which
+ * you must release with a suitable _unref() when no longer needed. For
+ * strings and boxed types you will acquire a copy which you will need to
+ * release with either g_free() or the suiteable function for the boxed type.
+ *
+ * Returns: FALSE if there was a problem reading any of the fields (e.g.
+ *     because the field requested did not exist, or was of a type other
+ *     than the type specified), otherwise TRUE.
+ *
+ * Since: 0.10.24
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_structure_id_get (GstStructure * structure, GQuark first_field_id, ...)
+{
+  gboolean ret;
+  va_list args;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_field_id != 0, FALSE);
+
+  va_start (args, first_field_id);
+  ret = gst_structure_id_get_valist (structure, first_field_id, args);
+  va_end (args);
+
+  return ret;
+}