Commit cdd47a37 authored by Tim-Philipp Müller's avatar Tim-Philipp Müller
Browse files

structure: add gst_structure_*_get*() vararg functions

Add a bunch of vararg getter convenience functions to complement
the vararg setter functions, and a basic unit test. Fixes #534208.

API: gst_structure_get()
API: gst_structure_id_get()
API: gst_structure_get_valist()
API: gst_structure_id_get_valist()
parent 26b20127
......@@ -2012,8 +2012,12 @@ gst_structure_get_name
gst_structure_has_name
gst_structure_set_name
gst_structure_get_name_id
gst_structure_id_get
gst_structure_id_get_valist
gst_structure_id_get_value
gst_structure_id_set_value
gst_structure_get
gst_structure_get_valist
gst_structure_get_value
gst_structure_set_value
gst_structure_set
......
......@@ -2342,3 +2342,250 @@ gst_structure_fixate_field_nearest_fraction (GstStructure * structure,
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
* @valist: 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
*/
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
* @valist: 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
*/
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
*/
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
*/
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;
}
......@@ -132,6 +132,21 @@ void gst_structure_id_set_valist (GstStructure
GQuark fieldname,
va_list varargs);
gboolean gst_structure_get_valist (GstStructure *structure,
const char *first_fieldname,
va_list args);
gboolean gst_structure_get (GstStructure *structure,
const char *first_fieldname,
...) G_GNUC_NULL_TERMINATED;
gboolean gst_structure_id_get_valist (GstStructure *structure,
GQuark first_field_id,
va_list args);
gboolean gst_structure_id_get (GstStructure *structure,
GQuark first_field_id,
...) G_GNUC_NULL_TERMINATED;
G_CONST_RETURN GValue * gst_structure_id_get_value (const GstStructure *structure,
GQuark field);
......
......@@ -433,6 +433,108 @@ GST_START_TEST (test_empty_string_fields)
GST_END_TEST;
GST_START_TEST (test_vararg_getters)
{
GstStructure *s;
GstBuffer *buf, *buf2;
gboolean ret;
GstCaps *caps, *caps2;
gdouble d;
gint64 i64;
gchar *c;
gint i, num, denom;
buf = gst_buffer_new_and_alloc (3);
GST_BUFFER_DATA (buf)[0] = 0xf0;
GST_BUFFER_DATA (buf)[1] = 0x66;
GST_BUFFER_DATA (buf)[2] = 0x0d;
caps = gst_caps_new_simple ("video/x-foo", NULL);
s = gst_structure_new ("test", "int", G_TYPE_INT, 12345678, "string",
G_TYPE_STRING, "Hello World!", "buf", GST_TYPE_BUFFER, buf, "caps",
GST_TYPE_CAPS, caps, "int64", G_TYPE_INT64, G_GINT64_CONSTANT (-99),
"double", G_TYPE_DOUBLE, G_MAXDOUBLE, "frag", GST_TYPE_FRACTION, 39, 14,
NULL);
/* first the plain one */
ret = gst_structure_get (s, "double", G_TYPE_DOUBLE, &d, "string",
G_TYPE_STRING, &c, "caps", GST_TYPE_CAPS, &caps2, "buf",
GST_TYPE_BUFFER, &buf2, "frag", GST_TYPE_FRACTION, &num, &denom, "int",
G_TYPE_INT, &i, "int64", G_TYPE_INT64, &i64, NULL);
fail_unless (ret);
fail_unless_equals_string (c, "Hello World!");
fail_unless_equals_int (i, 12345678);
fail_unless_equals_float (d, G_MAXDOUBLE);
fail_unless_equals_int (num, 39);
fail_unless_equals_int (denom, 14);
fail_unless (i64 == -99);
fail_unless (caps == caps2);
fail_unless (buf == buf2);
/* expected failures */
ASSERT_CRITICAL (gst_structure_get (s, NULL, G_TYPE_INT, &i, NULL));
fail_if (gst_structure_get (s, "int", G_TYPE_INT, &i, "double",
G_TYPE_FLOAT, &d, NULL));
fail_if (gst_structure_get (s, "int", G_TYPE_INT, &i, "dooble",
G_TYPE_DOUBLE, &d, NULL));
g_free (c);
c = NULL;
gst_caps_unref (caps2);
caps2 = NULL;
gst_buffer_unref (buf2);
buf2 = NULL;
/* and now the _id variant */
ret = gst_structure_id_get (s, g_quark_from_static_string ("double"),
G_TYPE_DOUBLE, &d, g_quark_from_static_string ("string"), G_TYPE_STRING,
&c, g_quark_from_static_string ("caps"), GST_TYPE_CAPS, &caps2,
g_quark_from_static_string ("buf"), GST_TYPE_BUFFER, &buf2,
g_quark_from_static_string ("int"), G_TYPE_INT, &i,
g_quark_from_static_string ("int64"), G_TYPE_INT64, &i64, NULL);
fail_unless (ret);
fail_unless_equals_string (c, "Hello World!");
fail_unless_equals_int (i, 12345678);
fail_unless_equals_float (d, G_MAXDOUBLE);
fail_unless (i64 == -99);
fail_unless (caps == caps2);
fail_unless (buf == buf2);
/* expected failures */
ASSERT_CRITICAL (gst_structure_get (s, 0, G_TYPE_INT, &i, NULL));
fail_if (gst_structure_id_get (s, g_quark_from_static_string ("int"),
G_TYPE_INT, &i, g_quark_from_static_string ("double"), G_TYPE_FLOAT,
&d, NULL));
fail_if (gst_structure_id_get (s, g_quark_from_static_string ("int"),
G_TYPE_INT, &i, g_quark_from_static_string ("dooble"), G_TYPE_DOUBLE,
&d, NULL));
g_free (c);
gst_caps_unref (caps2);
gst_buffer_unref (buf2);
/* finally make sure NULL as return location is handled gracefully */
ret = gst_structure_get (s, "double", G_TYPE_DOUBLE, NULL, "string",
G_TYPE_STRING, NULL, "caps", GST_TYPE_CAPS, NULL, "buf",
GST_TYPE_BUFFER, NULL, "int", G_TYPE_INT, &i, "frag", GST_TYPE_FRACTION,
NULL, NULL, "int64", G_TYPE_INT64, &i64, NULL);
ASSERT_WARNING (gst_structure_get (s, "frag", GST_TYPE_FRACTION, NULL,
&denom, NULL));
ASSERT_WARNING (gst_structure_get (s, "frag", GST_TYPE_FRACTION, &num,
NULL, NULL));
/* clean up */
gst_caps_unref (caps);
gst_buffer_unref (buf);
gst_structure_free (s);
}
GST_END_TEST;
static Suite *
gst_structure_suite (void)
{
......@@ -451,6 +553,7 @@ gst_structure_suite (void)
tcase_add_test (tc_chain, test_structure_nested);
tcase_add_test (tc_chain, test_structure_nested_from_and_to_string);
tcase_add_test (tc_chain, test_empty_string_fields);
tcase_add_test (tc_chain, test_vararg_getters);
return s;
}
......
......@@ -866,6 +866,7 @@ EXPORTS
gst_structure_foreach
gst_structure_free
gst_structure_from_string
gst_structure_get
gst_structure_get_boolean
gst_structure_get_clock_time
gst_structure_get_date
......@@ -880,11 +881,14 @@ EXPORTS
gst_structure_get_string
gst_structure_get_type
gst_structure_get_uint
gst_structure_get_valist
gst_structure_get_value
gst_structure_has_field
gst_structure_has_field_typed
gst_structure_has_name
gst_structure_id_empty_new
gst_structure_id_get
gst_structure_id_get_valist
gst_structure_id_get_value
gst_structure_id_new
gst_structure_id_set
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment