From 9a758d78a91db0b6e063c363935662a8c7e46fee Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 1 Jul 2021 15:54:34 +1000 Subject: [PATCH] webrtcbin: support using an a=mid value from the sink/transceiver caps Part-of: --- .../gst-plugins-bad/ext/webrtc/gstwebrtcbin.c | 54 +++++++---- .../tests/check/elements/webrtcbin.c | 97 ++++++++++++++++++- 2 files changed, 129 insertions(+), 22 deletions(-) diff --git a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c index 0c42ad35aa..ac26965d64 100644 --- a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c +++ b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c @@ -2974,7 +2974,7 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05 */ GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc); - gchar *direction, *sdp_mid, *ufrag, *pwd; + gchar *direction, *ufrag, *pwd, *mid; gboolean bundle_only; GstCaps *caps; GstStructure *extmap; @@ -3156,17 +3156,34 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, /* Some identifier; we also add the media name to it so it's identifiable */ if (trans->mid) { gst_sdp_media_add_attribute (media, "mid", trans->mid); + mid = g_strdup (trans->mid); } else { - /* Make sure to avoid mid collisions */ - while (TRUE) { - sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media), - webrtc->priv->media_counter++); - if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) { - g_free (sdp_mid); - } else { - gst_sdp_media_add_attribute (media, "mid", sdp_mid); - g_hash_table_insert (all_mids, sdp_mid, NULL); - break; + const GstStructure *s = gst_caps_get_structure (caps, 0); + + mid = g_strdup (gst_structure_get_string (s, "a-mid")); + + if (mid) { + if (g_hash_table_contains (all_mids, (gpointer) mid)) { + g_set_error (error, GST_WEBRTC_ERROR, + GST_WEBRTC_ERROR_INTERNAL_FAILURE, + "Cannot re-use mid \'%s\' from the caps in m= line %u that has " + "already been used for a previous m= line in the SDP", mid, + media_idx); + return FALSE; + } + g_hash_table_insert (all_mids, g_strdup (mid), NULL); + } else { + /* Make sure to avoid mid collisions */ + while (TRUE) { + mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media), + webrtc->priv->media_counter++); + if (g_hash_table_contains (all_mids, (gpointer) mid)) { + g_free (mid); + } else { + gst_sdp_media_add_attribute (media, "mid", mid); + g_hash_table_insert (all_mids, g_strdup (mid), NULL); + break; + } } } } @@ -3190,12 +3207,12 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, } if (bundled_mids) { - const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid"); - g_assert (mid); g_string_append_printf (bundled_mids, " %s", mid); } + g_clear_pointer (&mid, g_free); + gst_caps_unref (caps); return TRUE; @@ -4155,12 +4172,13 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options, rtp_trans->mline, rtp_trans->kind, webrtc_kind_from_caps (answer_caps)); - if (!trans->do_nack) { - answer_caps = gst_caps_make_writable (answer_caps); - for (k = 0; k < gst_caps_get_size (answer_caps); k++) { - GstStructure *s = gst_caps_get_structure (answer_caps, k); + answer_caps = gst_caps_make_writable (answer_caps); + for (k = 0; k < gst_caps_get_size (answer_caps); k++) { + GstStructure *s = gst_caps_get_structure (answer_caps, k); + /* taken from the offer sdp already and already intersected above */ + gst_structure_remove_field (s, "a-mid"); + if (!trans->do_nack) gst_structure_remove_fields (s, "rtcp-fb-nack", NULL); - } } gst_sdp_media_set_media_from_caps (answer_caps, media); diff --git a/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c b/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c index 8508bd15e3..cc5b6b186a 100644 --- a/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c +++ b/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c @@ -1628,6 +1628,30 @@ GST_START_TEST (test_get_transceivers) GST_END_TEST; +static void +on_sdp_media_check_mid (struct test_webrtc *t, GstElement * element, + GstWebRTCSessionDescription * desc, gpointer user_data) +{ + const char **mid = user_data; + guint i; + + for (i = 0; i < gst_sdp_message_medias_len (desc->sdp); i++) { + const GstSDPMedia *media = gst_sdp_message_get_media (desc->sdp, i); + gboolean seen_mid = FALSE; + guint j; + + for (j = 0; j < gst_sdp_media_attributes_len (media); j++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j); + + if (g_strcmp0 (attr->key, "mid") == 0) { + fail_unless (!seen_mid); + seen_mid = TRUE; + fail_unless_equals_string (attr->value, mid[i]); + } + } + } +} + GST_START_TEST (test_add_recvonly_transceiver) { struct test_webrtc *t = test_webrtc_new (); @@ -1640,11 +1664,12 @@ GST_START_TEST (test_add_recvonly_transceiver) media_format_count, &no_duplicate_payloads); VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (1), &media_formats); + const char *expected_mid[] = { "gst", }; + VAL_SDP_INIT (mid, on_sdp_media_check_mid, expected_mid, &count); const gchar *expected_offer_setup[] = { "actpass", }; - VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, &count); + VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, &mid); const gchar *expected_answer_setup[] = { "active", }; - VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup, - &count); + VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup, &mid); const gchar *expected_offer_direction[] = { "recvonly", }; VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction, &offer_setup); @@ -1661,7 +1686,7 @@ GST_START_TEST (test_add_recvonly_transceiver) t->on_pad_added = _pad_added_fakesink; /* setup recvonly transceiver */ - caps = gst_caps_from_string (OPUS_RTP_CAPS (96)); + caps = gst_caps_from_string (OPUS_RTP_CAPS (96) ", a-mid=(string)gst"); direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY; g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps, &trans); @@ -4425,6 +4450,69 @@ GST_START_TEST (test_renego_rtx) GST_END_TEST; +GST_START_TEST (test_bundle_mid_header_extension) +{ + struct test_webrtc *t = test_webrtc_new (); + GstWebRTCRTPTransceiverDirection direction; + GstWebRTCRTPTransceiver *trans; + VAL_SDP_INIT (no_duplicate_payloads, on_sdp_media_no_duplicate_payloads, + NULL, NULL); + guint media_format_count[] = { 1, 1, }; + VAL_SDP_INIT (media_formats, on_sdp_media_count_formats, + media_format_count, &no_duplicate_payloads); + VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (1), + &media_formats); + const char *expected_mid[] = { "gst", }; + VAL_SDP_INIT (mid, on_sdp_media_check_mid, expected_mid, &count); + const gchar *expected_offer_setup[] = { "actpass", }; + VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, &mid); + const gchar *expected_answer_setup[] = { "active", }; + VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup, &mid); + const gchar *expected_offer_direction[] = { "recvonly", }; + VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction, + &offer_setup); + const gchar *expected_answer_direction[] = { "sendonly", }; + VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer_direction, + &answer_setup); + GstCaps *caps; + GstHarness *h; + guint mline; + char *trans_mid; + + /* add a transceiver that will only receive an opus stream and check that + * the created offer is marked as recvonly */ + t->on_negotiation_needed = NULL; + t->on_ice_candidate = NULL; + t->on_pad_added = _pad_added_fakesink; + + /* setup recvonly transceiver */ + caps = gst_caps_from_string (OPUS_RTP_CAPS (96) ", a-mid=(string)gst"); + direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY; + g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps, + &trans); + gst_caps_unref (caps); + fail_unless (trans != NULL); + g_object_get (trans, "mlineindex", &mline, NULL); + fail_unless_equals_int (mline, -1); + + /* setup sendonly peer */ + h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL); + add_fake_audio_src_harness (h, 96); + t->harnesses = g_list_prepend (t->harnesses, h); + + test_validate_sdp (t, &offer, &answer); + + g_object_get (trans, "mlineindex", &mline, "mid", &trans_mid, NULL); + fail_unless_equals_int (mline, 0); + fail_unless_equals_string (trans_mid, "gst"); + g_clear_pointer (&trans_mid, g_free); + gst_object_unref (trans); + + test_webrtc_free (t); +} + +GST_END_TEST; + static Suite * webrtcbin_suite (void) { @@ -4480,6 +4568,7 @@ webrtcbin_suite (void) tcase_add_test (tc, test_codec_preferences_incompatible_extmaps); tcase_add_test (tc, test_codec_preferences_invalid_extmap); tcase_add_test (tc, test_renego_rtx); + tcase_add_test (tc, test_bundle_mid_header_extension); if (sctpenc && sctpdec) { tcase_add_test (tc, test_data_channel_create); tcase_add_test (tc, test_data_channel_remote_notify);