sdp: support multiple rid parameters

As specified formally in RFC8851

Each rid description is placed in its own caps field in the structure.
This is very similar to the already existing extmap-$id sdp<->caps
transformations that already exists.

The mapping is as follows:

  a=rid:0 direction ';'-separated params

where direction is either 'send' or 'recv'

gets put into a caps structure like so:

   rid-0=(string)<"direction","param1","param2",etc>

If there are no rid parameters then the caps structure is generated to
only contain the direction as a single string like:

   rid-0=(string)direction

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1760>
This commit is contained in:
Matthew Waters 2021-09-01 14:02:29 +10:00 committed by GStreamer Marge Bot
parent f8016687a4
commit 207769edbb
2 changed files with 326 additions and 0 deletions

View file

@ -3991,6 +3991,52 @@ gst_sdp_media_set_media_from_caps (const GstCaps * caps, GstSDPMedia * media)
continue; continue;
} }
/* rid values */
if (g_str_has_prefix (fname, "rid-")) {
const char *rid_id = &fname[strlen ("rid-")];
const GValue *arr;
if (!rid_id || !*rid_id)
continue;
if ((fval = gst_structure_get_string (s, fname))) {
char *rid_val = g_strdup_printf ("%s %s", rid_id, fval);
gst_sdp_media_add_attribute (media, "rid", rid_val);
g_free (rid_val);
} else if ((arr = gst_structure_get_value (s, fname))
&& GST_VALUE_HOLDS_ARRAY (arr)
&& gst_value_array_get_size (arr) > 1) {
const gchar *direction, *param;
GString *str;
guint i, n;
str = g_string_new (NULL);
g_string_append_printf (str, "%s ", rid_id);
n = gst_value_array_get_size (arr);
for (i = 0; i < n; i++) {
const GValue *val = gst_value_array_get_value (arr, i);
if (i == 0) {
direction = g_value_get_string (val);
g_string_append_printf (str, "%s", direction);
} else {
param = g_value_get_string (val);
if (i == 1)
g_string_append_c (str, ' ');
else
g_string_append_c (str, ';');
g_string_append_printf (str, "%s", param);
}
}
gst_sdp_media_add_attribute (media, "rid", str->str);
g_string_free (str, TRUE);
} else {
GST_WARNING ("caps field %s is an unsupported format", fname);
}
continue;
}
if ((fval = gst_structure_get_string (s, fname))) { if ((fval = gst_structure_get_string (s, fname))) {
/* "profile" is our internal representation of the notion of /* "profile" is our internal representation of the notion of
@ -4170,6 +4216,8 @@ sdp_add_attributes_to_caps (GArray * attributes, GstCaps * caps)
continue; continue;
if (!strcmp (key, "extmap")) if (!strcmp (key, "extmap"))
continue; continue;
if (!strcmp (key, "rid"))
continue;
/* string must be valid UTF8 */ /* string must be valid UTF8 */
if (!g_utf8_validate (attr->value, -1, NULL)) if (!g_utf8_validate (attr->value, -1, NULL))
@ -4295,6 +4343,138 @@ gst_sdp_media_add_extmap_attributes (GArray * attributes, GstCaps * caps)
return GST_SDP_OK; return GST_SDP_OK;
} }
/* parses RID SDP attributes (RFC8851) into caps */
static GstSDPResult
gst_sdp_media_add_rid_attributes (GArray * attributes, GstCaps * caps)
{
const gchar *rid;
char *p, *to_free;
guint i;
GstStructure *s;
g_return_val_if_fail (attributes != NULL, GST_SDP_EINVAL);
g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
g_return_val_if_fail (gst_caps_is_writable (caps), GST_SDP_EINVAL);
s = gst_caps_get_structure (caps, 0);
for (i = 0; i < attributes->len; i++) {
GstSDPAttribute *attr;
const char *direction, *params, *id;
const char *tmp;
attr = &g_array_index (attributes, GstSDPAttribute, i);
if (strcmp (attr->key, "rid") != 0)
continue;
rid = attr->value;
/* p is now of the format id dir ;-separated-params */
to_free = p = g_strdup (rid);
PARSE_STRING (p, " ", id);
if (id == NULL || *id == '\0') {
GST_ERROR ("Invalid rid \'%s\'", to_free);
goto next;
}
tmp = id;
while (*tmp && (*tmp == '-' || *tmp == '_' || g_ascii_isalnum (*tmp)))
tmp++;
if (*tmp != '\0') {
GST_ERROR ("Invalid rid-id \'%s\'", id);
goto next;
}
SKIP_SPACES (p);
PARSE_STRING (p, " ", direction);
if (direction == NULL || *direction == '\0') {
direction = p;
params = NULL;
} else {
SKIP_SPACES (p);
params = p;
}
if (direction == NULL || *direction == '\0'
|| (g_strcmp0 (direction, "send") != 0
&& g_strcmp0 (direction, "recv") != 0)) {
GST_ERROR ("Invalid rid direction \'%s\'", p);
goto next;
}
if (params && *params != '\0') {
GValue arr = G_VALUE_INIT;
GValue val = G_VALUE_INIT;
gchar *key;
#if !defined(GST_DISABLE_DEBUG)
GString *debug_params = g_string_new (NULL);
int i = 0;
#endif
key = g_strdup_printf ("rid-%s", id);
g_value_init (&arr, GST_TYPE_ARRAY);
g_value_init (&val, G_TYPE_STRING);
g_value_set_string (&val, direction);
gst_value_array_append_and_take_value (&arr, &val);
val = (GValue) G_VALUE_INIT;
while (*p) {
const char *param;
gboolean done = FALSE;
PARSE_STRING (p, ";", param);
if (param) {
} else if (*p) {
param = p;
done = TRUE;
} else {
break;
}
g_value_init (&val, G_TYPE_STRING);
g_value_set_string (&val, param);
gst_value_array_append_and_take_value (&arr, &val);
val = (GValue) G_VALUE_INIT;
#if !defined(GST_DISABLE_DEBUG)
if (i++ > 0)
g_string_append_c (debug_params, ',');
g_string_append (debug_params, param);
#endif
if (done)
break;
}
gst_structure_take_value (s, key, &arr);
arr = (GValue) G_VALUE_INIT;
#if !defined(GST_DISABLE_DEBUG)
{
char *debug_str = g_string_free (debug_params, FALSE);
GST_DEBUG ("adding caps: %s=<%s,%s>", key, direction, debug_str);
g_free (debug_str);
}
#endif
g_free (key);
} else {
gchar *key;
key = g_strdup_printf ("rid-%s", id);
gst_structure_set (s, key, G_TYPE_STRING, direction, NULL);
GST_DEBUG ("adding caps: %s=%s", key, direction);
g_free (key);
}
next:
g_clear_pointer (&to_free, g_free);
}
return GST_SDP_OK;
}
/** /**
* gst_sdp_message_attributes_to_caps: * gst_sdp_message_attributes_to_caps:
* @msg: a #GstSDPMessage * @msg: a #GstSDPMessage
@ -4371,6 +4551,11 @@ gst_sdp_media_attributes_to_caps (const GstSDPMedia * media, GstCaps * caps)
res = gst_sdp_media_add_extmap_attributes (media->attributes, caps); res = gst_sdp_media_add_extmap_attributes (media->attributes, caps);
} }
if (res == GST_SDP_OK) {
/* parse media rid fields */
res = gst_sdp_media_add_rid_attributes (media->attributes, caps);
}
done: done:
if (mikey) if (mikey)
gst_mikey_message_unref (mikey); gst_mikey_message_unref (mikey);

