From 08dd305a205060ed50182f9c903d493d559633eb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Olivier=20Cr=C3=AAte?= Date: Fri, 26 Mar 2021 18:15:50 -0400 Subject: [PATCH] webrtcbin: Enforce m-line restrictions when creating offer 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: --- ext/webrtc/gstwebrtcbin.c | 119 ++++++++++++++++++++++++++++++++++++---------- ext/webrtc/utils.h | 1 + 2 files changed, 94 insertions(+), 26 deletions(-) diff --git a/ext/webrtc/gstwebrtcbin.c b/ext/webrtc/gstwebrtcbin.c index a554a7b..ad4073c 100644 --- a/ext/webrtc/gstwebrtcbin.c +++ b/ext/webrtc/gstwebrtcbin.c @@ -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; diff --git a/ext/webrtc/utils.h b/ext/webrtc/utils.h index ac18673..f15abe4 100644 --- a/ext/webrtc/utils.h +++ b/ext/webrtc/utils.h @@ -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, -- 2.7.4