Skip to content

Commit

Permalink
gst: Add new structure/caps/_to_string using the brackets for nesting
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
thiblahute committed Dec 4, 2020
1 parent 330450e commit c35d471
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 53 deletions.
3 changes: 2 additions & 1 deletion gst/gst_private.h
Expand Up @@ -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,
Expand Down
76 changes: 55 additions & 21 deletions gst/gstcaps.c
Expand Up @@ -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.)
Expand Down Expand Up @@ -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 ';' */
Expand All @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions gst/gstcaps.h
Expand Up @@ -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;
Expand Down
115 changes: 85 additions & 30 deletions gst/gststructure.c
Expand Up @@ -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).
*
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

Expand All @@ -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)) {
Expand Down Expand Up @@ -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:
* |[<!-- language="C" -->
* GST_LOG ("structure is %" GST_PTR_FORMAT, structure);
* 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.
* 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
*
Expand All @@ -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
Expand Down Expand Up @@ -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
*
Expand Down
17 changes: 17 additions & 0 deletions gst/gststructure.h
Expand Up @@ -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))
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion 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.
Expand Down
3 changes: 3 additions & 0 deletions tests/check/gst/capslist.h
Expand Up @@ -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"
};
Expand Down
9 changes: 9 additions & 0 deletions tests/check/gst/gstcaps.c
Expand Up @@ -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));

Expand Down

0 comments on commit c35d471

Please sign in to comment.