gst: Add new structure/caps/_to_string using the brackets for nesting

This adds `gst_structure_serialize` and `gst_caps_serialize` which use
the newly introduced bracket delimiters for nested structures.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/532>
This commit is contained in:
Thibault Saunier 2020-07-14 22:36:36 -04:00
parent 330450ef93
commit c35d47127b
9 changed files with 214 additions and 57 deletions

View file

@ -183,7 +183,8 @@ G_GNUC_INTERNAL void _priv_gst_element_state_changed (GstElement *element,
G_GNUC_INTERNAL
gboolean priv_gst_structure_append_to_gstring (const GstStructure * structure,
GString * s);
GString * s,
GstSerializeFlags flags);
G_GNUC_INTERNAL
gboolean priv__gst_structure_append_template_to_gstring (GQuark field_id,
const GValue *value,

View file

@ -2291,29 +2291,13 @@ gst_caps_fixate (GstCaps * caps)
/* utility */
/**
* gst_caps_to_string:
* @caps: a #GstCaps
*
* Converts @caps to a string representation. This string representation
* can be converted back to a #GstCaps by gst_caps_from_string().
*
* For debugging purposes its easier to do something like this:
* |[<!-- language="C" -->
* GST_LOG ("caps are %" GST_PTR_FORMAT, caps);
* ]|
* This prints the caps in human readable form.
*
* The current implementation of serialization will lead to unexpected results
* when there are nested #GstCaps / #GstStructure deeper than one level.
*
* Returns: (transfer full): a newly allocated string representing @caps.
*/
gchar *
gst_caps_to_string (const GstCaps * caps)
static gchar *
caps_serialize (const GstCaps * caps, GstSerializeFlags flags)
{
guint i, slen, clen;
GString *s;
gboolean nested_structs_brackets =
!(flags & GST_SERIALIZE_FLAG_BACKWARD_COMPAT);
/* NOTE: This function is potentially called by the debug system,
* so any calls to gst_log() (and GST_DEBUG(), GST_LOG(), etc.)
@ -2366,7 +2350,8 @@ gst_caps_to_string (const GstCaps * caps)
priv_gst_caps_features_append_to_gstring (features, s);
g_string_append_c (s, ')');
}
priv_gst_structure_append_to_gstring (structure, s);
priv_gst_structure_append_to_gstring (structure, s,
nested_structs_brackets);
}
if (s->len && s->str[s->len - 1] == ';') {
/* remove latest ';' */
@ -2375,6 +2360,55 @@ gst_caps_to_string (const GstCaps * caps)
return g_string_free (s, FALSE);
}
/**
* gst_caps_to_string:
* @caps: a #GstCaps
*
* Converts @caps to a string representation. This string representation
* can be converted back to a #GstCaps by gst_caps_from_string().
*
* For debugging purposes its easier to do something like this:
* |[<!-- language="C" -->
* GST_LOG ("caps are %" GST_PTR_FORMAT, caps);
* ]|
* This prints the caps in human readable form.
*
* The current implementation of serialization will lead to unexpected results
* when there are nested #GstCaps / #GstStructure deeper than one level.
*
* Returns: (transfer full): a newly allocated string representing @caps.
*/
gchar *
gst_caps_to_string (const GstCaps * caps)
{
return caps_serialize (caps, GST_SERIALIZE_FLAG_BACKWARD_COMPAT);
}
/**
* gst_caps_serialize:
* @caps: a #GstCaps
* @flags: a #GstSerializeFlags
*
* Converts @caps to a string representation. This string representation can be
* converted back to a #GstCaps by gst_caps_from_string().
*
* This prints the caps in human readable form.
*
* This version of the caps serialization function introduces support for nested
* structures and caps but the resulting strings won't be parsable with
* GStreamer prior to 1.20 unless #GST_SERIALIZE_FLAG_BACKWARD_COMPAT is passed
* as @flag.
*
* Returns: (transfer full): a newly allocated string representing @caps.
*
* Since: 1.20
*/
gchar *
gst_caps_serialize (const GstCaps * caps, GstSerializeFlags flags)
{
return caps_serialize (caps, flags);
}
static gboolean
gst_caps_from_string_inplace (GstCaps * caps, const gchar * string)
{

View file

@ -576,6 +576,8 @@ GstCaps * gst_caps_fixate (GstCaps *caps) G_GNUC_WARN_U
GST_API
gchar * gst_caps_to_string (const GstCaps *caps) G_GNUC_MALLOC;
GST_API
gchar * gst_caps_serialize (const GstCaps *caps, GstSerializeFlags flags) G_GNUC_MALLOC;
GST_API
GstCaps * gst_caps_from_string (const gchar *string) G_GNUC_WARN_UNUSED_RESULT;

View file

@ -99,7 +99,7 @@
* - [GstValueList](GST_TYPE_LIST) are inside "less and greater than" (`<` and
* `>`). For example `a-structure, list=<1, 2, 3>
*
* Structures are delimited either by a null character `\0` or a semicolumn `;`
* Structures are delimited either by a null character `\0` or a semicolon `;`
* the latter allowing to store multiple structures in the same string (see
* #GstCaps).
*
@ -119,15 +119,16 @@
* a-struct, nested=(GstStructure)"nested-struct, nested=true"
* ```
*
* Since 1.20, nested structures and caps can be specified using brackets
* (`[` and `]`), for example:
* Since 1.20, nested structures and caps can be specified using brackets (`[`
* and `]`), for example:
*
* ```
* a-struct, nested=[nested-struct, nested=true]
* ```
*
* > *note*: For backward compatility reason, the serialization functions won't
* > use that synthax.
* > *note*: gst_structure_to_string() won't use that syntax for backward
* > compatibility reason, gst_structure_serialize() has been added for
* > that purpose.
*/
#ifdef HAVE_CONFIG_H
@ -2026,16 +2027,18 @@ gst_structure_value_get_generic_type (const GValue * val)
gboolean
priv_gst_structure_append_to_gstring (const GstStructure * structure,
GString * s)
GString * s, GstSerializeFlags flags)
{
GstStructureField *field;
guint i, len;
gboolean nested_structs_brackets =
!(flags & GST_SERIALIZE_FLAG_BACKWARD_COMPAT);
g_return_val_if_fail (s != NULL, FALSE);
len = GST_STRUCTURE_LEN (structure);
for (i = 0; i < len; i++) {
char *t;
gchar *t = NULL;
GType type;
field = GST_STRUCTURE_FIELD (structure, i);
@ -2044,7 +2047,9 @@ priv_gst_structure_append_to_gstring (const GstStructure * structure,
t = _priv_gst_value_serialize_any_list (&field->value, "< ", " >", FALSE);
} else if (G_VALUE_TYPE (&field->value) == GST_TYPE_LIST) {
t = _priv_gst_value_serialize_any_list (&field->value, "{ ", " }", FALSE);
} else {
} else if (!nested_structs_brackets
|| (G_VALUE_TYPE (&field->value) != GST_TYPE_STRUCTURE
&& G_VALUE_TYPE (&field->value) != GST_TYPE_CAPS)) {
t = gst_value_serialize (&field->value);
}
@ -2056,7 +2061,22 @@ priv_gst_structure_append_to_gstring (const GstStructure * structure,
g_string_append_len (s, "=(", 2);
g_string_append (s, _priv_gst_value_gtype_to_abbr (type));
g_string_append_c (s, ')');
if (t) {
if (nested_structs_brackets
&& G_VALUE_TYPE (&field->value) == GST_TYPE_STRUCTURE) {
const GstStructure *substruct = gst_value_get_structure (&field->value);
g_string_append_c (s, '[');
g_string_append (s, g_quark_to_string (substruct->name));
priv_gst_structure_append_to_gstring (substruct, s, flags);
g_string_append_c (s, ']');
} else if (nested_structs_brackets
&& G_VALUE_TYPE (&field->value) == GST_TYPE_CAPS) {
const GstCaps *subcaps = gst_value_get_caps (&field->value);
gchar *capsstr = gst_caps_serialize (subcaps, flags);
g_string_append_printf (s, "[%s]", capsstr);
g_free (capsstr);
} else if (t) {
g_string_append (s, t);
g_free (t);
} else if (G_TYPE_CHECK_VALUE_TYPE (&field->value, G_TYPE_POINTER)) {
@ -2129,28 +2149,8 @@ priv__gst_structure_append_template_to_gstring (GQuark field_id,
return TRUE;
}
/**
* gst_structure_to_string:
* @structure: a #GstStructure
*
* Converts @structure to a human-readable string representation.
*
* For debugging purposes its easier to do something like this:
* |[<!-- language="C" -->
* GST_LOG ("structure is %" GST_PTR_FORMAT, structure);
* ]|
* This prints the structure in human readable form.
*
* The current implementation of serialization will lead to unexpected results
* when there are nested #GstCaps / #GstStructure deeper than one level.
*
* Free-function: g_free
*
* Returns: (transfer full): a pointer to string allocated by g_malloc().
* g_free() after usage.
*/
gchar *
gst_structure_to_string (const GstStructure * structure)
static gchar *
structure_serialize (const GstStructure * structure, GstSerializeFlags flags)
{
GString *s;
@ -2166,8 +2166,61 @@ gst_structure_to_string (const GstStructure * structure)
* avoid unnecessary reallocs within GString */
s = g_string_sized_new (STRUCTURE_ESTIMATED_STRING_LEN (structure));
g_string_append (s, g_quark_to_string (structure->name));
priv_gst_structure_append_to_gstring (structure, s);
priv_gst_structure_append_to_gstring (structure, s, flags);
return g_string_free (s, FALSE);
}
/**
* gst_structure_to_string:
* @structure: a #GstStructure
*
* Converts @structure to a human-readable string representation.
*
* For debugging purposes its easier to do something like this: |[<!--
* language="C" --> GST_LOG ("structure is %" GST_PTR_FORMAT, structure);
* ]|
* This prints the structure in human readable form.
*
* This function will lead to unexpected results when there are nested #GstCaps
* / #GstStructure deeper than one level, you should user
* gst_structure_serialize() instead for those cases.
*
* Free-function: g_free
*
* Returns: (transfer full): a pointer to string allocated by g_malloc().
* g_free() after usage.
*/
gchar *
gst_structure_to_string (const GstStructure * structure)
{
return structure_serialize (structure, GST_SERIALIZE_FLAG_BACKWARD_COMPAT);
}
/**
* gst_structure_serialize:
* @structure: a #GstStructure
* @flags: The flags to use to serialize structure
*
* Converts @structure to a human-readable string representation.
*
* This version of the caps serialization function introduces support for nested
* structures and caps but the resulting strings won't be parsable with
* GStreamer prior to 1.20 unless #GST_SERIALIZE_FLAG_BACKWARD_COMPAT is passed
* as @flag.
*
* Free-function: g_free
*
* Returns: (transfer full): a pointer to string allocated by g_malloc().
* g_free() after usage.
*
* Since: 1.20
*/
gchar *
gst_structure_serialize (const GstStructure * structure,
GstSerializeFlags flags)
{
return structure_serialize (structure, flags);
}
static gboolean
@ -2306,7 +2359,9 @@ priv_gst_structure_parse_fields (gchar * str, gchar ** end,
* where parsing ended will be returned.
*
* The current implementation of serialization will lead to unexpected results
* when there are nested #GstCaps / #GstStructure deeper than one level.
* when there are nested #GstCaps / #GstStructure deeper than one level unless
* the gst_structure_serialize() function is used (without
* #GST_SERIALIZE_FLAG_BACKWARD_COMPAT)
*
* Free-function: gst_structure_free
*

View file

@ -32,6 +32,20 @@ GST_API GType _gst_structure_type;
typedef struct _GstStructure GstStructure;
/**
* GstSerializeFlags:
* @GST_SERIALIZE_FLAG_NONE: No special flags specified.
* @GST_SERIALIZE_FLAG_BACKWARD_COMPAT: Serialize using the old format for
* nested structures.
*
* Since: 1.20
*/
typedef enum
{
GST_SERIALIZE_FLAG_NONE = 0,
GST_SERIALIZE_FLAG_BACKWARD_COMPAT = (1 << 0),
} GstSerializeFlags;
#define GST_TYPE_STRUCTURE (_gst_structure_type)
#define GST_IS_STRUCTURE(object) ((object) && (GST_STRUCTURE(object)->type == GST_TYPE_STRUCTURE))
#define GST_STRUCTURE_CAST(object) ((GstStructure *)(object))
@ -330,6 +344,9 @@ gboolean gst_structure_get_list (GstStructure *
GValueArray ** array);
GST_API
gchar * gst_structure_to_string (const GstStructure * structure) G_GNUC_MALLOC;
GST_API
gchar * gst_structure_serialize (const GstStructure * structure,
GstSerializeFlags flags) G_GNUC_MALLOC;
GST_API
GstStructure * gst_structure_from_string (const gchar * string,

View file

@ -1,6 +1,6 @@
#!/bin/sh
#
# Check that the code follows a consistent code style
# Check that the code follows a consistant code style
#
# Check for existence of indent, and error out if not present.

View file

@ -32,6 +32,9 @@ static const gchar *caps_list[] = {
/* Some random checks */
"video/x-raw, format = (string) { I420, Y42B, Y444 }, framerate = (fraction) [1/MAX, MAX], width = (int) [ 1, MAX ], height = (int) [ 1, MAX ]",
/* Some nesting check */
"caps, nested=(GstCaps)[c1;c2;c3,deeply-nested-field=[deep-structure, test=true]]",
"ANY",
"EMPTY"
};

View file

@ -43,7 +43,16 @@ GST_START_TEST (test_from_string)
"Could not convert caps back to string %s\n", caps_list[i]);
caps2 = gst_caps_from_string (to_str);
fail_if (caps2 == NULL, "Could not create caps from string %s\n", to_str);
g_free (to_str);
fail_unless (gst_caps_is_equal (caps, caps));
fail_unless (gst_caps_is_equal (caps, caps2));
gst_caps_unref (caps2);
to_str = gst_caps_serialize (caps, GST_SERIALIZE_FLAG_NONE);
fail_if (to_str == NULL,
"Could not convert caps back to string %s\n", caps_list[i]);
caps2 = gst_caps_from_string (to_str);
fail_unless (gst_caps_is_equal (caps, caps));
fail_unless (gst_caps_is_equal (caps, caps2));

View file

@ -723,6 +723,41 @@ GST_START_TEST (test_structure_nested_from_and_to_string)
GST_END_TEST;
GST_START_TEST (test_serialize_nested_structures)
{
GstStructure *s;
const gchar *str1;
gchar *str2, *end = NULL;
str1 = "main"
", main-sub1=(structure)[type-b, machine-type=(int)0;]"
", main-sub2=(structure)[type-a, plugin-filename=(string)\"/home/user/lib/lib\\ with\\ spaces.dll\", machine-type=(int)1;]"
", main-sub3=(structure)[type-b, plugin-filename=(string)/home/user/lib/lib_no_spaces.so, machine-type=(int)1;]"
";";
s = gst_structure_from_string (str1, &end);
fail_unless (s != NULL);
GST_DEBUG ("not parsed part : %s", end);
fail_unless (*end == '\0');
fail_unless (gst_structure_n_fields (s) == 3);
fail_unless (gst_structure_has_field_typed (s, "main-sub1",
GST_TYPE_STRUCTURE));
str2 = gst_structure_serialize (s, GST_SERIALIZE_FLAG_NONE);
fail_unless (str2 != NULL);
fail_unless (g_str_equal (str1, str2));
g_free (str2);
gst_structure_free (s);
}
GST_END_TEST;
GST_START_TEST (test_vararg_getters)
{
GstStructure *s;
@ -963,6 +998,7 @@ gst_structure_suite (void)
tcase_add_test (tc_chain, test_is_subset_superset_extra_values);
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_serialize_nested_structures);
tcase_add_test (tc_chain, test_vararg_getters);
tcase_add_test (tc_chain, test_foreach);
tcase_add_test (tc_chain, test_map_in_place);