View file

@ -182,6 +182,19 @@ static const gchar caps_application_raptor_fec_pt_110[] =
"raptor-scheme-id=(string)1, kmax=(string)8192, t=(string)128, p=(string)A, repair-window=(string)200000, " "raptor-scheme-id=(string)1, kmax=(string)8192, t=(string)128, p=(string)A, repair-window=(string)200000, "
"a-mid=(string)R1"; "a-mid=(string)R1";
static const gchar caps_multiple_rid[] =
"application/x-unknown, media=(string)video, payload=(int)96, "
"clock-rate=(int)90000, encoding-name=(string)VP8, "
"rid-h=(string)\"send\", "
"rid-m=(string)\"send\", "
"rid-l=(string)\"send\", "
"a-simulcast=(string)\"send\\ h\\;m\\;l\"";
static const gchar caps_rid_params[] =
"application/x-unknown, media=(string)video, payload=(int)96, "
"clock-rate=(int)90000, encoding-name=(string)VP8, "
"rid-0=(string)<\"send\",\"max-width=1920\",\"max-height=1080\">, "
"rid-1=(string)<\"send\",\"max-width=1280\",\"max-height=720\">";
/* *INDENT-ON* */ /* *INDENT-ON* */
@ -758,6 +771,132 @@ GST_START_TEST (media_from_caps_h264_with_profile_asymmetry_allowed)
gst_sdp_message_free (message); gst_sdp_message_free (message);
} }
GST_END_TEST
GST_START_TEST (caps_multiple_rid_parse)
{
GstSDPMedia media, media2;
GstCaps *caps, *expected;
/* BUG: gst_sdp_media_add_attributes_to_caps() would only set a single rid
* string attribute key/value in caps */
memset (&media, 0, sizeof (media));
fail_unless_equals_int (gst_sdp_media_init (&media), GST_SDP_OK);
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_set_media (&media, "video"));
fail_unless_equals_int (GST_SDP_OK, gst_sdp_media_add_format (&media, "96"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "rtpmap", "96 VP8/90000"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "rid", "h send"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "rid", "m send"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "rid", "l send"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "simulcast", "send h;m;l"));
expected = gst_caps_from_string (caps_multiple_rid);
fail_unless (gst_caps_is_fixed (expected));
fail_unless (expected != NULL);
caps = gst_sdp_media_get_caps_from_media (&media, 96);
fail_unless (caps != NULL);
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_attributes_to_caps (&media, caps));
fail_unless (gst_caps_is_fixed (caps));
GST_DEBUG (" caps %" GST_PTR_FORMAT, caps);
GST_DEBUG ("expected %" GST_PTR_FORMAT, expected);
fail_unless (gst_caps_is_equal (caps, expected));
memset (&media2, 0, sizeof (media2));
fail_unless_equals_int (GST_SDP_OK, gst_sdp_media_init (&media2));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_set_media_from_caps (caps, &media2));
gst_clear_caps (&caps);
caps = gst_sdp_media_get_caps_from_media (&media, 96);
fail_unless (caps != NULL);
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_attributes_to_caps (&media, caps));
fail_unless (gst_caps_is_fixed (caps));
GST_DEBUG (" caps %" GST_PTR_FORMAT, caps);
GST_DEBUG ("expected %" GST_PTR_FORMAT, expected);
fail_unless (gst_caps_is_equal (caps, expected));
gst_sdp_media_uninit (&media);
gst_sdp_media_uninit (&media2);
gst_clear_caps (&caps);
gst_clear_caps (&expected);
}
GST_END_TEST
GST_START_TEST (caps_multiple_rid_parse_with_params)
{
GstSDPMedia media, media2;
GstCaps *caps, *expected;
/* BUG: gst_sdp_media_add_attributes_to_caps() would only set a single rid
* string attribute key/value in caps */
memset (&media, 0, sizeof (media));
fail_unless_equals_int (gst_sdp_media_init (&media), GST_SDP_OK);
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_set_media (&media, "video"));
fail_unless_equals_int (GST_SDP_OK, gst_sdp_media_add_format (&media, "96"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "rtpmap", "96 VP8/90000"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "rid",
"0 send max-width=1920;max-height=1080"));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_add_attribute (&media, "rid",
"1 send max-width=1280;max-height=720"));
expected = gst_caps_from_string (caps_rid_params);
fail_unless (gst_caps_is_fixed (expected));
fail_unless (expected != NULL);
caps = gst_sdp_media_get_caps_from_media (&media, 96);
fail_unless (caps != NULL);
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_attributes_to_caps (&media, caps));
fail_unless (gst_caps_is_fixed (caps));
GST_DEBUG (" caps %" GST_PTR_FORMAT, caps);
GST_DEBUG ("expected %" GST_PTR_FORMAT, expected);
fail_unless (gst_caps_is_equal (caps, expected));
memset (&media2, 0, sizeof (media2));
fail_unless_equals_int (GST_SDP_OK, gst_sdp_media_init (&media2));
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_set_media_from_caps (caps, &media2));
gst_clear_caps (&caps);
caps = gst_sdp_media_get_caps_from_media (&media, 96);
fail_unless (caps != NULL);
fail_unless_equals_int (GST_SDP_OK,
gst_sdp_media_attributes_to_caps (&media, caps));
fail_unless (gst_caps_is_fixed (caps));
GST_DEBUG (" caps %" GST_PTR_FORMAT, caps);
GST_DEBUG ("expected %" GST_PTR_FORMAT, expected);
fail_unless (gst_caps_is_equal (caps, expected));
gst_sdp_media_uninit (&media);
gst_sdp_media_uninit (&media2);
gst_clear_caps (&caps);
gst_clear_caps (&expected);
}
GST_END_TEST GST_END_TEST
/* /*
* End of test cases * End of test cases
@ -785,6 +924,8 @@ sdp_suite (void)
tcase_add_test (tc_chain, media_from_caps_extmap_pt_100); tcase_add_test (tc_chain, media_from_caps_extmap_pt_100);
tcase_add_test (tc_chain, tcase_add_test (tc_chain,
media_from_caps_h264_with_profile_asymmetry_allowed); media_from_caps_h264_with_profile_asymmetry_allowed);
tcase_add_test (tc_chain, caps_multiple_rid_parse);
tcase_add_test (tc_chain, caps_multiple_rid_parse_with_params);
return s; return s;
} }