gstvalue: Add (de)serialize of G_TYPE_STRV

This allows setting strv properties from gst-launch-1.0, such as uris in
uriplaylistbin.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8438>
This commit is contained in:
Xavier Claessens 2025-02-10 11:54:18 -05:00 committed by GStreamer Marge Bot
parent a54568a93c
commit e06e977304
2 changed files with 174 additions and 8 deletions

View File

@ -8133,6 +8133,115 @@ gst_value_transform_object_string (const GValue * src_value,
dest_value->data[0].v_pointer = str;
}
/*********
* GStrv *
*********/
static gchar *
gst_value_serialize_strv (const GValue * value)
{
const gchar **strv = g_value_get_boxed (value);
GString *str = g_string_new ("<");
while (*strv != NULL) {
const gchar *s = *strv;
/* Add separator if it's not the first string */
if (str->len > 1)
g_string_append_c (str, ',');
g_string_append_c (str, '\"');
/* Escape \ to \\ and " to \" */
while (*s != '\0') {
if (*s == '\"' || *s == '\\')
g_string_append_c (str, '\\');
g_string_append_c (str, *s);
s++;
}
g_string_append_c (str, '\"');
strv++;
}
g_string_append_c (str, '>');
return g_string_free (str, FALSE);
}
static gboolean
gst_value_deserialize_strv (GValue * dest, const gchar * s)
{
/* If it's not starting with '<' assume it's a simple comma separated list
* with no escaping. Otherwise assume the format <"foo","bar"> with spaces
* allowed between delimiters and \ for escaping. */
if (*s != '<') {
g_value_take_boxed (dest, g_strsplit (s, ",", -1));
return TRUE;
}
s++;
while (g_ascii_isspace (*s))
s++;
GPtrArray *strv = g_ptr_array_new_with_free_func (g_free);
while (*s != '>') {
if (*s != '\"')
goto error;
s++;
/* Find string end and check if we need to unescape it */
gboolean escaped = FALSE;
const gchar *start = s;
while (*s != '\"') {
if (*s == '\\') {
escaped = TRUE;
s++;
}
if (*s == '\0')
goto error;
s++;
}
s++;
/* Always copy the whole string and unescape inplace */
gchar *substr = g_strndup (start, s - start - 1);
if (escaped) {
gchar *p1 = substr;
const gchar *p2 = substr;
while (*p2 != '\0') {
if (*p2 == '\\')
p2++;
*p1 = *p2;
p1++;
p2++;
}
*p1 = '\0';
}
g_ptr_array_add (strv, substr);
while (g_ascii_isspace (*s))
s++;
if (*s == ',') {
s++;
while (g_ascii_isspace (*s))
s++;
}
}
g_ptr_array_add (strv, NULL);
g_value_take_boxed (dest, g_ptr_array_free (strv, FALSE));
return TRUE;
error:
g_ptr_array_free (strv, TRUE);
return FALSE;
}
static GTypeInfo _info = {
0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL,
};
@ -8363,10 +8472,12 @@ _priv_gst_value_initialize (void)
REGISTER_SERIALIZATION (gst_bitmask_get_type (), bitmask);
REGISTER_SERIALIZATION (gst_structure_get_type (), structure);
REGISTER_SERIALIZATION (gst_flagset_get_type (), flagset);
REGISTER_SERIALIZATION (G_TYPE_GTYPE, gtype);
REGISTER_SERIALIZATION_NO_COMPARE (gst_segment_get_type (), segment);
REGISTER_SERIALIZATION_NO_COMPARE (gst_caps_features_get_type (),
caps_features);
REGISTER_SERIALIZATION_NO_COMPARE (G_TYPE_STRV, strv);
REGISTER_SERIALIZATION_COMPARE_ONLY (gst_allocation_params_get_type (),
allocation_params);
@ -8374,26 +8485,18 @@ _priv_gst_value_initialize (void)
REGISTER_SERIALIZATION_CONST (G_TYPE_DOUBLE, double);
REGISTER_SERIALIZATION_CONST (G_TYPE_FLOAT, float);
REGISTER_SERIALIZATION_CONST (G_TYPE_STRING, string);
REGISTER_SERIALIZATION_CONST (G_TYPE_BOOLEAN, boolean);
REGISTER_SERIALIZATION_CONST (G_TYPE_ENUM, enum);
REGISTER_SERIALIZATION_CONST (G_TYPE_FLAGS, gflags);
REGISTER_SERIALIZATION_CONST (G_TYPE_INT, int);
REGISTER_SERIALIZATION_CONST (G_TYPE_INT64, int64);
REGISTER_SERIALIZATION_CONST (G_TYPE_LONG, long);
REGISTER_SERIALIZATION_CONST (G_TYPE_UINT, uint);
REGISTER_SERIALIZATION_CONST (G_TYPE_UINT64, uint64);
REGISTER_SERIALIZATION_CONST (G_TYPE_ULONG, ulong);
REGISTER_SERIALIZATION_CONST (G_TYPE_UCHAR, uchar);
REGISTER_SERIALIZATION (G_TYPE_GTYPE, gtype);
REGISTER_SERIALIZATION_WITH_PSPEC (gst_value_list_get_type (), value_list);
REGISTER_SERIALIZATION_WITH_PSPEC (gst_value_array_get_type (), value_array);

