webrtcbin: Enforce m-line restrictions when creating offer
authorOlivier Crête <olivier.crete@collabora.com>
Fri, 26 Mar 2021 22:15:50 +0000 (18:15 -0400)
committerOlivier Crête <olivier.crete@collabora.com>
Mon, 12 Apr 2021 21:55:07 +0000 (17:55 -0400)
First fail the offer creation if the mid of an existing offer doesn't
match a forced m-mline.

Then, for all newly added mlines, first look for a transceiver that
forces this m-line, then add a "floating" one, then the data channel.
And repeat this until we're out of transceivers.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2104>

ext/webrtc/gstwebrtcbin.c
ext/webrtc/utils.h

index a554a7b..ad4073c 100644 (file)
@@ -470,6 +470,9 @@ match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
 static gboolean
 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
 {
+  if (trans->stopped)
+    return FALSE;
+
   return trans->mline == *mline;
 }
 
@@ -2866,9 +2869,20 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
           if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
             GstSDPMedia *media;
             const gchar *mid;
+            WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
 
             g_assert (!g_list_find (seen_transceivers, trans));
 
+            if (wtrans->mline_locked && trans->mline != media_idx) {
+              g_set_error (error, GST_WEBRTC_BIN_ERROR,
+                  GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
+                  "Previous negotiatied transceiver %"
+                  GST_PTR_FORMAT " with mid %s was in mline %d but transceiver"
+                  " has locked mline %u", trans, trans->mid, media_idx,
+                  trans->mline);
+              goto cancel_offer;
+            }
+
             GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
                 GST_PTR_FORMAT " with mid %s into media index %u", trans,
                 trans->mid, media_idx);
@@ -2885,7 +2899,7 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
               g_set_error (error, GST_WEBRTC_BIN_ERROR,
                   GST_WEBRTC_BIN_ERROR_FAILED,
                   "Duplicate mid %s when creating offer", mid);
-              goto duplicate_mid;
+              goto cancel_offer;
             }
 
             g_hash_table_insert (all_mids, g_strdup (mid), NULL);
@@ -2929,27 +2943,94 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
       if (g_hash_table_contains (all_mids, trans->mid)) {
         g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_FAILED,
             "Duplicate mid %s when creating offer", trans->mid);
-        goto duplicate_mid;
+        goto cancel_offer;
       }
 
       g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
     }
   }
 
+
   /* add any extra streams */
-  for (i = 0; i < webrtc->priv->transceivers->len; i++) {
-    GstWebRTCRTPTransceiver *trans;
+  for (;;) {
+    GstWebRTCRTPTransceiver *trans = NULL;
     GstSDPMedia media = { 0, };
 
-    trans = g_ptr_array_index (webrtc->priv->transceivers, i);
+    /* First find a transceiver requesting this m-line */
+    trans = _find_transceiver_for_mline (webrtc, media_idx);
 
-    /* don't add transceivers twice */
-    if (g_list_find (seen_transceivers, trans))
-      continue;
+    if (trans) {
+      /* We can't have seen it already, because it is locked to this line */
+      g_assert (!g_list_find (seen_transceivers, trans));
+      seen_transceivers = g_list_prepend (seen_transceivers, trans);
+    } else {
+      /* Otherwise find a free transceiver */
+      for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+        WebRTCTransceiver *wtrans;
+
+        trans = g_ptr_array_index (webrtc->priv->transceivers, i);
+        wtrans = WEBRTC_TRANSCEIVER (trans);
+
+        /* don't add transceivers twice */
+        if (g_list_find (seen_transceivers, trans))
+          continue;
+
+        /* Ignore transceivers with a locked mline, as they would have been
+         * found above or will be used later */
+        if (wtrans->mline_locked)
+          continue;
+
+        seen_transceivers = g_list_prepend (seen_transceivers, trans);
+        /* don't add stopped transceivers */
+        if (trans->stopped) {
+          continue;
+        }
 
-    /* don't add stopped transceivers */
-    if (trans->stopped)
-      continue;
+        /* Otherwise take it */
+        break;
+      }
+
+      /* Stop if we got all transceivers */
+      if (i == webrtc->priv->transceivers->len) {
+
+        /* But try to add a data channel first, we do it here, because
+         * it can allow a locked m-line to be put after, so we need to
+         * do another iteration after.
+         */
+        if (_message_get_datachannel_index (ret) == G_MAXUINT) {
+          GstSDPMedia media = { 0, };
+          gst_sdp_media_init (&media);
+          if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
+                  bundle_ufrag, bundle_pwd, all_mids)) {
+            gst_sdp_message_add_media (ret, &media);
+            media_idx++;
+            continue;
+          } else {
+            gst_sdp_media_uninit (&media);
+          }
+        }
+
+        /* Verify that we didn't ignore any locked m-line transceivers */
+        for (i = 0; i < webrtc->priv->transceivers->len; i++) {
+          WebRTCTransceiver *wtrans;
+
+          trans = g_ptr_array_index (webrtc->priv->transceivers, i);
+          wtrans = WEBRTC_TRANSCEIVER (trans);
+          /* don't add transceivers twice */
+          if (g_list_find (seen_transceivers, trans))
+            continue;
+          g_assert (wtrans->mline_locked);
+
+          g_set_error (error, GST_WEBRTC_BIN_ERROR,
+              GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
+              "Tranceiver %" GST_PTR_FORMAT " with mid %s has locked mline %d"
+              " but the whole offer only has %u sections", trans, trans->mid,
+              trans->mline, media_idx);
+          goto cancel_offer;
+        }
+        break;
+      }
+    }
 
     gst_sdp_media_init (&media);
 
@@ -2973,7 +3054,6 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
       g_array_free (reserved_pts, TRUE);
       reserved_pts = NULL;
     }
-    seen_transceivers = g_list_prepend (seen_transceivers, trans);
   }
 
   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
@@ -2981,19 +3061,6 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
     reserved_pts = NULL;
   }
 
-  /* add a data channel if exists and not renegotiated */
-  if (_message_get_datachannel_index (ret) == G_MAXUINT) {
-    GstSDPMedia media = { 0, };
-    gst_sdp_media_init (&media);
-    if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
-            bundle_ufrag, bundle_pwd, all_mids)) {
-      gst_sdp_message_add_media (ret, &media);
-      media_idx++;
-    } else {
-      gst_sdp_media_uninit (&media);
-    }
-  }
-
   webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
       media_idx);
 
@@ -3040,7 +3107,7 @@ out:
 
   return ret;
 
-duplicate_mid:
+cancel_offer:
   gst_sdp_message_uninit (ret);
   ret = NULL;
   goto out;
index ac18673..f15abe4 100644 (file)
@@ -41,6 +41,7 @@ typedef enum
   GST_WEBRTC_BIN_ERROR_DATA_CHANNEL_FAILURE,
   GST_WEBRTC_BIN_ERROR_CLOSED,
   GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED,
+  GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION
 } GstWebRTCError;
 
 GstPadTemplate *        _find_pad_template          (GstElement * element,