diff --git a/gst-libs/gst/sdp/gstsdpmessage.c b/gst-libs/gst/sdp/gstsdpmessage.c index 3be9df419f..ae5fcd2d6d 100644 --- a/gst-libs/gst/sdp/gstsdpmessage.c +++ b/gst-libs/gst/sdp/gstsdpmessage.c @@ -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); diff --git a/tests/check/libs/sdp.c b/tests/check/libs/sdp.c index 3ec09c8966..d93b96f53d 100644 --- a/tests/check/libs/sdp.c +++ b/tests/check/libs/sdp.c @@ -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; }