View File

@ -4001,6 +4001,68 @@ GST_START_TEST (test_serialize_deserialize_sample)
GST_END_TEST;
GST_START_TEST (test_serialize_deserialize_strv)
{
GValue v = G_VALUE_INIT;
g_value_init (&v, G_TYPE_STRV);
fail_if (gst_value_deserialize (&v, "<"));
fail_if (gst_value_deserialize (&v, "< foo"));
fail_if (gst_value_deserialize (&v, "< \"foo"));
fail_if (gst_value_deserialize (&v, "< \"foo\\"));
fail_if (gst_value_deserialize (&v, "< \"foo\""));
fail_if (gst_value_deserialize (&v, "< \"foo\","));
struct
{
const gchar *str;
const gchar *deserialized[3];
const gchar *serialized;
} tests[] = {
{"", {NULL}, "<>"},
{"foo", {"foo", NULL}, "<\"foo\">"},
{"foo ", {"foo ", NULL}, "<\"foo \">"},
{"foo ,", {"foo ", "", NULL}, "<\"foo \",\"\">"},
{"foo , ", {"foo ", " ", NULL}, "<\"foo \",\" \">"},
{"foo,bar", {"foo", "bar", NULL}, "<\"foo\",\"bar\">"},
{"<>", {NULL}, "<>"},
{"<\"\">", {"", NULL}, "<\"\">"},
{"< \" \" > ", {" ", NULL}, "<\" \">"},
{"<\"foo\",> ", {"foo", NULL}, "<\"foo\">"},
{"<\"foo\" , > ", {"foo", NULL}, "<\"foo\">"},
{"<\"foo\",\"bar\"> ", {"foo", "bar", NULL}, "<\"foo\",\"bar\">"},
{"<\"foo\" , \"bar\"> ", {"foo", "bar", NULL}, "<\"foo\",\"bar\">"},
{"<\"\\\"\\\\,<>\">", {"\"\\,<>", NULL}, "<\"\\\"\\\\,<>\">"},
};
for (int i = 0; i < G_N_ELEMENTS (tests); i++) {
const gchar *str = tests[i].str;
const gchar *const *deserialized = tests[i].deserialized;
/* Deserialize */
if (!gst_value_deserialize (&v, str))
fail ("Failed to deserialize %dth '%s'", i, str);
const gchar *const *strv = g_value_get_boxed (&v);
if (!g_strv_equal (strv, deserialized)) {
gchar *strv_str = g_strjoinv (", ", (gchar **) strv);
gchar *expected_str = g_strjoinv (", ", (gchar **) deserialized);
fail ("Deserialized %dth '%s' to '%s', expected '%s'", i, str, strv_str,
expected_str);
}
/* Re-serialize */
gchar *serialized = gst_value_serialize (&v);
if (!g_str_equal (serialized, tests[i].serialized))
fail ("Serialized %dth '%s' to '%s', expected '%s'", i, str, serialized,
tests[i].serialized);
g_free (serialized);
g_value_reset (&v);
}
}
GST_END_TEST;
static Suite *
gst_value_suite (void)
{
@ -4063,6 +4125,7 @@ gst_value_suite (void)
tcase_add_test (tc_chain, test_serialize_deserialize_caps_features);
tcase_add_test (tc_chain, test_serialize_deserialize_tag_list);
tcase_add_test (tc_chain, test_serialize_deserialize_sample);
tcase_add_test (tc_chain, test_serialize_deserialize_strv);
return s;
}