mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 17:20:36 +00:00
sdp: Add support for parsing the extmap attribute from caps and storing inside caps
The extmap attribute allows mapping RTP extension header IDs to well-known RTP extension header specifications. See RFC8285 for details. We store the extmap attribute either as string in the caps extmap-X=extensionname where X is the integer extension header ID, or as 3-tuple of strings extmap-X=<direction,extensionname,extensionattributes> where direction or extensionattributes are allowed to be the empty string. Both formats are allowed because usually only the extension name is given and it's much simpler to handle in caps.
This commit is contained in:
parent
6d49814932
commit
1381e3e2be
2 changed files with 271 additions and 1 deletions
|
@ -3553,6 +3553,8 @@ gst_sdp_media_add_rtcp_fb_attributes_from_media (const GstSDPMedia * media,
|
|||
*
|
||||
* a=fmtp:(payload) (param)[=(value)];...
|
||||
*
|
||||
* Note that the extmap attribute is set only by gst_sdp_media_attributes_to_caps().
|
||||
*
|
||||
* Returns: a #GstCaps, or %NULL if an error happened
|
||||
*
|
||||
* Since: 1.8
|
||||
|
@ -3752,6 +3754,8 @@ no_rate:
|
|||
*
|
||||
* a=rtcp-fb:(payload) (param1) [param2]...
|
||||
*
|
||||
* a=extmap:(id)[/direction] (extensionname) (extensionattributes)
|
||||
*
|
||||
* Returns: a #GstSDPResult.
|
||||
*
|
||||
* Since: 1.8
|
||||
|
@ -3829,7 +3833,7 @@ gst_sdp_media_set_media_from_caps (const GstCaps * caps, GstSDPMedia * media)
|
|||
}
|
||||
}
|
||||
|
||||
/* collect all other properties and add them to fmtp or attributes */
|
||||
/* collect all other properties and add them to fmtp, extmap or attributes */
|
||||
fmtp = g_string_new ("");
|
||||
g_string_append_printf (fmtp, "%d ", caps_pt);
|
||||
first = TRUE;
|
||||
|
@ -3889,6 +3893,63 @@ gst_sdp_media_set_media_from_caps (const GstCaps * caps, GstSDPMedia * media)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* extmap */
|
||||
if (g_str_has_prefix (fname, "extmap-")) {
|
||||
gchar *endptr;
|
||||
guint id = strtoull (fname + 7, &endptr, 10);
|
||||
const GValue *arr;
|
||||
|
||||
if (*endptr != '\0' || id == 0 || id == 15 || id > 9999)
|
||||
continue;
|
||||
|
||||
if ((fval = gst_structure_get_string (s, fname))) {
|
||||
gchar *extmap = g_strdup_printf ("%u %s", id, fval);
|
||||
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
||||
g_free (extmap);
|
||||
} else if ((arr = gst_structure_get_value (s, fname))
|
||||
&& G_VALUE_HOLDS (arr, GST_TYPE_ARRAY)
|
||||
&& gst_value_array_get_size (arr) == 3) {
|
||||
const GValue *val;
|
||||
const gchar *direction, *extensionname, *extensionattributes;
|
||||
|
||||
val = gst_value_array_get_value (arr, 0);
|
||||
direction = g_value_get_string (val);
|
||||
|
||||
val = gst_value_array_get_value (arr, 1);
|
||||
extensionname = g_value_get_string (val);
|
||||
|
||||
val = gst_value_array_get_value (arr, 2);
|
||||
extensionattributes = g_value_get_string (val);
|
||||
|
||||
if (!extensionname || *extensionname == '\0')
|
||||
continue;
|
||||
|
||||
if (direction && *direction != '\0' && extensionattributes
|
||||
&& *extensionattributes != '\0') {
|
||||
gchar *extmap =
|
||||
g_strdup_printf ("%u/%s %s %s", id, direction, extensionname,
|
||||
extensionattributes);
|
||||
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
||||
g_free (extmap);
|
||||
} else if (direction && *direction != '\0') {
|
||||
gchar *extmap =
|
||||
g_strdup_printf ("%u/%s %s", id, direction, extensionname);
|
||||
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
||||
g_free (extmap);
|
||||
} else if (extensionattributes && *extensionattributes != '\0') {
|
||||
gchar *extmap = g_strdup_printf ("%u %s %s", id, extensionname,
|
||||
extensionattributes);
|
||||
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
||||
g_free (extmap);
|
||||
} else {
|
||||
gchar *extmap = g_strdup_printf ("%u %s", id, extensionname);
|
||||
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
||||
g_free (extmap);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((fval = gst_structure_get_string (s, fname))) {
|
||||
g_string_append_printf (fmtp, "%s%s=%s", first ? "" : ";", fname, fval);
|
||||
first = FALSE;
|
||||
|
@ -4056,6 +4117,8 @@ sdp_add_attributes_to_caps (GArray * attributes, GstCaps * caps)
|
|||
continue;
|
||||
if (!strcmp (key, "key-mgmt"))
|
||||
continue;
|
||||
if (!strcmp (key, "extmap"))
|
||||
continue;
|
||||
|
||||
/* string must be valid UTF8 */
|
||||
if (!g_utf8_validate (attr->value, -1, NULL))
|
||||
|
@ -4075,6 +4138,112 @@ sdp_add_attributes_to_caps (GArray * attributes, GstCaps * caps)
|
|||
return GST_SDP_OK;
|
||||
}
|
||||
|
||||
static GstSDPResult
|
||||
gst_sdp_media_add_extmap_attributes (GArray * attributes, GstCaps * caps)
|
||||
{
|
||||
const gchar *extmap;
|
||||
gchar *p, *tmp, *to_free;
|
||||
guint id, 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);
|
||||
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
|
||||
for (i = 0; i < attributes->len; i++) {
|
||||
GstSDPAttribute *attr;
|
||||
const gchar *direction, *extensionname, *extensionattributes;
|
||||
|
||||
attr = &g_array_index (attributes, GstSDPAttribute, i);
|
||||
if (strcmp (attr->key, "extmap") != 0)
|
||||
continue;
|
||||
|
||||
extmap = attr->value;
|
||||
|
||||
/* p is now of the format id[/direction] extensionname [extensionattributes] */
|
||||
to_free = p = g_strdup (extmap);
|
||||
|
||||
id = strtoul (p, &tmp, 10);
|
||||
if (id == 0 || id == 15 || id > 9999 || (*tmp != ' ' && *tmp != '/')) {
|
||||
GST_ERROR ("Invalid extmap '%s'", to_free);
|
||||
goto next;
|
||||
} else if (*tmp == '/') {
|
||||
p = tmp;
|
||||
p++;
|
||||
|
||||
PARSE_STRING (p, " ", direction);
|
||||
|
||||
/* Invalid format */
|
||||
if (direction == NULL || *direction == '\0') {
|
||||
GST_ERROR ("Invalid extmap '%s'", to_free);
|
||||
goto next;
|
||||
}
|
||||
} else {
|
||||
/* At the space */
|
||||
p = tmp;
|
||||
direction = "";
|
||||
}
|
||||
|
||||
SKIP_SPACES (p);
|
||||
|
||||
tmp = strstr (p, " ");
|
||||
if (tmp == NULL) {
|
||||
extensionname = p;
|
||||
extensionattributes = "";
|
||||
} else {
|
||||
extensionname = p;
|
||||
*tmp = '\0';
|
||||
p = tmp + 1;
|
||||
SKIP_SPACES (p);
|
||||
extensionattributes = p;
|
||||
}
|
||||
|
||||
if (extensionname == NULL || *extensionname == '\0') {
|
||||
GST_ERROR ("Invalid extmap '%s'", to_free);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (*direction != '\0' || *extensionattributes != '\0') {
|
||||
GValue arr = G_VALUE_INIT;
|
||||
GValue val = G_VALUE_INIT;
|
||||
gchar *key;
|
||||
|
||||
key = g_strdup_printf ("extmap-%u", 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_value (&arr, &val);
|
||||
|
||||
g_value_set_string (&val, extensionname);
|
||||
gst_value_array_append_value (&arr, &val);
|
||||
|
||||
g_value_set_string (&val, extensionattributes);
|
||||
gst_value_array_append_value (&arr, &val);
|
||||
|
||||
gst_structure_set_value (s, key, &arr);
|
||||
g_value_unset (&val);
|
||||
g_value_unset (&arr);
|
||||
GST_DEBUG ("adding caps: %s=<%s,%s,%s>", key, direction, extensionname,
|
||||
extensionattributes);
|
||||
g_free (key);
|
||||
} else {
|
||||
gchar *key;
|
||||
|
||||
key = g_strdup_printf ("extmap-%u", id);
|
||||
gst_structure_set (s, key, G_TYPE_STRING, extensionname, NULL);
|
||||
GST_DEBUG ("adding caps: %s=%s", key, extensionname);
|
||||
g_free (key);
|
||||
}
|
||||
|
||||
next:
|
||||
g_free (to_free);
|
||||
}
|
||||
return GST_SDP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_sdp_message_attributes_to_caps:
|
||||
* @msg: a #GstSDPMessage
|
||||
|
@ -4105,6 +4274,11 @@ gst_sdp_message_attributes_to_caps (const GstSDPMessage * msg, GstCaps * caps)
|
|||
|
||||
res = sdp_add_attributes_to_caps (msg->attributes, caps);
|
||||
|
||||
if (res == GST_SDP_OK) {
|
||||
/* parse global extmap field */
|
||||
res = gst_sdp_media_add_extmap_attributes (msg->attributes, caps);
|
||||
}
|
||||
|
||||
done:
|
||||
if (mikey)
|
||||
gst_mikey_message_unref (mikey);
|
||||
|
@ -4141,6 +4315,11 @@ gst_sdp_media_attributes_to_caps (const GstSDPMedia * media, GstCaps * caps)
|
|||
|
||||
res = sdp_add_attributes_to_caps (media->attributes, caps);
|
||||
|
||||
if (res == GST_SDP_OK) {
|
||||
/* parse media extmap field */
|
||||
res = gst_sdp_media_add_extmap_attributes (media->attributes, caps);
|
||||
}
|
||||
|
||||
done:
|
||||
if (mikey)
|
||||
gst_mikey_message_unref (mikey);
|
||||
|
|
|
@ -125,6 +125,26 @@ static const gchar caps_video_rtcp_fb_all_pt_102[] =
|
|||
"clock-rate=(int)90000, encoding-name=(string)H264, "
|
||||
"rtcp-fb-nack=(boolean)true, rtcp-fb-nack-pli=(boolean)true";
|
||||
|
||||
static const gchar * sdp_extmap = "v=0\r\n"
|
||||
"o=- 123456 2 IN IP4 127.0.0.1 \r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"a=maxptime:60\r\n"
|
||||
"a=sendrecv\r\n"
|
||||
"m=video 1 UDP/TLS/RTP/SAVPF 100 101 102\r\n"
|
||||
"c=IN IP4 1.1.1.1\r\n"
|
||||
"a=rtpmap:100 VP8/90000\r\n"
|
||||
"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
|
||||
"a=extmap:3/recvonly http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"
|
||||
"a=extmap:4 urn:3gpp:video-orientation attributes\r\n";
|
||||
|
||||
static const gchar caps_video_extmap_pt_100[] =
|
||||
"application/x-unknown, media=(string)video, payload=(int)100, "
|
||||
"clock-rate=(int)90000, encoding-name=(string)VP8, "
|
||||
"extmap-2=urn:ietf:params:rtp-hdrext:toffset, "
|
||||
"extmap-3=(string)<\"recvonly\",\"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\",\"\">, "
|
||||
"extmap-4=(string)<\"\",\"urn:3gpp:video-orientation\",\"attributes\">";
|
||||
|
||||
/* *INDENT-ON* */
|
||||
|
||||
GST_START_TEST (boxed)
|
||||
|
@ -535,6 +555,75 @@ GST_START_TEST (media_from_caps_rtcp_fb_pt_101)
|
|||
gst_sdp_message_free (message);
|
||||
}
|
||||
|
||||
GST_END_TEST
|
||||
GST_START_TEST (caps_from_media_extmap)
|
||||
{
|
||||
GstSDPMessage *message;
|
||||
glong length = -1;
|
||||
const GstSDPMedia *media1;
|
||||
GstCaps *caps1;
|
||||
GstCaps *result1;
|
||||
|
||||
gst_sdp_message_new (&message);
|
||||
gst_sdp_message_parse_buffer ((guint8 *) sdp_extmap, length, message);
|
||||
|
||||
media1 = gst_sdp_message_get_media (message, 0);
|
||||
fail_unless (media1 != NULL);
|
||||
|
||||
caps1 = gst_sdp_media_get_caps_from_media (media1, 100);
|
||||
gst_sdp_media_attributes_to_caps (media1, caps1);
|
||||
result1 = gst_caps_from_string (caps_video_extmap_pt_100);
|
||||
fail_unless (gst_caps_is_strictly_equal (caps1, result1));
|
||||
|
||||
gst_caps_unref (result1);
|
||||
gst_caps_unref (caps1);
|
||||
|
||||
gst_sdp_message_free (message);
|
||||
}
|
||||
|
||||
GST_END_TEST
|
||||
GST_START_TEST (media_from_caps_extmap_pt_100)
|
||||
{
|
||||
GstSDPResult ret = GST_SDP_OK;
|
||||
GstSDPMessage *message;
|
||||
glong length = -1;
|
||||
GstSDPMedia *media_caps;
|
||||
const GstSDPMedia *media_sdp;
|
||||
GstCaps *caps;
|
||||
const gchar *attr_val_caps1, *attr_val_caps2, *attr_val_caps3;
|
||||
const gchar *attr_val_sdp1, *attr_val_sdp2, *attr_val_sdp3;
|
||||
|
||||
caps = gst_caps_from_string (caps_video_extmap_pt_100);
|
||||
|
||||
gst_sdp_media_new (&media_caps);
|
||||
fail_unless (media_caps != NULL);
|
||||
|
||||
ret = gst_sdp_media_set_media_from_caps (caps, media_caps);
|
||||
fail_unless (ret == GST_SDP_OK);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_sdp_message_new (&message);
|
||||
gst_sdp_message_parse_buffer ((guint8 *) sdp_extmap, length, message);
|
||||
|
||||
media_sdp = gst_sdp_message_get_media (message, 0);
|
||||
fail_unless (media_sdp != NULL);
|
||||
|
||||
attr_val_caps1 = gst_sdp_media_get_attribute_val_n (media_caps, "extmap", 0);
|
||||
attr_val_caps2 = gst_sdp_media_get_attribute_val_n (media_caps, "extmap", 1);
|
||||
attr_val_caps3 = gst_sdp_media_get_attribute_val_n (media_caps, "extmap", 2);
|
||||
|
||||
attr_val_sdp1 = gst_sdp_media_get_attribute_val_n (media_sdp, "extmap", 0);
|
||||
attr_val_sdp2 = gst_sdp_media_get_attribute_val_n (media_sdp, "extmap", 1);
|
||||
attr_val_sdp3 = gst_sdp_media_get_attribute_val_n (media_sdp, "extmap", 2);
|
||||
|
||||
fail_if (g_strcmp0 (attr_val_caps1, attr_val_sdp1) != 0);
|
||||
fail_if (g_strcmp0 (attr_val_caps2, attr_val_sdp2) != 0);
|
||||
fail_if (g_strcmp0 (attr_val_caps3, attr_val_sdp3) != 0);
|
||||
|
||||
gst_sdp_media_free (media_caps);
|
||||
gst_sdp_message_free (message);
|
||||
}
|
||||
|
||||
GST_END_TEST
|
||||
GST_START_TEST (caps_from_media_really_const)
|
||||
{
|
||||
|
@ -588,8 +677,10 @@ sdp_suite (void)
|
|||
tcase_add_test (tc_chain, media_from_caps);
|
||||
tcase_add_test (tc_chain, caps_from_media_rtcp_fb);
|
||||
tcase_add_test (tc_chain, caps_from_media_rtcp_fb_all);
|
||||
tcase_add_test (tc_chain, caps_from_media_extmap);
|
||||
tcase_add_test (tc_chain, media_from_caps_rtcp_fb_pt_100);
|
||||
tcase_add_test (tc_chain, media_from_caps_rtcp_fb_pt_101);
|
||||
tcase_add_test (tc_chain, media_from_caps_extmap_pt_100);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue