From c35d47127bb9425ad46a245d6444baab8088cf40 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 14 Jul 2020 22:36:36 -0400 Subject: [PATCH] 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: --- gst/gst_private.h | 3 +- gst/gstcaps.c | 76 ++++++++++++++++------ gst/gstcaps.h | 2 + gst/gststructure.c | 115 ++++++++++++++++++++++++--------- gst/gststructure.h | 19 +++++- hooks/pre-commit.hook | 2 +- tests/check/gst/capslist.h | 3 + tests/check/gst/gstcaps.c | 9 +++ tests/check/gst/gststructure.c | 36 +++++++++++ 9 files changed, 211 insertions(+), 54 deletions(-) diff --git a/gst/gst_private.h b/gst/gst_private.h index 7f45f07fe7f..739981ac213 100644 --- a/gst/gst_private.h +++ b/gst/gst_private.h @@ -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, diff --git a/gst/gstcaps.c b/gst/gstcaps.c index 5ee88bbc8dc..680eb572162 100644 --- a/gst/gstcaps.c +++ b/gst/gstcaps.c @@ -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: - * |[ - * 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: + * |[ + * 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) { diff --git a/gst/gstcaps.h b/gst/gstcaps.h index 5032ea927e0..c38671fed8a 100644 --- a/gst/gstcaps.h +++ b/gst/gstcaps.h @@ -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; diff --git a/gst/gststructure.c b/gst/gststructure.c index 870fd4f0061..0039f38f1e2 100644 --- a/gst/gststructure.c +++ b/gst/gststructure.c @@ -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,20 +2149,42 @@ priv__gst_structure_append_template_to_gstring (GQuark field_id, return TRUE; } +static gchar * +structure_serialize (const GstStructure * structure, GstSerializeFlags flags) +{ + GString *s; + + /* NOTE: This function is potentially called by the debug system, + * so any calls to gst_log() (and GST_DEBUG(), GST_LOG(), etc.) + * should be careful to avoid recursion. This includes any functions + * called by gst_structure_to_string. In particular, calls should + * not use the GST_PTR_FORMAT extension. */ + + g_return_val_if_fail (structure != NULL, NULL); + + /* we estimate a minimum size based on the number of fields in order to + * 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, 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: - * |[ - * GST_LOG ("structure is %" GST_PTR_FORMAT, structure); + * For debugging purposes its easier to do something like this: |[ 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. + * 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 * @@ -2152,22 +2194,33 @@ priv__gst_structure_append_template_to_gstring (GQuark field_id, gchar * gst_structure_to_string (const GstStructure * structure) { - GString *s; - - /* NOTE: This function is potentially called by the debug system, - * so any calls to gst_log() (and GST_DEBUG(), GST_LOG(), etc.) - * should be careful to avoid recursion. This includes any functions - * called by gst_structure_to_string. In particular, calls should - * not use the GST_PTR_FORMAT extension. */ - - g_return_val_if_fail (structure != NULL, NULL); + return structure_serialize (structure, GST_SERIALIZE_FLAG_BACKWARD_COMPAT); +} - /* we estimate a minimum size based on the number of fields in order to - * 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); - return g_string_free (s, FALSE); +/** + * 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 * diff --git a/gst/gststructure.h b/gst/gststructure.h index a267bc8735a..38ea2095c0e 100644 --- a/gst/gststructure.h +++ b/gst/gststructure.h @@ -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)) @@ -329,7 +343,10 @@ gboolean gst_structure_get_list (GstStructure * const gchar * fieldname, GValueArray ** array); GST_API -gchar * gst_structure_to_string (const GstStructure * structure) G_GNUC_MALLOC; +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, diff --git a/hooks/pre-commit.hook b/hooks/pre-commit.hook index c1a7a0d69a3..a295d2b42a6 100755 --- a/hooks/pre-commit.hook +++ b/hooks/pre-commit.hook @@ -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. diff --git a/tests/check/gst/capslist.h b/tests/check/gst/capslist.h index 0991fa89fd7..9988dbc8918 100644 --- a/tests/check/gst/capslist.h +++ b/tests/check/gst/capslist.h @@ -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" }; diff --git a/tests/check/gst/gstcaps.c b/tests/check/gst/gstcaps.c index 0335e8bbdf7..d9931d6ca9d 100644 --- a/tests/check/gst/gstcaps.c +++ b/tests/check/gst/gstcaps.c @@ -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)); diff --git a/tests/check/gst/gststructure.c b/tests/check/gst/gststructure.c index b87163600b9..814c4a41048 100644 --- a/tests/check/gst/gststructure.c +++ b/tests/check/gst/gststructure.c @@ -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);