webrtcbin: support using an a=mid value from the sink/transceiver caps
authorMatthew Waters <matthew@centricular.com>
Thu, 1 Jul 2021 05:54:34 +0000 (15:54 +1000)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 29 Mar 2022 23:55:40 +0000 (23:55 +0000)
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1664>

subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c
subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c

index 0c42ad3..ac26965 100644 (file)
@@ -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);
index 8508bd1..cc5b6b1 100644 (file)
@@ -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);