blob: d75606ab4e66e881eef066c2fcde5973fc7d3815 [file] [log] [blame]
Harald Alvestrandcdcfab02020-09-28 13:02:071/*
2 * Copyright 2020 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "pc/sdp_offer_answer.h"
12
Harald Alvestrandc06e3742020-10-01 10:23:3313#include <algorithm>
Harald Alvestrand981c5722022-02-09 12:42:2714#include <cstddef>
Harald Alvestrand1f7eab62020-10-18 16:51:4715#include <iterator>
Harald Alvestrand3eaee6b2020-10-19 06:35:5516#include <map>
Mirko Bonadei9ff450d2021-08-02 08:56:3317#include <memory>
Harald Alvestrandc06e3742020-10-01 10:23:3318#include <queue>
Harald Alvestrand981c5722022-02-09 12:42:2719#include <string>
Harald Alvestrandf01bd6c2020-10-23 13:30:4620#include <utility>
Harald Alvestrandc06e3742020-10-01 10:23:3321
Harald Alvestrand1f7eab62020-10-18 16:51:4722#include "absl/algorithm/container.h"
Harald Alvestrand9cd199d2020-10-27 07:10:4323#include "absl/memory/memory.h"
Tomas Gunnarsson0dd75392022-01-17 18:19:5624#include "absl/strings/match.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4725#include "absl/strings/string_view.h"
26#include "api/array_view.h"
27#include "api/crypto/crypto_options.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4728#include "api/dtls_transport_interface.h"
Harald Alvestrand8f429922022-05-04 10:32:3029#include "api/field_trials_view.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4730#include "api/rtp_parameters.h"
31#include "api/rtp_receiver_interface.h"
32#include "api/rtp_sender_interface.h"
Harald Alvestrand763f5a92020-10-22 10:39:4033#include "api/video/builtin_video_bitrate_allocator_factory.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4734#include "media/base/codec.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4735#include "media/base/rid_description.h"
Harald Alvestrand85466662021-04-19 21:21:3636#include "p2p/base/ice_transport_internal.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4737#include "p2p/base/p2p_constants.h"
38#include "p2p/base/p2p_transport_channel.h"
39#include "p2p/base/port.h"
40#include "p2p/base/transport_description.h"
41#include "p2p/base/transport_description_factory.h"
42#include "p2p/base/transport_info.h"
Harald Alvestrand981c5722022-02-09 12:42:2743#include "pc/channel_interface.h"
Harald Alvestrand85466662021-04-19 21:21:3644#include "pc/dtls_transport.h"
Henrik Boströmf7859892022-07-04 12:36:3745#include "pc/legacy_stats_collector.h"
Harald Alvestrandcdcfab02020-09-28 13:02:0746#include "pc/media_stream.h"
Markus Handella1b82012021-05-26 16:56:3047#include "pc/media_stream_proxy.h"
Harald Alvestrand8e344192022-02-08 12:55:4248#include "pc/peer_connection_internal.h"
Harald Alvestrand1090e442020-10-05 07:01:0949#include "pc/peer_connection_message_handler.h"
Harald Alvestrandcdcfab02020-09-28 13:02:0750#include "pc/rtp_media_utils.h"
Harald Alvestrand981c5722022-02-09 12:42:2751#include "pc/rtp_receiver_proxy.h"
Harald Alvestrand3eaee6b2020-10-19 06:35:5552#include "pc/rtp_sender.h"
Harald Alvestrand981c5722022-02-09 12:42:2753#include "pc/rtp_sender_proxy.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4754#include "pc/simulcast_description.h"
55#include "pc/usage_pattern.h"
Philipp Hancke7d1aff62023-09-25 12:42:5156#include "pc/used_ids.h"
Harald Alvestrand763f5a92020-10-22 10:39:4057#include "pc/webrtc_session_description_factory.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4758#include "rtc_base/helpers.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4759#include "rtc_base/logging.h"
Harald Alvestrand763f5a92020-10-22 10:39:4060#include "rtc_base/rtc_certificate.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4761#include "rtc_base/ssl_stream_adapter.h"
62#include "rtc_base/string_encode.h"
63#include "rtc_base/strings/string_builder.h"
Harald Alvestrandcdcfab02020-09-28 13:02:0764#include "rtc_base/trace_event.h"
65#include "system_wrappers/include/metrics.h"
66
67using cricket::ContentInfo;
68using cricket::ContentInfos;
69using cricket::MediaContentDescription;
70using cricket::MediaProtocolType;
71using cricket::RidDescription;
72using cricket::RidDirection;
73using cricket::SessionDescription;
74using cricket::SimulcastDescription;
75using cricket::SimulcastLayer;
76using cricket::SimulcastLayerList;
77using cricket::StreamParams;
78using cricket::TransportInfo;
79
Harald Alvestrandcdcfab02020-09-28 13:02:0780namespace webrtc {
81
82namespace {
83
Harald Alvestranda6544372023-11-13 09:33:5684typedef PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions;
Harald Alvestrandc06e3742020-10-01 10:23:3385
Harald Alvestrandcdcfab02020-09-28 13:02:0786// Error messages
87const char kInvalidSdp[] = "Invalid session description.";
88const char kInvalidCandidates[] = "Description contains invalid candidates.";
89const char kBundleWithoutRtcpMux[] =
90 "rtcp-mux must be enabled when BUNDLE "
91 "is enabled.";
92const char kMlineMismatchInAnswer[] =
93 "The order of m-lines in answer doesn't match order in offer. Rejecting "
94 "answer.";
95const char kMlineMismatchInSubsequentOffer[] =
96 "The order of m-lines in subsequent offer doesn't match order from "
97 "previous offer/answer.";
98const char kSdpWithoutIceUfragPwd[] =
99 "Called with SDP without ice-ufrag and ice-pwd.";
100const char kSdpWithoutDtlsFingerprint[] =
101 "Called with SDP without DTLS fingerprint.";
Harald Alvestrand974044e2024-02-08 13:15:51102const char kSdpWithoutCrypto[] = "Called with SDP without crypto setup.";
Harald Alvestrandcdcfab02020-09-28 13:02:07103
Harald Alvestranda474fbf2020-10-01 16:47:23104const char kSessionError[] = "Session error code: ";
105const char kSessionErrorDesc[] = "Session error description: ";
106
Harald Alvestrandbc9ca252020-10-05 13:08:41107// The length of RTCP CNAMEs.
108static const int kRtcpCnameLength = 16;
109
Philipp Hancke187e9d42021-11-08 08:58:15110// The maximum length of the MID attribute.
Philipp Hanckee2652e12022-12-07 10:49:31111static constexpr size_t kMidMaxSize = 16;
Philipp Hancke187e9d42021-11-08 08:58:15112
Harald Alvestranda474fbf2020-10-01 16:47:23113const char kDefaultStreamId[] = "default";
114// NOTE: Duplicated in peer_connection.cc:
115static const char kDefaultAudioSenderId[] = "defaulta0";
116static const char kDefaultVideoSenderId[] = "defaultv0";
117
Harald Alvestrandcdcfab02020-09-28 13:02:07118void NoteAddIceCandidateResult(int result) {
119 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result,
120 kAddIceCandidateMax);
121}
122
Henrik Boströmf8187e02021-04-26 19:04:26123std::map<std::string, const cricket::ContentGroup*> GetBundleGroupsByMid(
124 const SessionDescription* desc) {
125 std::vector<const cricket::ContentGroup*> bundle_groups =
126 desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
127 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid;
128 for (const cricket::ContentGroup* bundle_group : bundle_groups) {
129 for (const std::string& content_name : bundle_group->content_names()) {
130 bundle_groups_by_mid[content_name] = bundle_group;
131 }
132 }
133 return bundle_groups_by_mid;
134}
135
Artem Titov880fa812021-07-30 20:30:23136// Returns true if `new_desc` requests an ICE restart (i.e., new ufrag/pwd).
Harald Alvestrandcdcfab02020-09-28 13:02:07137bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc,
138 const SessionDescriptionInterface* new_desc,
139 const std::string& content_name) {
140 if (!old_desc) {
141 return false;
142 }
143 const SessionDescription* new_sd = new_desc->description();
144 const SessionDescription* old_sd = old_desc->description();
145 const ContentInfo* cinfo = new_sd->GetContentByName(content_name);
146 if (!cinfo || cinfo->rejected) {
147 return false;
148 }
149 // If the content isn't rejected, check if ufrag and password has changed.
150 const cricket::TransportDescription* new_transport_desc =
151 new_sd->GetTransportDescriptionByName(content_name);
152 const cricket::TransportDescription* old_transport_desc =
153 old_sd->GetTransportDescriptionByName(content_name);
154 if (!new_transport_desc || !old_transport_desc) {
155 // No transport description exists. This is not an ICE restart.
156 return false;
157 }
158 if (cricket::IceCredentialsChanged(
159 old_transport_desc->ice_ufrag, old_transport_desc->ice_pwd,
160 new_transport_desc->ice_ufrag, new_transport_desc->ice_pwd)) {
161 RTC_LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name
162 << ".";
163 return true;
164 }
165 return false;
166}
167
168// Generates a string error message for SetLocalDescription/SetRemoteDescription
169// from an RTCError.
170std::string GetSetDescriptionErrorMessage(cricket::ContentSource source,
171 SdpType type,
172 const RTCError& error) {
173 rtc::StringBuilder oss;
174 oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote")
Tomas Gunnarsson0dd75392022-01-17 18:19:56175 << " " << SdpTypeToString(type) << " sdp: ";
176 RTC_DCHECK(!absl::StartsWith(error.message(), oss.str())) << error.message();
177 oss << error.message();
Harald Alvestrandcdcfab02020-09-28 13:02:07178 return oss.Release();
179}
180
181std::string GetStreamIdsString(rtc::ArrayView<const std::string> stream_ids) {
182 std::string output = "streams=[";
183 const char* separator = "";
184 for (const auto& stream_id : stream_ids) {
185 output.append(separator).append(stream_id);
186 separator = ", ";
187 }
188 output.append("]");
189 return output;
190}
191
Harald Alvestrandcdcfab02020-09-28 13:02:07192const ContentInfo* FindTransceiverMSection(
Harald Alvestrand85466662021-04-19 21:21:36193 RtpTransceiver* transceiver,
Harald Alvestrandcdcfab02020-09-28 13:02:07194 const SessionDescriptionInterface* session_description) {
195 return transceiver->mid()
196 ? session_description->description()->GetContentByName(
197 *transceiver->mid())
198 : nullptr;
199}
200
201// If the direction is "recvonly" or "inactive", treat the description
202// as containing no streams.
203// See: https://code.google.com/p/webrtc/issues/detail?id=5054
204std::vector<cricket::StreamParams> GetActiveStreams(
205 const cricket::MediaContentDescription* desc) {
206 return RtpTransceiverDirectionHasSend(desc->direction())
207 ? desc->streams()
208 : std::vector<cricket::StreamParams>();
209}
210
211// Logic to decide if an m= section can be recycled. This means that the new
212// m= section is not rejected, but the old local or remote m= section is
Artem Titov880fa812021-07-30 20:30:23213// rejected. `old_content_one` and `old_content_two` refer to the m= section
Harald Alvestrandcdcfab02020-09-28 13:02:07214// of the old remote and old local descriptions in no particular order.
215// We need to check both the old local and remote because either
216// could be the most current from the latest negotation.
217bool IsMediaSectionBeingRecycled(SdpType type,
218 const ContentInfo& content,
219 const ContentInfo* old_content_one,
220 const ContentInfo* old_content_two) {
221 return type == SdpType::kOffer && !content.rejected &&
222 ((old_content_one && old_content_one->rejected) ||
223 (old_content_two && old_content_two->rejected));
224}
225
Artem Titov880fa812021-07-30 20:30:23226// Verify that the order of media sections in `new_desc` matches
227// `current_desc`. The number of m= sections in `new_desc` should be no
228// less than `current_desc`. In the case of checking an answer's
229// `new_desc`, the `current_desc` is the last offer that was set as the
230// local or remote. In the case of checking an offer's `new_desc` we
Harald Alvestrandcdcfab02020-09-28 13:02:07231// check against the local and remote descriptions stored from the last
232// negotiation, because either of these could be the most up to date for
Artem Titov880fa812021-07-30 20:30:23233// possible rejected m sections. These are the `current_desc` and
234// `secondary_current_desc`.
Harald Alvestrandcdcfab02020-09-28 13:02:07235bool MediaSectionsInSameOrder(const SessionDescription& current_desc,
236 const SessionDescription* secondary_current_desc,
237 const SessionDescription& new_desc,
238 const SdpType type) {
239 if (current_desc.contents().size() > new_desc.contents().size()) {
240 return false;
241 }
242
243 for (size_t i = 0; i < current_desc.contents().size(); ++i) {
244 const cricket::ContentInfo* secondary_content_info = nullptr;
245 if (secondary_current_desc &&
246 i < secondary_current_desc->contents().size()) {
247 secondary_content_info = &secondary_current_desc->contents()[i];
248 }
249 if (IsMediaSectionBeingRecycled(type, new_desc.contents()[i],
250 &current_desc.contents()[i],
251 secondary_content_info)) {
252 // For new offer descriptions, if the media section can be recycled, it's
253 // valid for the MID and media type to change.
254 continue;
255 }
256 if (new_desc.contents()[i].name != current_desc.contents()[i].name) {
257 return false;
258 }
259 const MediaContentDescription* new_desc_mdesc =
260 new_desc.contents()[i].media_description();
261 const MediaContentDescription* current_desc_mdesc =
262 current_desc.contents()[i].media_description();
263 if (new_desc_mdesc->type() != current_desc_mdesc->type()) {
264 return false;
265 }
266 }
267 return true;
268}
269
270bool MediaSectionsHaveSameCount(const SessionDescription& desc1,
271 const SessionDescription& desc2) {
272 return desc1.contents().size() == desc2.contents().size();
273}
Harald Alvestrand974044e2024-02-08 13:15:51274// Checks that each non-rejected content has a DTLS
Harald Alvestrandcdcfab02020-09-28 13:02:07275// fingerprint, unless it's in a BUNDLE group, in which case only the
276// BUNDLE-tag section (first media section/description in the BUNDLE group)
277// needs a ufrag and pwd. Mismatches, such as replying with a DTLS fingerprint
278// to SDES keys, will be caught in JsepTransport negotiation, and backstopped
Artem Titov880fa812021-07-30 20:30:23279// by Channel's `srtp_required` check.
Henrik Boströmf8187e02021-04-26 19:04:26280RTCError VerifyCrypto(const SessionDescription* desc,
Harald Alvestrand0d018412021-11-04 13:52:31281 bool dtls_enabled,
Henrik Boströmf8187e02021-04-26 19:04:26282 const std::map<std::string, const cricket::ContentGroup*>&
283 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:07284 for (const cricket::ContentInfo& content_info : desc->contents()) {
285 if (content_info.rejected) {
286 continue;
287 }
Harald Alvestrandcdcfab02020-09-28 13:02:07288 const std::string& mid = content_info.name;
Henrik Boströmf8187e02021-04-26 19:04:26289 auto it = bundle_groups_by_mid.find(mid);
290 const cricket::ContentGroup* bundle =
291 it != bundle_groups_by_mid.end() ? it->second : nullptr;
292 if (bundle && mid != *(bundle->FirstContentName())) {
Harald Alvestrandcdcfab02020-09-28 13:02:07293 // This isn't the first media section in the BUNDLE group, so it's not
294 // required to have crypto attributes, since only the crypto attributes
295 // from the first section actually get used.
296 continue;
297 }
298
299 // If the content isn't rejected or bundled into another m= section, crypto
300 // must be present.
301 const MediaContentDescription* media = content_info.media_description();
302 const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
303 if (!media || !tinfo) {
304 // Something is not right.
305 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
306 }
Harald Alvestrand0d018412021-11-04 13:52:31307 if (dtls_enabled) {
308 if (!tinfo->description.identity_fingerprint) {
Philipp Hanckeb81bf532023-07-18 09:03:39309 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
310 kSdpWithoutDtlsFingerprint);
Harald Alvestrand0d018412021-11-04 13:52:31311 }
312 } else {
Harald Alvestrand974044e2024-02-08 13:15:51313 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kSdpWithoutCrypto);
Harald Alvestrandcdcfab02020-09-28 13:02:07314 }
315 }
316 return RTCError::OK();
317}
318
319// Checks that each non-rejected content has ice-ufrag and ice-pwd set, unless
320// it's in a BUNDLE group, in which case only the BUNDLE-tag section (first
321// media section/description in the BUNDLE group) needs a ufrag and pwd.
Henrik Boströmf8187e02021-04-26 19:04:26322bool VerifyIceUfragPwdPresent(
323 const SessionDescription* desc,
324 const std::map<std::string, const cricket::ContentGroup*>&
325 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:07326 for (const cricket::ContentInfo& content_info : desc->contents()) {
327 if (content_info.rejected) {
328 continue;
329 }
330 const std::string& mid = content_info.name;
Henrik Boströmf8187e02021-04-26 19:04:26331 auto it = bundle_groups_by_mid.find(mid);
332 const cricket::ContentGroup* bundle =
333 it != bundle_groups_by_mid.end() ? it->second : nullptr;
334 if (bundle && mid != *(bundle->FirstContentName())) {
Harald Alvestrandcdcfab02020-09-28 13:02:07335 // This isn't the first media section in the BUNDLE group, so it's not
336 // required to have ufrag/password, since only the ufrag/password from
337 // the first section actually get used.
338 continue;
339 }
340
341 // If the content isn't rejected or bundled into another m= section,
342 // ice-ufrag and ice-pwd must be present.
343 const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
344 if (!tinfo) {
345 // Something is not right.
346 RTC_LOG(LS_ERROR) << kInvalidSdp;
347 return false;
348 }
349 if (tinfo->description.ice_ufrag.empty() ||
350 tinfo->description.ice_pwd.empty()) {
351 RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
352 return false;
353 }
354 }
355 return true;
356}
357
Harald Alvestrand85466662021-04-19 21:21:36358RTCError ValidateMids(const cricket::SessionDescription& description) {
Harald Alvestrandcdcfab02020-09-28 13:02:07359 std::set<std::string> mids;
360 for (const cricket::ContentInfo& content : description.contents()) {
361 if (content.name.empty()) {
362 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
363 "A media section is missing a MID attribute.");
364 }
Philipp Hancke187e9d42021-11-08 08:58:15365 if (content.name.size() > kMidMaxSize) {
366 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
367 "The MID attribute exceeds the maximum supported "
Philipp Hanckee2652e12022-12-07 10:49:31368 "length of 16 characters.");
Philipp Hancke187e9d42021-11-08 08:58:15369 }
Harald Alvestrandcdcfab02020-09-28 13:02:07370 if (!mids.insert(content.name).second) {
371 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
372 "Duplicate a=mid value '" + content.name + "'.");
373 }
374 }
375 return RTCError::OK();
376}
377
Philipp Hanckef0ea56a2022-11-28 16:48:04378RTCError FindDuplicateCodecParameters(
379 const RtpCodecParameters codec_parameters,
380 std::map<int, RtpCodecParameters>& payload_to_codec_parameters) {
381 auto existing_codec_parameters =
382 payload_to_codec_parameters.find(codec_parameters.payload_type);
383 if (existing_codec_parameters != payload_to_codec_parameters.end() &&
384 codec_parameters != existing_codec_parameters->second) {
Philipp Hanckeb81bf532023-07-18 09:03:39385 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
386 "A BUNDLE group contains a codec collision for "
387 "payload_type='" +
388 rtc::ToString(codec_parameters.payload_type) +
389 ". All codecs must share the same type, "
390 "encoding name, clock rate and parameters.");
Philipp Hanckef0ea56a2022-11-28 16:48:04391 }
392 payload_to_codec_parameters.insert(
393 std::make_pair(codec_parameters.payload_type, codec_parameters));
394 return RTCError::OK();
395}
396
397RTCError ValidateBundledPayloadTypes(
398 const cricket::SessionDescription& description) {
399 // https://www.rfc-editor.org/rfc/rfc8843#name-payload-type-pt-value-reuse
400 // ... all codecs associated with the payload type number MUST share an
401 // identical codec configuration. This means that the codecs MUST share
402 // the same media type, encoding name, clock rate, and any parameter
403 // that can affect the codec configuration and packetization.
Philipp Hanckef0ea56a2022-11-28 16:48:04404 std::vector<const cricket::ContentGroup*> bundle_groups =
405 description.GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
406 for (const cricket::ContentGroup* bundle_group : bundle_groups) {
407 std::map<int, RtpCodecParameters> payload_to_codec_parameters;
408 for (const std::string& content_name : bundle_group->content_names()) {
Philipp Hancke465bc0f2023-08-25 10:42:36409 const ContentInfo* content_description =
410 description.GetContentByName(content_name);
411 if (!content_description) {
Philipp Hanckeb81bf532023-07-18 09:03:39412 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
413 "A BUNDLE group contains a MID='" + content_name +
414 "' matching no m= section.");
Philipp Hanckef0ea56a2022-11-28 16:48:04415 }
Philipp Hancke465bc0f2023-08-25 10:42:36416 const cricket::MediaContentDescription* media_description =
417 content_description->media_description();
418 RTC_DCHECK(media_description);
419 if (content_description->rejected || !media_description ||
420 !media_description->has_codecs()) {
Philipp Hanckef0ea56a2022-11-28 16:48:04421 continue;
422 }
423 const auto type = media_description->type();
Philipp Hanckef14dfed2023-09-18 12:20:34424 if (type == cricket::MEDIA_TYPE_AUDIO ||
425 type == cricket::MEDIA_TYPE_VIDEO) {
426 for (const auto& c : media_description->codecs()) {
Philipp Hanckef0ea56a2022-11-28 16:48:04427 auto error = FindDuplicateCodecParameters(
428 c.ToCodecParameters(), payload_to_codec_parameters);
429 if (!error.ok()) {
430 return error;
431 }
432 }
433 }
434 }
435 }
436 return RTCError::OK();
437}
438
Philipp Hanckea2f5d452022-12-19 10:04:06439RTCError FindDuplicateHeaderExtensionIds(
440 const RtpExtension extension,
441 std::map<int, RtpExtension>& id_to_extension) {
442 auto existing_extension = id_to_extension.find(extension.id);
443 if (existing_extension != id_to_extension.end() &&
444 !(extension.uri == existing_extension->second.uri &&
445 extension.encrypt == existing_extension->second.encrypt)) {
Philipp Hanckeb81bf532023-07-18 09:03:39446 LOG_AND_RETURN_ERROR(
Philipp Hanckea2f5d452022-12-19 10:04:06447 RTCErrorType::INVALID_PARAMETER,
448 "A BUNDLE group contains a codec collision for "
Philipp Hancke4bf52382023-05-25 14:11:36449 "header extension id=" +
Philipp Hanckea2f5d452022-12-19 10:04:06450 rtc::ToString(extension.id) +
451 ". The id must be the same across all bundled media descriptions");
452 }
453 id_to_extension.insert(std::make_pair(extension.id, extension));
454 return RTCError::OK();
455}
456
457RTCError ValidateBundledRtpHeaderExtensions(
458 const cricket::SessionDescription& description) {
459 // https://www.rfc-editor.org/rfc/rfc8843#name-rtp-header-extensions-consi
460 // ... the identifier used for a given extension MUST identify the same
461 // extension across all the bundled media descriptions.
462 std::vector<const cricket::ContentGroup*> bundle_groups =
463 description.GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
464 for (const cricket::ContentGroup* bundle_group : bundle_groups) {
465 std::map<int, RtpExtension> id_to_extension;
466 for (const std::string& content_name : bundle_group->content_names()) {
Philipp Hancke465bc0f2023-08-25 10:42:36467 const ContentInfo* content_description =
468 description.GetContentByName(content_name);
469 if (!content_description) {
Philipp Hanckeb81bf532023-07-18 09:03:39470 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
471 "A BUNDLE group contains a MID='" + content_name +
472 "' matching no m= section.");
Philipp Hanckea2f5d452022-12-19 10:04:06473 }
Philipp Hancke465bc0f2023-08-25 10:42:36474 const cricket::MediaContentDescription* media_description =
475 content_description->media_description();
476 RTC_DCHECK(media_description);
477 if (content_description->rejected || !media_description ||
478 !media_description->has_codecs()) {
479 continue;
480 }
481
Philipp Hanckea2f5d452022-12-19 10:04:06482 for (const auto& extension : media_description->rtp_header_extensions()) {
483 auto error =
484 FindDuplicateHeaderExtensionIds(extension, id_to_extension);
485 if (!error.ok()) {
486 return error;
487 }
488 }
489 }
490 }
491 return RTCError::OK();
492}
493
Philipp Hancke34887262023-06-01 17:07:50494RTCError ValidateRtpHeaderExtensionsForSpecSimulcast(
495 const cricket::SessionDescription& description) {
496 for (const ContentInfo& content : description.contents()) {
Philipp Hancke465bc0f2023-08-25 10:42:36497 if (content.type != MediaProtocolType::kRtp || content.rejected) {
Philipp Hancke34887262023-06-01 17:07:50498 continue;
499 }
500 const auto media_description = content.media_description();
501 if (!media_description->HasSimulcast()) {
502 continue;
503 }
504 auto extensions = media_description->rtp_header_extensions();
505 auto it = absl::c_find_if(extensions, [](const RtpExtension& ext) {
506 return ext.uri == RtpExtension::kRidUri;
507 });
508 if (it == extensions.end()) {
Philipp Hanckeb81bf532023-07-18 09:03:39509 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
510 "The media section with MID='" + content.mid() +
511 "' negotiates simulcast but does not negotiate "
512 "the RID RTP header extension.");
Philipp Hancke34887262023-06-01 17:07:50513 }
514 }
515 return RTCError::OK();
516}
517
Philipp Hancke1f1b0b32023-08-11 07:32:50518RTCError ValidateSsrcGroups(const cricket::SessionDescription& description) {
519 for (const ContentInfo& content : description.contents()) {
520 if (content.type != MediaProtocolType::kRtp) {
521 continue;
522 }
523 for (const StreamParams& stream : content.media_description()->streams()) {
524 for (const cricket::SsrcGroup& group : stream.ssrc_groups) {
525 // Validate the number of SSRCs for standard SSRC group semantics such
526 // as FID and FEC-FR and the non-standard SIM group.
527 if ((group.semantics == cricket::kFidSsrcGroupSemantics &&
528 group.ssrcs.size() != 2) ||
529 (group.semantics == cricket::kFecFrSsrcGroupSemantics &&
530 group.ssrcs.size() != 2) ||
531 (group.semantics == cricket::kSimSsrcGroupSemantics &&
532 group.ssrcs.size() > kMaxSimulcastStreams)) {
533 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
534 "The media section with MID='" + content.mid() +
535 "' has a ssrc-group with semantics " +
536 group.semantics +
537 " and an unexpected number of SSRCs.");
538 }
539 }
540 }
541 }
542 return RTCError::OK();
543}
544
Philipp Hanckeb64615a2023-09-14 14:23:31545RTCError ValidatePayloadTypes(const cricket::SessionDescription& description) {
546 for (const ContentInfo& content : description.contents()) {
547 if (content.type != MediaProtocolType::kRtp) {
548 continue;
549 }
550 const auto media_description = content.media_description();
551 RTC_DCHECK(media_description);
552 if (content.rejected || !media_description ||
553 !media_description->has_codecs()) {
554 continue;
555 }
556 const auto type = media_description->type();
Philipp Hancked0f0f382023-11-23 19:21:05557 if (type == cricket::MEDIA_TYPE_AUDIO ||
558 type == cricket::MEDIA_TYPE_VIDEO) {
559 for (const auto& codec : media_description->codecs()) {
Philipp Hancke7d1aff62023-09-25 12:42:51560 if (!cricket::UsedPayloadTypes::IsIdValid(
561 codec, media_description->rtcp_mux())) {
Philipp Hanckeb64615a2023-09-14 14:23:31562 LOG_AND_RETURN_ERROR(
563 RTCErrorType::INVALID_PARAMETER,
564 "The media section with MID='" + content.mid() +
565 "' used an invalid payload type " + rtc::ToString(codec.id) +
566 " for codec '" + codec.name + ", rtcp-mux:" +
567 (media_description->rtcp_mux() ? "enabled" : "disabled"));
568 }
569 }
570 }
571 }
572 return RTCError::OK();
573}
574
Harald Alvestrandcdcfab02020-09-28 13:02:07575bool IsValidOfferToReceiveMedia(int value) {
576 typedef PeerConnectionInterface::RTCOfferAnswerOptions Options;
577 return (value >= Options::kUndefined) &&
578 (value <= Options::kMaxOfferToReceiveMedia);
579}
580
581bool ValidateOfferAnswerOptions(
582 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) {
583 return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) &&
584 IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video);
585}
586
Harald Alvestrandcdcfab02020-09-28 13:02:07587// This method will extract any send encodings that were sent by the remote
588// connection. This is currently only relevant for Simulcast scenario (where
589// the number of layers may be communicated by the server).
Harald Alvestrand85466662021-04-19 21:21:36590std::vector<RtpEncodingParameters> GetSendEncodingsFromRemoteDescription(
Harald Alvestrandcdcfab02020-09-28 13:02:07591 const MediaContentDescription& desc) {
592 if (!desc.HasSimulcast()) {
593 return {};
594 }
595 std::vector<RtpEncodingParameters> result;
596 const SimulcastDescription& simulcast = desc.simulcast_description();
597
598 // This is a remote description, the parameters we are after should appear
599 // as receive streams.
600 for (const auto& alternatives : simulcast.receive_layers()) {
601 RTC_DCHECK(!alternatives.empty());
602 // There is currently no way to specify or choose from alternatives.
603 // We will always use the first alternative, which is the most preferred.
604 const SimulcastLayer& layer = alternatives[0];
605 RtpEncodingParameters parameters;
606 parameters.rid = layer.rid;
607 parameters.active = !layer.is_paused;
608 result.push_back(parameters);
609 }
610
611 return result;
612}
613
Harald Alvestrand85466662021-04-19 21:21:36614RTCError UpdateSimulcastLayerStatusInSender(
Harald Alvestrandcdcfab02020-09-28 13:02:07615 const std::vector<SimulcastLayer>& layers,
616 rtc::scoped_refptr<RtpSenderInternal> sender) {
617 RTC_DCHECK(sender);
Harald Alvestrand0166be82022-08-25 11:31:01618 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
Harald Alvestrandcdcfab02020-09-28 13:02:07619 std::vector<std::string> disabled_layers;
620
621 // The simulcast envelope cannot be changed, only the status of the streams.
622 // So we will iterate over the send encodings rather than the layers.
623 for (RtpEncodingParameters& encoding : parameters.encodings) {
624 auto iter = std::find_if(layers.begin(), layers.end(),
625 [&encoding](const SimulcastLayer& layer) {
626 return layer.rid == encoding.rid;
627 });
628 // A layer that cannot be found may have been removed by the remote party.
629 if (iter == layers.end()) {
630 disabled_layers.push_back(encoding.rid);
631 continue;
632 }
633
634 encoding.active = !iter->is_paused;
635 }
636
Harald Alvestrand0166be82022-08-25 11:31:01637 RTCError result = sender->SetParametersInternalWithAllLayers(parameters);
Harald Alvestrandcdcfab02020-09-28 13:02:07638 if (result.ok()) {
639 result = sender->DisableEncodingLayers(disabled_layers);
640 }
641
642 return result;
643}
644
Harald Alvestrand85466662021-04-19 21:21:36645bool SimulcastIsRejected(const ContentInfo* local_content,
Lennart Grahl0d0ed762021-05-17 14:06:37646 const MediaContentDescription& answer_media_desc,
647 bool enable_encrypted_rtp_header_extensions) {
Harald Alvestrandcdcfab02020-09-28 13:02:07648 bool simulcast_offered = local_content &&
649 local_content->media_description() &&
650 local_content->media_description()->HasSimulcast();
651 bool simulcast_answered = answer_media_desc.HasSimulcast();
652 bool rids_supported = RtpExtension::FindHeaderExtensionByUri(
Lennart Grahl0d0ed762021-05-17 14:06:37653 answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri,
654 enable_encrypted_rtp_header_extensions
655 ? RtpExtension::Filter::kPreferEncryptedExtension
656 : RtpExtension::Filter::kDiscardEncryptedExtension);
Harald Alvestrandcdcfab02020-09-28 13:02:07657 return simulcast_offered && (!simulcast_answered || !rids_supported);
658}
659
Harald Alvestrand85466662021-04-19 21:21:36660RTCError DisableSimulcastInSender(
Harald Alvestrandcdcfab02020-09-28 13:02:07661 rtc::scoped_refptr<RtpSenderInternal> sender) {
662 RTC_DCHECK(sender);
Harald Alvestrand0166be82022-08-25 11:31:01663 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
Harald Alvestrandcdcfab02020-09-28 13:02:07664 if (parameters.encodings.size() <= 1) {
665 return RTCError::OK();
666 }
667
668 std::vector<std::string> disabled_layers;
669 std::transform(
670 parameters.encodings.begin() + 1, parameters.encodings.end(),
671 std::back_inserter(disabled_layers),
672 [](const RtpEncodingParameters& encoding) { return encoding.rid; });
673 return sender->DisableEncodingLayers(disabled_layers);
674}
675
Harald Alvestrandc06e3742020-10-01 10:23:33676// The SDP parser used to populate these values by default for the 'content
677// name' if an a=mid line was absent.
Harald Alvestrand85466662021-04-19 21:21:36678absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) {
Harald Alvestrandc06e3742020-10-01 10:23:33679 switch (media_type) {
680 case cricket::MEDIA_TYPE_AUDIO:
681 return cricket::CN_AUDIO;
682 case cricket::MEDIA_TYPE_VIDEO:
683 return cricket::CN_VIDEO;
684 case cricket::MEDIA_TYPE_DATA:
685 return cricket::CN_DATA;
Philipp Hancke4e8c1152020-10-13 10:43:15686 case cricket::MEDIA_TYPE_UNSUPPORTED:
687 return "not supported";
Harald Alvestrandc06e3742020-10-01 10:23:33688 }
Artem Titovd3251962021-11-15 15:57:07689 RTC_DCHECK_NOTREACHED();
Harald Alvestrandc06e3742020-10-01 10:23:33690 return "";
691}
692
Artem Titov880fa812021-07-30 20:30:23693// Add options to |[audio/video]_media_description_options| from `senders`.
Harald Alvestrandc06e3742020-10-01 10:23:33694void AddPlanBRtpSenderOptions(
695 const std::vector<rtc::scoped_refptr<
696 RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
697 cricket::MediaDescriptionOptions* audio_media_description_options,
698 cricket::MediaDescriptionOptions* video_media_description_options,
699 int num_sim_layers) {
700 for (const auto& sender : senders) {
701 if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
702 if (audio_media_description_options) {
703 audio_media_description_options->AddAudioSender(
704 sender->id(), sender->internal()->stream_ids());
705 }
706 } else {
707 RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO);
708 if (video_media_description_options) {
709 video_media_description_options->AddVideoSender(
710 sender->id(), sender->internal()->stream_ids(), {},
711 SimulcastLayerList(), num_sim_layers);
712 }
713 }
714 }
715}
716
Harald Alvestrand85466662021-04-19 21:21:36717cricket::MediaDescriptionOptions GetMediaDescriptionOptionsForTransceiver(
718 RtpTransceiver* transceiver,
Harald Alvestrandc06e3742020-10-01 10:23:33719 const std::string& mid,
720 bool is_create_offer) {
721 // NOTE: a stopping transceiver should be treated as a stopped one in
722 // createOffer as specified in
723 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
724 bool stopped =
725 is_create_offer ? transceiver->stopping() : transceiver->stopped();
726 cricket::MediaDescriptionOptions media_description_options(
727 transceiver->media_type(), mid, transceiver->direction(), stopped);
728 media_description_options.codec_preferences =
729 transceiver->codec_preferences();
730 media_description_options.header_extensions =
Philipp Hancke9f6ae372023-03-06 17:08:31731 transceiver->GetHeaderExtensionsToNegotiate();
Harald Alvestrandc06e3742020-10-01 10:23:33732 // This behavior is specified in JSEP. The gist is that:
733 // 1. The MSID is included if the RtpTransceiver's direction is sendonly or
734 // sendrecv.
735 // 2. If the MSID is included, then it must be included in any subsequent
736 // offer/answer exactly the same until the RtpTransceiver is stopped.
737 if (stopped || (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
Harald Alvestrand85466662021-04-19 21:21:36738 !transceiver->has_ever_been_used_to_send())) {
Harald Alvestrandc06e3742020-10-01 10:23:33739 return media_description_options;
740 }
741
742 cricket::SenderOptions sender_options;
743 sender_options.track_id = transceiver->sender()->id();
744 sender_options.stream_ids = transceiver->sender()->stream_ids();
745
746 // The following sets up RIDs and Simulcast.
747 // RIDs are included if Simulcast is requested or if any RID was specified.
748 RtpParameters send_parameters =
Harald Alvestrand0166be82022-08-25 11:31:01749 transceiver->sender_internal()->GetParametersInternalWithAllLayers();
Harald Alvestrandc06e3742020-10-01 10:23:33750 bool has_rids = std::any_of(send_parameters.encodings.begin(),
751 send_parameters.encodings.end(),
752 [](const RtpEncodingParameters& encoding) {
753 return !encoding.rid.empty();
754 });
755
756 std::vector<RidDescription> send_rids;
757 SimulcastLayerList send_layers;
758 for (const RtpEncodingParameters& encoding : send_parameters.encodings) {
759 if (encoding.rid.empty()) {
760 continue;
761 }
762 send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend));
763 send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
764 }
765
766 if (has_rids) {
767 sender_options.rids = send_rids;
768 }
769
770 sender_options.simulcast_layers = send_layers;
771 // When RIDs are configured, we must set num_sim_layers to 0 to.
772 // Otherwise, num_sim_layers must be 1 because either there is no
773 // simulcast, or simulcast is acheived by munging the SDP.
774 sender_options.num_sim_layers = has_rids ? 0 : 1;
775 media_description_options.sender_options.push_back(sender_options);
776
777 return media_description_options;
778}
779
Artem Titov880fa812021-07-30 20:30:23780// Returns the ContentInfo at mline index `i`, or null if none exists.
Harald Alvestrand85466662021-04-19 21:21:36781const ContentInfo* GetContentByIndex(const SessionDescriptionInterface* sdesc,
782 size_t i) {
Harald Alvestrandc06e3742020-10-01 10:23:33783 if (!sdesc) {
784 return nullptr;
785 }
786 const ContentInfos& contents = sdesc->description()->contents();
787 return (i < contents.size() ? &contents[i] : nullptr);
788}
789
Artem Titov880fa812021-07-30 20:30:23790// From `rtc_options`, fill parts of `session_options` shared by all generated
Harald Alvestrandc06e3742020-10-01 10:23:33791// m= sectionss (in other words, nothing that involves a map/array).
792void ExtractSharedMediaSessionOptions(
793 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
794 cricket::MediaSessionOptions* session_options) {
795 session_options->vad_enabled = rtc_options.voice_activity_detection;
796 session_options->bundle_enabled = rtc_options.use_rtp_mux;
797 session_options->raw_packetization_for_video =
798 rtc_options.raw_packetization_for_video;
799}
800
Harald Alvestrandbc9ca252020-10-05 13:08:41801// Generate a RTCP CNAME when a PeerConnection is created.
802std::string GenerateRtcpCname() {
803 std::string cname;
804 if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) {
805 RTC_LOG(LS_ERROR) << "Failed to generate CNAME.";
Artem Titovd3251962021-11-15 15:57:07806 RTC_DCHECK_NOTREACHED();
Harald Alvestrandbc9ca252020-10-05 13:08:41807 }
808 return cname;
809}
810
Artem Titov880fa812021-07-30 20:30:23811// Check if we can send `new_stream` on a PeerConnection.
Harald Alvestranda6544372023-11-13 09:33:56812bool CanAddLocalMediaStream(StreamCollectionInterface* current_streams,
813 MediaStreamInterface* new_stream) {
Harald Alvestrand6f04b6532020-10-09 11:42:17814 if (!new_stream || !current_streams) {
815 return false;
816 }
817 if (current_streams->find(new_stream->id()) != nullptr) {
818 RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id()
819 << " is already added.";
820 return false;
821 }
822 return true;
823}
824
Harald Alvestranda6544372023-11-13 09:33:56825rtc::scoped_refptr<DtlsTransport> LookupDtlsTransportByMid(
Tomas Gunnarsson92eebef2021-02-10 12:05:44826 rtc::Thread* network_thread,
827 JsepTransportController* controller,
828 const std::string& mid) {
829 // TODO(tommi): Can we post this (and associated operations where this
Danil Chapovalov9e09a1f2022-09-08 16:38:10830 // function is called) to the network thread and avoid this BlockingCall?
Tomas Gunnarsson92eebef2021-02-10 12:05:44831 // We might be able to simplify a few things if we set the transport on
832 // the network thread and then update the implementation to check that
833 // the set_ and relevant get methods are always called on the network
834 // thread (we'll need to update proxy maps).
Danil Chapovalov9e09a1f2022-09-08 16:38:10835 return network_thread->BlockingCall(
Tomas Gunnarsson92eebef2021-02-10 12:05:44836 [controller, &mid] { return controller->LookupDtlsTransportByMid(mid); });
837}
838
Henrik Boström4ea80f32021-06-09 08:29:50839bool ContentHasHeaderExtension(const cricket::ContentInfo& content_info,
840 absl::string_view header_extension_uri) {
841 for (const RtpExtension& rtp_header_extension :
842 content_info.media_description()->rtp_header_extensions()) {
843 if (rtp_header_extension.uri == header_extension_uri) {
844 return true;
845 }
846 }
847 return false;
848}
849
Harald Alvestrandcdcfab02020-09-28 13:02:07850} // namespace
851
Philipp Hancke49e55872023-03-30 11:51:39852void UpdateRtpHeaderExtensionPreferencesFromSdpMunging(
853 const cricket::SessionDescription* description,
854 TransceiverList* transceivers) {
855 // This integrates the RTP Header Extension Control API and local SDP munging
856 // for backward compability reasons. If something was enabled in the local
857 // description via SDP munging, consider it non-stopped in the API as well
858 // so that is shows up in subsequent offers/answers.
859 RTC_DCHECK(description);
860 RTC_DCHECK(transceivers);
861 for (const auto& content : description->contents()) {
862 auto transceiver = transceivers->FindByMid(content.name);
863 if (!transceiver) {
864 continue;
865 }
866 auto extension_capabilities = transceiver->GetHeaderExtensionsToNegotiate();
867 // Set the capability of every extension we see here to "sendrecv".
868 for (auto& ext : content.media_description()->rtp_header_extensions()) {
869 auto it = absl::c_find_if(extension_capabilities,
870 [&ext](const RtpHeaderExtensionCapability c) {
871 return ext.uri == c.uri;
872 });
873 if (it != extension_capabilities.end()) {
874 it->direction = RtpTransceiverDirection::kSendRecv;
875 }
876 }
877 transceiver->SetHeaderExtensionsToNegotiate(extension_capabilities);
878 }
879}
880
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04881// This class stores state related to a SetRemoteDescription operation, captures
Philipp Hancke49e55872023-03-30 11:51:39882// and reports potential errors that might occur and makes sure to notify the
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04883// observer of the operation and the operations chain of completion.
884class SdpOfferAnswerHandler::RemoteDescriptionOperation {
885 public:
886 RemoteDescriptionOperation(
887 SdpOfferAnswerHandler* handler,
888 std::unique_ptr<SessionDescriptionInterface> desc,
889 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer,
890 std::function<void()> operations_chain_callback)
891 : handler_(handler),
892 desc_(std::move(desc)),
893 observer_(std::move(observer)),
Tomas Gunnarsson0dd75392022-01-17 18:19:56894 operations_chain_callback_(std::move(operations_chain_callback)),
895 unified_plan_(handler_->IsUnifiedPlan()) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04896 if (!desc_) {
Tomas Gunnarsson0dd75392022-01-17 18:19:56897 type_ = static_cast<SdpType>(-1);
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04898 InvalidParam("SessionDescription is NULL.");
899 } else {
900 type_ = desc_->GetType();
901 }
902 }
903
904 ~RemoteDescriptionOperation() {
905 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
906 SignalCompletion();
907 operations_chain_callback_();
908 }
909
910 bool ok() const { return error_.ok(); }
911
912 // Notifies the observer that the operation is complete and releases the
913 // reference to the observer.
914 void SignalCompletion() {
Tomas Gunnarsson0dd75392022-01-17 18:19:56915 if (!observer_)
916 return;
917
918 if (!error_.ok() && type_ != static_cast<SdpType>(-1)) {
919 std::string error_message =
920 GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type_, error_);
921 RTC_LOG(LS_ERROR) << error_message;
Danil Chapovalova2d85e42023-03-20 16:37:22922 error_.set_message(error_message);
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04923 }
Tomas Gunnarsson0dd75392022-01-17 18:19:56924
925 observer_->OnSetRemoteDescriptionComplete(error_);
926 observer_ = nullptr; // Only fire the notification once.
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04927 }
928
929 // If a session error has occurred the PeerConnection is in a possibly
930 // inconsistent state so fail right away.
931 bool HaveSessionError() {
932 RTC_DCHECK(ok());
Tomas Gunnarsson0dd75392022-01-17 18:19:56933 if (handler_->session_error() != SessionError::kNone)
934 InternalError(handler_->GetSessionErrorMsg());
935 return !ok();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04936 }
937
938 // Returns true if the operation was a rollback operation. If this function
939 // returns true, the caller should consider the operation complete. Otherwise
940 // proceed to the next step.
941 bool MaybeRollback() {
942 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
943 RTC_DCHECK(ok());
944 if (type_ != SdpType::kRollback) {
945 // Check if we can do an implicit rollback.
Tomas Gunnarsson0dd75392022-01-17 18:19:56946 if (type_ == SdpType::kOffer && unified_plan_ &&
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04947 handler_->pc_->configuration()->enable_implicit_rollback &&
948 handler_->signaling_state() ==
949 PeerConnectionInterface::kHaveLocalOffer) {
950 handler_->Rollback(type_);
951 }
952 return false;
953 }
954
Tomas Gunnarsson0dd75392022-01-17 18:19:56955 if (unified_plan_) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04956 error_ = handler_->Rollback(type_);
957 } else if (type_ == SdpType::kRollback) {
958 Unsupported("Rollback not supported in Plan B");
959 }
960
961 return true;
962 }
963
964 // Report to UMA the format of the received offer or answer.
965 void ReportOfferAnswerUma() {
966 RTC_DCHECK(ok());
967 if (type_ == SdpType::kOffer || type_ == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04968 handler_->pc_->ReportSdpBundleUsage(*desc_.get());
969 }
970 }
971
972 // Checks if the session description for the operation is valid. If not, the
973 // function captures error information and returns false. Note that if the
974 // return value is false, the operation should be considered done.
975 bool IsDescriptionValid() {
976 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
977 RTC_DCHECK(ok());
978 RTC_DCHECK(bundle_groups_by_mid_.empty()) << "Already called?";
979 bundle_groups_by_mid_ = GetBundleGroupsByMid(description());
980 error_ = handler_->ValidateSessionDescription(
981 desc_.get(), cricket::CS_REMOTE, bundle_groups_by_mid_);
Tomas Gunnarsson0dd75392022-01-17 18:19:56982 return ok();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04983 }
984
Tomas Gunnarsson0dd75392022-01-17 18:19:56985 // Transfers ownership of the session description object over to `handler_`.
Philipp Hancke43df03d2023-05-02 09:28:10986 bool ReplaceRemoteDescriptionAndCheckError() {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04987 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
988 RTC_DCHECK(ok());
Tomas Gunnarsson0dd75392022-01-17 18:19:56989 RTC_DCHECK(desc_);
990 RTC_DCHECK(!replaced_remote_description_);
991#if RTC_DCHECK_IS_ON
992 const auto* existing_remote_description = handler_->remote_description();
993#endif
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04994
Tomas Gunnarsson0dd75392022-01-17 18:19:56995 error_ = handler_->ReplaceRemoteDescription(std::move(desc_), type_,
996 &replaced_remote_description_);
997
998 if (ok()) {
999#if RTC_DCHECK_IS_ON
1000 // Sanity check that our `old_remote_description()` method always returns
1001 // the same value as `remote_description()` did before the call to
1002 // ReplaceRemoteDescription.
1003 RTC_DCHECK_EQ(existing_remote_description, old_remote_description());
1004#endif
1005 } else {
1006 SetAsSessionError();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041007 }
1008
Tomas Gunnarsson0dd75392022-01-17 18:19:561009 return ok();
1010 }
1011
1012 bool UpdateChannels() {
1013 RTC_DCHECK(ok());
1014 RTC_DCHECK(!desc_) << "ReplaceRemoteDescription hasn't been called";
1015
1016 const auto* remote_description = handler_->remote_description();
1017
1018 const cricket::SessionDescription* session_desc =
1019 remote_description->description();
1020
1021 // Transport and Media channels will be created only when offer is set.
1022 if (unified_plan_) {
1023 error_ = handler_->UpdateTransceiversAndDataChannels(
1024 cricket::CS_REMOTE, *remote_description,
1025 handler_->local_description(), old_remote_description(),
1026 bundle_groups_by_mid_);
1027 } else {
1028 // Media channels will be created only when offer is set. These may use
1029 // new transports just created by PushdownTransportDescription.
1030 if (type_ == SdpType::kOffer) {
1031 // TODO(mallinath) - Handle CreateChannel failure, as new local
1032 // description is applied. Restore back to old description.
1033 error_ = handler_->CreateChannels(*session_desc);
1034 }
1035 // Remove unused channels if MediaContentDescription is rejected.
1036 handler_->RemoveUnusedChannels(session_desc);
1037 }
1038
1039 return ok();
1040 }
1041
1042 bool UpdateSessionState() {
1043 RTC_DCHECK(ok());
1044 error_ = handler_->UpdateSessionState(
1045 type_, cricket::CS_REMOTE,
1046 handler_->remote_description()->description(), bundle_groups_by_mid_);
1047 if (!ok())
1048 SetAsSessionError();
1049 return ok();
1050 }
1051
1052 bool UseCandidatesInRemoteDescription() {
1053 RTC_DCHECK(ok());
1054 if (handler_->local_description() &&
1055 !handler_->UseCandidatesInRemoteDescription()) {
1056 InvalidParam(kInvalidCandidates);
1057 }
1058 return ok();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041059 }
1060
1061 // Convenience getter for desc_->GetType().
1062 SdpType type() const { return type_; }
Tomas Gunnarsson0dd75392022-01-17 18:19:561063 bool unified_plan() const { return unified_plan_; }
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041064 cricket::SessionDescription* description() { return desc_->description(); }
1065
Tomas Gunnarsson0dd75392022-01-17 18:19:561066 const SessionDescriptionInterface* old_remote_description() const {
1067 RTC_DCHECK(!desc_) << "Called before replacing the remote description";
1068 if (type_ == SdpType::kAnswer)
1069 return replaced_remote_description_.get();
1070 return replaced_remote_description_
1071 ? replaced_remote_description_.get()
1072 : handler_->current_remote_description();
1073 }
1074
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041075 // Returns a reference to a cached map of bundle groups ordered by mid.
1076 // Note that this will only be valid after a successful call to
1077 // `IsDescriptionValid`.
1078 const std::map<std::string, const cricket::ContentGroup*>&
1079 bundle_groups_by_mid() const {
1080 RTC_DCHECK(ok());
1081 return bundle_groups_by_mid_;
1082 }
1083
1084 private:
1085 // Convenience methods for populating the embedded `error_` object.
1086 void Unsupported(std::string message) {
1087 SetError(RTCErrorType::UNSUPPORTED_OPERATION, std::move(message));
1088 }
1089
1090 void InvalidParam(std::string message) {
1091 SetError(RTCErrorType::INVALID_PARAMETER, std::move(message));
1092 }
1093
Tomas Gunnarsson0dd75392022-01-17 18:19:561094 void InternalError(std::string message) {
1095 SetError(RTCErrorType::INTERNAL_ERROR, std::move(message));
1096 }
1097
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041098 void SetError(RTCErrorType type, std::string message) {
1099 RTC_DCHECK(ok()) << "Overwriting an existing error?";
1100 error_ = RTCError(type, std::move(message));
1101 }
1102
Tomas Gunnarsson0dd75392022-01-17 18:19:561103 // Called when the PeerConnection could be in an inconsistent state and we set
1104 // the session error so that future calls to
1105 // SetLocalDescription/SetRemoteDescription fail.
1106 void SetAsSessionError() {
1107 RTC_DCHECK(!ok());
1108 handler_->SetSessionError(SessionError::kContent, error_.message());
1109 }
1110
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041111 SdpOfferAnswerHandler* const handler_;
1112 std::unique_ptr<SessionDescriptionInterface> desc_;
Tomas Gunnarsson0dd75392022-01-17 18:19:561113 // Keeps the replaced session description object alive while the operation
1114 // is taking place since methods that depend on `old_remote_description()`
1115 // for updating the state, need it.
1116 std::unique_ptr<SessionDescriptionInterface> replaced_remote_description_;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041117 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer_;
1118 std::function<void()> operations_chain_callback_;
1119 RTCError error_ = RTCError::OK();
1120 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid_;
1121 SdpType type_;
Tomas Gunnarsson0dd75392022-01-17 18:19:561122 const bool unified_plan_;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041123};
Harald Alvestrandcdcfab02020-09-28 13:02:071124// Used by parameterless SetLocalDescription() to create an offer or answer.
1125// Upon completion of creating the session description, SetLocalDescription() is
1126// invoked with the result.
1127class SdpOfferAnswerHandler::ImplicitCreateSessionDescriptionObserver
1128 : public CreateSessionDescriptionObserver {
1129 public:
1130 ImplicitCreateSessionDescriptionObserver(
1131 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler,
1132 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
1133 set_local_description_observer)
1134 : sdp_handler_(std::move(sdp_handler)),
1135 set_local_description_observer_(
1136 std::move(set_local_description_observer)) {}
1137 ~ImplicitCreateSessionDescriptionObserver() override {
1138 RTC_DCHECK(was_called_);
1139 }
1140
1141 void SetOperationCompleteCallback(
1142 std::function<void()> operation_complete_callback) {
1143 operation_complete_callback_ = std::move(operation_complete_callback);
1144 }
1145
1146 bool was_called() const { return was_called_; }
1147
1148 void OnSuccess(SessionDescriptionInterface* desc_ptr) override {
1149 RTC_DCHECK(!was_called_);
1150 std::unique_ptr<SessionDescriptionInterface> desc(desc_ptr);
1151 was_called_ = true;
1152
Artem Titov880fa812021-07-30 20:30:231153 // Abort early if `pc_` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071154 if (!sdp_handler_) {
1155 operation_complete_callback_();
1156 return;
1157 }
1158 // DoSetLocalDescription() is a synchronous operation that invokes
Artem Titov880fa812021-07-30 20:30:231159 // `set_local_description_observer_` with the result.
Harald Alvestrandcdcfab02020-09-28 13:02:071160 sdp_handler_->DoSetLocalDescription(
1161 std::move(desc), std::move(set_local_description_observer_));
1162 operation_complete_callback_();
1163 }
1164
1165 void OnFailure(RTCError error) override {
1166 RTC_DCHECK(!was_called_);
1167 was_called_ = true;
1168 set_local_description_observer_->OnSetLocalDescriptionComplete(RTCError(
1169 error.type(), std::string("SetLocalDescription failed to create "
1170 "session description - ") +
1171 error.message()));
1172 operation_complete_callback_();
1173 }
1174
1175 private:
1176 bool was_called_ = false;
1177 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler_;
1178 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
1179 set_local_description_observer_;
1180 std::function<void()> operation_complete_callback_;
1181};
1182
1183// Wraps a CreateSessionDescriptionObserver and an OperationsChain operation
1184// complete callback. When the observer is invoked, the wrapped observer is
1185// invoked followed by invoking the completion callback.
1186class CreateSessionDescriptionObserverOperationWrapper
1187 : public CreateSessionDescriptionObserver {
1188 public:
1189 CreateSessionDescriptionObserverOperationWrapper(
1190 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer,
1191 std::function<void()> operation_complete_callback)
1192 : observer_(std::move(observer)),
1193 operation_complete_callback_(std::move(operation_complete_callback)) {
1194 RTC_DCHECK(observer_);
1195 }
1196 ~CreateSessionDescriptionObserverOperationWrapper() override {
Tomas Gunnarsson36992362020-10-05 19:41:361197#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071198 RTC_DCHECK(was_called_);
Tomas Gunnarsson36992362020-10-05 19:41:361199#endif
Harald Alvestrandcdcfab02020-09-28 13:02:071200 }
1201
1202 void OnSuccess(SessionDescriptionInterface* desc) override {
Tomas Gunnarsson36992362020-10-05 19:41:361203#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071204 RTC_DCHECK(!was_called_);
Harald Alvestrandcdcfab02020-09-28 13:02:071205 was_called_ = true;
1206#endif // RTC_DCHECK_IS_ON
1207 // Completing the operation before invoking the observer allows the observer
1208 // to execute SetLocalDescription() without delay.
1209 operation_complete_callback_();
1210 observer_->OnSuccess(desc);
1211 }
1212
1213 void OnFailure(RTCError error) override {
Tomas Gunnarsson36992362020-10-05 19:41:361214#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071215 RTC_DCHECK(!was_called_);
Harald Alvestrandcdcfab02020-09-28 13:02:071216 was_called_ = true;
1217#endif // RTC_DCHECK_IS_ON
1218 operation_complete_callback_();
1219 observer_->OnFailure(std::move(error));
1220 }
1221
1222 private:
Tomas Gunnarsson36992362020-10-05 19:41:361223#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071224 bool was_called_ = false;
1225#endif // RTC_DCHECK_IS_ON
1226 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer_;
1227 std::function<void()> operation_complete_callback_;
1228};
1229
1230// Wrapper for SetSessionDescriptionObserver that invokes the success or failure
1231// callback in a posted message handled by the peer connection. This introduces
1232// a delay that prevents recursive API calls by the observer, but this also
1233// means that the PeerConnection can be modified before the observer sees the
1234// result of the operation. This is ill-advised for synchronizing states.
1235//
1236// Implements both the SetLocalDescriptionObserverInterface and the
1237// SetRemoteDescriptionObserverInterface.
1238class SdpOfferAnswerHandler::SetSessionDescriptionObserverAdapter
1239 : public SetLocalDescriptionObserverInterface,
1240 public SetRemoteDescriptionObserverInterface {
1241 public:
1242 SetSessionDescriptionObserverAdapter(
1243 rtc::WeakPtr<SdpOfferAnswerHandler> handler,
1244 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer)
1245 : handler_(std::move(handler)),
1246 inner_observer_(std::move(inner_observer)) {}
1247
1248 // SetLocalDescriptionObserverInterface implementation.
1249 void OnSetLocalDescriptionComplete(RTCError error) override {
1250 OnSetDescriptionComplete(std::move(error));
1251 }
1252 // SetRemoteDescriptionObserverInterface implementation.
1253 void OnSetRemoteDescriptionComplete(RTCError error) override {
1254 OnSetDescriptionComplete(std::move(error));
1255 }
1256
1257 private:
1258 void OnSetDescriptionComplete(RTCError error) {
1259 if (!handler_)
1260 return;
1261 if (error.ok()) {
Harald Alvestrand1090e442020-10-05 07:01:091262 handler_->pc_->message_handler()->PostSetSessionDescriptionSuccess(
Niels Möllerafb246b2022-04-20 12:26:501263 inner_observer_.get());
Harald Alvestrandcdcfab02020-09-28 13:02:071264 } else {
Harald Alvestrand1090e442020-10-05 07:01:091265 handler_->pc_->message_handler()->PostSetSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:501266 inner_observer_.get(), std::move(error));
Harald Alvestrandcdcfab02020-09-28 13:02:071267 }
1268 }
1269
1270 rtc::WeakPtr<SdpOfferAnswerHandler> handler_;
1271 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer_;
1272};
1273
1274class SdpOfferAnswerHandler::LocalIceCredentialsToReplace {
1275 public:
1276 // Sets the ICE credentials that need restarting to the ICE credentials of
1277 // the current and pending descriptions.
1278 void SetIceCredentialsFromLocalDescriptions(
1279 const SessionDescriptionInterface* current_local_description,
1280 const SessionDescriptionInterface* pending_local_description) {
1281 ice_credentials_.clear();
1282 if (current_local_description) {
1283 AppendIceCredentialsFromSessionDescription(*current_local_description);
1284 }
1285 if (pending_local_description) {
1286 AppendIceCredentialsFromSessionDescription(*pending_local_description);
1287 }
1288 }
1289
1290 void ClearIceCredentials() { ice_credentials_.clear(); }
1291
1292 // Returns true if we have ICE credentials that need restarting.
1293 bool HasIceCredentials() const { return !ice_credentials_.empty(); }
1294
Artem Titov880fa812021-07-30 20:30:231295 // Returns true if `local_description` shares no ICE credentials with the
Harald Alvestrandcdcfab02020-09-28 13:02:071296 // ICE credentials that need restarting.
1297 bool SatisfiesIceRestart(
1298 const SessionDescriptionInterface& local_description) const {
1299 for (const auto& transport_info :
1300 local_description.description()->transport_infos()) {
1301 if (ice_credentials_.find(std::make_pair(
1302 transport_info.description.ice_ufrag,
1303 transport_info.description.ice_pwd)) != ice_credentials_.end()) {
1304 return false;
1305 }
1306 }
1307 return true;
1308 }
1309
1310 private:
1311 void AppendIceCredentialsFromSessionDescription(
1312 const SessionDescriptionInterface& desc) {
1313 for (const auto& transport_info : desc.description()->transport_infos()) {
1314 ice_credentials_.insert(
1315 std::make_pair(transport_info.description.ice_ufrag,
1316 transport_info.description.ice_pwd));
1317 }
1318 }
1319
1320 std::set<std::pair<std::string, std::string>> ice_credentials_;
1321};
1322
Harald Alvestrand66c40362022-01-28 17:41:301323SdpOfferAnswerHandler::SdpOfferAnswerHandler(PeerConnectionSdpMethods* pc,
1324 ConnectionContext* context)
Harald Alvestrandcdcfab02020-09-28 13:02:071325 : pc_(pc),
Harald Alvestrand66c40362022-01-28 17:41:301326 context_(context),
Harald Alvestrand6f04b6532020-10-09 11:42:171327 local_streams_(StreamCollection::Create()),
1328 remote_streams_(StreamCollection::Create()),
Harald Alvestrandcdcfab02020-09-28 13:02:071329 operations_chain_(rtc::OperationsChain::Create()),
Harald Alvestrandbc9ca252020-10-05 13:08:411330 rtcp_cname_(GenerateRtcpCname()),
Harald Alvestrandcdcfab02020-09-28 13:02:071331 local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()),
1332 weak_ptr_factory_(this) {
1333 operations_chain_->SetOnChainEmptyCallback(
1334 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr()]() {
1335 if (!this_weak_ptr)
1336 return;
1337 this_weak_ptr->OnOperationsChainEmpty();
1338 });
1339}
1340
1341SdpOfferAnswerHandler::~SdpOfferAnswerHandler() {}
1342
Harald Alvestrand9cd199d2020-10-27 07:10:431343// Static
1344std::unique_ptr<SdpOfferAnswerHandler> SdpOfferAnswerHandler::Create(
Harald Alvestrand5b661302022-01-28 13:08:341345 PeerConnectionSdpMethods* pc,
Harald Alvestrand9cd199d2020-10-27 07:10:431346 const PeerConnectionInterface::RTCConfiguration& configuration,
Harald Alvestrand66c40362022-01-28 17:41:301347 PeerConnectionDependencies& dependencies,
1348 ConnectionContext* context) {
1349 auto handler = absl::WrapUnique(new SdpOfferAnswerHandler(pc, context));
Jonas Orelanded99dae2022-03-09 08:28:101350 handler->Initialize(configuration, dependencies, context);
Harald Alvestrand9cd199d2020-10-27 07:10:431351 return handler;
1352}
1353
Harald Alvestrand763f5a92020-10-22 10:39:401354void SdpOfferAnswerHandler::Initialize(
1355 const PeerConnectionInterface::RTCConfiguration& configuration,
Jonas Orelanded99dae2022-03-09 08:28:101356 PeerConnectionDependencies& dependencies,
1357 ConnectionContext* context) {
Harald Alvestrand763f5a92020-10-22 10:39:401358 RTC_DCHECK_RUN_ON(signaling_thread());
Henrik Boström6344bf12022-05-10 07:34:271359 // 100 kbps is used by default, but can be overriden by a non-standard
1360 // RTCConfiguration value (not available on Web).
Harald Alvestrand763f5a92020-10-22 10:39:401361 video_options_.screencast_min_bitrate_kbps =
Henrik Boström6344bf12022-05-10 07:34:271362 configuration.screencast_min_bitrate.value_or(100);
Harald Alvestrand763f5a92020-10-22 10:39:401363
1364 audio_options_.audio_jitter_buffer_max_packets =
1365 configuration.audio_jitter_buffer_max_packets;
1366
1367 audio_options_.audio_jitter_buffer_fast_accelerate =
1368 configuration.audio_jitter_buffer_fast_accelerate;
1369
1370 audio_options_.audio_jitter_buffer_min_delay_ms =
1371 configuration.audio_jitter_buffer_min_delay_ms;
1372
Harald Alvestrand763f5a92020-10-22 10:39:401373 // Obtain a certificate from RTCConfiguration if any were provided (optional).
1374 rtc::scoped_refptr<rtc::RTCCertificate> certificate;
1375 if (!configuration.certificates.empty()) {
1376 // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of
1377 // just picking the first one. The decision should be made based on the DTLS
1378 // handshake. The DTLS negotiations need to know about all certificates.
1379 certificate = configuration.certificates[0];
1380 }
1381
1382 webrtc_session_desc_factory_ =
1383 std::make_unique<WebRtcSessionDescriptionFactory>(
Jonas Orelanded99dae2022-03-09 08:28:101384 context, this, pc_->session_id(), pc_->dtls_enabled(),
Danil Chapovalovb7da8162022-08-22 14:39:341385 std::move(dependencies.cert_generator), std::move(certificate),
Harald Alvestranda0947872020-11-09 14:15:001386 [this](const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
Harald Alvestrandbc32c562022-02-09 12:08:471387 RTC_DCHECK_RUN_ON(signaling_thread());
1388 transport_controller_s()->SetLocalCertificate(certificate);
Jonas Oreland6c7f9842022-04-19 15:24:101389 },
1390 pc_->trials());
Harald Alvestrand763f5a92020-10-22 10:39:401391
Harald Alvestrand0d018412021-11-04 13:52:311392 if (pc_->options()->disable_encryption) {
Harald Alvestrand974044e2024-02-08 13:15:511393 RTC_LOG(LS_INFO)
1394 << "Disabling encryption. This should only be done in tests.";
1395 webrtc_session_desc_factory_->SetInsecureForTesting();
Harald Alvestrand0d018412021-11-04 13:52:311396 }
1397
Harald Alvestrand763f5a92020-10-22 10:39:401398 webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions(
1399 pc_->GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions);
1400 webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan());
1401
Harald Alvestrand9cd199d2020-10-27 07:10:431402 if (dependencies.video_bitrate_allocator_factory) {
Harald Alvestrand763f5a92020-10-22 10:39:401403 video_bitrate_allocator_factory_ =
Harald Alvestrand9cd199d2020-10-27 07:10:431404 std::move(dependencies.video_bitrate_allocator_factory);
Harald Alvestrand763f5a92020-10-22 10:39:401405 } else {
1406 video_bitrate_allocator_factory_ =
1407 CreateBuiltinVideoBitrateAllocatorFactory();
1408 }
1409}
1410
Harald Alvestrandbc9ca252020-10-05 13:08:411411// ==================================================================
Artem Titovc6c02ef2022-05-09 08:30:091412// Access to pc_ variables
Harald Alvestrand35f4b4c2022-05-16 10:36:431413cricket::MediaEngineInterface* SdpOfferAnswerHandler::media_engine() const {
1414 RTC_DCHECK(context_);
Harald Alvestrandc3fa7c32022-05-22 10:57:011415 return context_->media_engine();
Harald Alvestrand35f4b4c2022-05-16 10:36:431416}
1417
Harald Alvestrande15fb152020-10-19 13:28:051418TransceiverList* SdpOfferAnswerHandler::transceivers() {
1419 if (!pc_->rtp_manager()) {
1420 return nullptr;
1421 }
1422 return pc_->rtp_manager()->transceivers();
Harald Alvestrandbc9ca252020-10-05 13:08:411423}
Harald Alvestrand35f4b4c2022-05-16 10:36:431424
Harald Alvestrande15fb152020-10-19 13:28:051425const TransceiverList* SdpOfferAnswerHandler::transceivers() const {
1426 if (!pc_->rtp_manager()) {
1427 return nullptr;
1428 }
1429 return pc_->rtp_manager()->transceivers();
Harald Alvestrandbc9ca252020-10-05 13:08:411430}
Harald Alvestrandbc32c562022-02-09 12:08:471431JsepTransportController* SdpOfferAnswerHandler::transport_controller_s() {
1432 return pc_->transport_controller_s();
Harald Alvestrandbc9ca252020-10-05 13:08:411433}
Harald Alvestrandbc32c562022-02-09 12:08:471434JsepTransportController* SdpOfferAnswerHandler::transport_controller_n() {
1435 return pc_->transport_controller_n();
1436}
1437const JsepTransportController* SdpOfferAnswerHandler::transport_controller_s()
Harald Alvestrandf01bd6c2020-10-23 13:30:461438 const {
Harald Alvestrandbc32c562022-02-09 12:08:471439 return pc_->transport_controller_s();
1440}
1441const JsepTransportController* SdpOfferAnswerHandler::transport_controller_n()
1442 const {
1443 return pc_->transport_controller_n();
Harald Alvestrandf01bd6c2020-10-23 13:30:461444}
Harald Alvestrandbc9ca252020-10-05 13:08:411445DataChannelController* SdpOfferAnswerHandler::data_channel_controller() {
Harald Alvestrand653429c2020-10-19 16:05:201446 return pc_->data_channel_controller();
Harald Alvestrandbc9ca252020-10-05 13:08:411447}
1448const DataChannelController* SdpOfferAnswerHandler::data_channel_controller()
1449 const {
Harald Alvestrand653429c2020-10-19 16:05:201450 return pc_->data_channel_controller();
Harald Alvestrandbc9ca252020-10-05 13:08:411451}
1452cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() {
Harald Alvestrand653429c2020-10-19 16:05:201453 return pc_->port_allocator();
Harald Alvestrandbc9ca252020-10-05 13:08:411454}
1455const cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() const {
Harald Alvestrand653429c2020-10-19 16:05:201456 return pc_->port_allocator();
Harald Alvestrandbc9ca252020-10-05 13:08:411457}
Harald Alvestrande15fb152020-10-19 13:28:051458RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() {
1459 return pc_->rtp_manager();
1460}
1461const RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() const {
1462 return pc_->rtp_manager();
1463}
Harald Alvestrandbc9ca252020-10-05 13:08:411464
1465// ===================================================================
1466
Harald Alvestrandcdcfab02020-09-28 13:02:071467void SdpOfferAnswerHandler::PrepareForShutdown() {
1468 RTC_DCHECK_RUN_ON(signaling_thread());
1469 weak_ptr_factory_.InvalidateWeakPtrs();
1470}
1471
1472void SdpOfferAnswerHandler::Close() {
1473 ChangeSignalingState(PeerConnectionInterface::kClosed);
1474}
1475
1476void SdpOfferAnswerHandler::RestartIce() {
1477 RTC_DCHECK_RUN_ON(signaling_thread());
1478 local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions(
1479 current_local_description(), pending_local_description());
1480 UpdateNegotiationNeeded();
1481}
1482
1483rtc::Thread* SdpOfferAnswerHandler::signaling_thread() const {
Harald Alvestrand66c40362022-01-28 17:41:301484 return context_->signaling_thread();
Harald Alvestrandcdcfab02020-09-28 13:02:071485}
1486
Harald Alvestrandbc32c562022-02-09 12:08:471487rtc::Thread* SdpOfferAnswerHandler::network_thread() const {
1488 return context_->network_thread();
1489}
1490
Harald Alvestrandcdcfab02020-09-28 13:02:071491void SdpOfferAnswerHandler::CreateOffer(
1492 CreateSessionDescriptionObserver* observer,
1493 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
1494 RTC_DCHECK_RUN_ON(signaling_thread());
1495 // Chain this operation. If asynchronous operations are pending on the chain,
1496 // this operation will be queued to be invoked, otherwise the contents of the
1497 // lambda will execute immediately.
1498 operations_chain_->ChainOperation(
1499 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1500 observer_refptr =
1501 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
1502 options](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 20:30:231503 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071504 if (!this_weak_ptr) {
1505 observer_refptr->OnFailure(
1506 RTCError(RTCErrorType::INTERNAL_ERROR,
1507 "CreateOffer failed because the session was shut down"));
1508 operations_chain_callback();
1509 return;
1510 }
1511 // The operation completes asynchronously when the wrapper is invoked.
Niels Möllerb7aac6f52021-08-23 13:48:061512 auto observer_wrapper = rtc::make_ref_counted<
1513 CreateSessionDescriptionObserverOperationWrapper>(
1514 std::move(observer_refptr), std::move(operations_chain_callback));
Harald Alvestrandcdcfab02020-09-28 13:02:071515 this_weak_ptr->DoCreateOffer(options, observer_wrapper);
1516 });
1517}
1518
1519void SdpOfferAnswerHandler::SetLocalDescription(
1520 SetSessionDescriptionObserver* observer,
1521 SessionDescriptionInterface* desc_ptr) {
1522 RTC_DCHECK_RUN_ON(signaling_thread());
1523 // Chain this operation. If asynchronous operations are pending on the chain,
1524 // this operation will be queued to be invoked, otherwise the contents of the
1525 // lambda will execute immediately.
1526 operations_chain_->ChainOperation(
1527 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1528 observer_refptr =
1529 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1530 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1531 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 20:30:231532 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071533 if (!this_weak_ptr) {
1534 // For consistency with SetSessionDescriptionObserverAdapter whose
1535 // posted messages doesn't get processed when the PC is destroyed, we
Artem Titov880fa812021-07-30 20:30:231536 // do not inform `observer_refptr` that the operation failed.
Harald Alvestrandcdcfab02020-09-28 13:02:071537 operations_chain_callback();
1538 return;
1539 }
1540 // SetSessionDescriptionObserverAdapter takes care of making sure the
Artem Titov880fa812021-07-30 20:30:231541 // `observer_refptr` is invoked in a posted message.
Harald Alvestrandcdcfab02020-09-28 13:02:071542 this_weak_ptr->DoSetLocalDescription(
1543 std::move(desc),
Niels Möllerb7aac6f52021-08-23 13:48:061544 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1545 this_weak_ptr, observer_refptr));
Harald Alvestrandcdcfab02020-09-28 13:02:071546 // For backwards-compatability reasons, we declare the operation as
1547 // completed here (rather than in a post), so that the operation chain
1548 // is not blocked by this operation when the observer is invoked. This
1549 // allows the observer to trigger subsequent offer/answer operations
1550 // synchronously if the operation chain is now empty.
1551 operations_chain_callback();
1552 });
1553}
1554
1555void SdpOfferAnswerHandler::SetLocalDescription(
1556 std::unique_ptr<SessionDescriptionInterface> desc,
1557 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1558 RTC_DCHECK_RUN_ON(signaling_thread());
1559 // Chain this operation. If asynchronous operations are pending on the chain,
1560 // this operation will be queued to be invoked, otherwise the contents of the
1561 // lambda will execute immediately.
1562 operations_chain_->ChainOperation(
1563 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1564 desc = std::move(desc)](
1565 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 20:30:231566 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071567 if (!this_weak_ptr) {
1568 observer->OnSetLocalDescriptionComplete(RTCError(
1569 RTCErrorType::INTERNAL_ERROR,
1570 "SetLocalDescription failed because the session was shut down"));
1571 operations_chain_callback();
1572 return;
1573 }
1574 this_weak_ptr->DoSetLocalDescription(std::move(desc), observer);
1575 // DoSetLocalDescription() is implemented as a synchronous operation.
Artem Titov880fa812021-07-30 20:30:231576 // The `observer` will already have been informed that it completed, and
Harald Alvestrandcdcfab02020-09-28 13:02:071577 // we can mark this operation as complete without any loose ends.
1578 operations_chain_callback();
1579 });
1580}
1581
1582void SdpOfferAnswerHandler::SetLocalDescription(
1583 SetSessionDescriptionObserver* observer) {
1584 RTC_DCHECK_RUN_ON(signaling_thread());
1585 SetLocalDescription(
Niels Möllerb7aac6f52021-08-23 13:48:061586 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
Niels Möllere7cc8832022-01-04 14:20:031587 weak_ptr_factory_.GetWeakPtr(),
1588 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer)));
Harald Alvestrandcdcfab02020-09-28 13:02:071589}
1590
1591void SdpOfferAnswerHandler::SetLocalDescription(
1592 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1593 RTC_DCHECK_RUN_ON(signaling_thread());
Artem Titov880fa812021-07-30 20:30:231594 // The `create_sdp_observer` handles performing DoSetLocalDescription() with
Harald Alvestrandcdcfab02020-09-28 13:02:071595 // the resulting description as well as completing the operation.
Niels Möllerb7aac6f52021-08-23 13:48:061596 auto create_sdp_observer =
1597 rtc::make_ref_counted<ImplicitCreateSessionDescriptionObserver>(
1598 weak_ptr_factory_.GetWeakPtr(), observer);
Harald Alvestrandcdcfab02020-09-28 13:02:071599 // Chain this operation. If asynchronous operations are pending on the chain,
1600 // this operation will be queued to be invoked, otherwise the contents of the
1601 // lambda will execute immediately.
1602 operations_chain_->ChainOperation(
1603 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1604 create_sdp_observer](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 20:30:231605 // The `create_sdp_observer` is responsible for completing the
Harald Alvestrandcdcfab02020-09-28 13:02:071606 // operation.
1607 create_sdp_observer->SetOperationCompleteCallback(
1608 std::move(operations_chain_callback));
Artem Titov880fa812021-07-30 20:30:231609 // Abort early if `this_weak_ptr` is no longer valid. This triggers the
Harald Alvestrandcdcfab02020-09-28 13:02:071610 // same code path as if DoCreateOffer() or DoCreateAnswer() failed.
1611 if (!this_weak_ptr) {
1612 create_sdp_observer->OnFailure(RTCError(
1613 RTCErrorType::INTERNAL_ERROR,
1614 "SetLocalDescription failed because the session was shut down"));
1615 return;
1616 }
1617 switch (this_weak_ptr->signaling_state()) {
1618 case PeerConnectionInterface::kStable:
1619 case PeerConnectionInterface::kHaveLocalOffer:
1620 case PeerConnectionInterface::kHaveRemotePrAnswer:
1621 // TODO(hbos): If [LastCreatedOffer] exists and still represents the
1622 // current state of the system, use that instead of creating another
1623 // offer.
1624 this_weak_ptr->DoCreateOffer(
1625 PeerConnectionInterface::RTCOfferAnswerOptions(),
1626 create_sdp_observer);
1627 break;
1628 case PeerConnectionInterface::kHaveLocalPrAnswer:
1629 case PeerConnectionInterface::kHaveRemoteOffer:
1630 // TODO(hbos): If [LastCreatedAnswer] exists and still represents
1631 // the current state of the system, use that instead of creating
1632 // another answer.
1633 this_weak_ptr->DoCreateAnswer(
1634 PeerConnectionInterface::RTCOfferAnswerOptions(),
1635 create_sdp_observer);
1636 break;
1637 case PeerConnectionInterface::kClosed:
1638 create_sdp_observer->OnFailure(RTCError(
1639 RTCErrorType::INVALID_STATE,
1640 "SetLocalDescription called when PeerConnection is closed."));
1641 break;
1642 }
1643 });
1644}
1645
1646RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
Henrik Boströmf8187e02021-04-26 19:04:261647 std::unique_ptr<SessionDescriptionInterface> desc,
1648 const std::map<std::string, const cricket::ContentGroup*>&
1649 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:461650 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyLocalDescription");
Harald Alvestrandcdcfab02020-09-28 13:02:071651 RTC_DCHECK_RUN_ON(signaling_thread());
1652 RTC_DCHECK(desc);
1653
Philipp Hancke7baa63f2022-09-01 13:39:501654 // Invalidate the stats caches to make sure that they get
1655 // updated the next time getStats() gets called, as updating the session
1656 // description affects the stats.
1657 pc_->ClearStatsCache();
Harald Alvestrandcdcfab02020-09-28 13:02:071658
1659 // Take a reference to the old local description since it's used below to
1660 // compare against the new local description. When setting the new local
1661 // description, grab ownership of the replaced session description in case it
Artem Titov880fa812021-07-30 20:30:231662 // is the same as `old_local_description`, to keep it alive for the duration
Harald Alvestrandcdcfab02020-09-28 13:02:071663 // of the method.
1664 const SessionDescriptionInterface* old_local_description =
1665 local_description();
1666 std::unique_ptr<SessionDescriptionInterface> replaced_local_description;
1667 SdpType type = desc->GetType();
1668 if (type == SdpType::kAnswer) {
1669 replaced_local_description = pending_local_description_
1670 ? std::move(pending_local_description_)
1671 : std::move(current_local_description_);
1672 current_local_description_ = std::move(desc);
1673 pending_local_description_ = nullptr;
1674 current_remote_description_ = std::move(pending_remote_description_);
1675 } else {
1676 replaced_local_description = std::move(pending_local_description_);
1677 pending_local_description_ = std::move(desc);
1678 }
Harald Alvestrandbc32c562022-02-09 12:08:471679 if (!initial_offerer_) {
1680 initial_offerer_.emplace(type == SdpType::kOffer);
1681 }
Harald Alvestrandcdcfab02020-09-28 13:02:071682 // The session description to apply now must be accessed by
Artem Titov880fa812021-07-30 20:30:231683 // `local_description()`.
Harald Alvestrandcdcfab02020-09-28 13:02:071684 RTC_DCHECK(local_description());
1685
Harald Alvestrandcdcfab02020-09-28 13:02:071686 if (!is_caller_) {
1687 if (remote_description()) {
1688 // Remote description was applied first, so this PC is the callee.
1689 is_caller_ = false;
1690 } else {
1691 // Local description is applied first, so this PC is the caller.
1692 is_caller_ = true;
1693 }
1694 }
1695
Harald Alvestranda474fbf2020-10-01 16:47:231696 RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type);
Harald Alvestrandcdcfab02020-09-28 13:02:071697 if (!error.ok()) {
1698 return error;
1699 }
1700
1701 if (IsUnifiedPlan()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561702 error = UpdateTransceiversAndDataChannels(
Harald Alvestrandcdcfab02020-09-28 13:02:071703 cricket::CS_LOCAL, *local_description(), old_local_description,
Henrik Boströmf8187e02021-04-26 19:04:261704 remote_description(), bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:071705 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561706 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1707 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:071708 return error;
1709 }
Harald Alvestrand8101e7b2022-05-23 14:57:471710 if (ConfiguredForMedia()) {
1711 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
1712 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
1713 for (const auto& transceiver_ext : transceivers()->List()) {
1714 auto transceiver = transceiver_ext->internal();
1715 if (transceiver->stopped()) {
1716 continue;
Harald Alvestrandc48ad732022-05-06 15:15:341717 }
Harald Alvestrand8101e7b2022-05-23 14:57:471718
1719 // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots.
1720 // Note that code paths that don't set MID won't be able to use
1721 // information about DTLS transports.
1722 if (transceiver->mid()) {
1723 auto dtls_transport = LookupDtlsTransportByMid(
1724 context_->network_thread(), transport_controller_s(),
1725 *transceiver->mid());
1726 transceiver->sender_internal()->set_transport(dtls_transport);
1727 transceiver->receiver_internal()->set_transport(dtls_transport);
1728 }
1729
1730 const ContentInfo* content =
1731 FindMediaSectionForTransceiver(transceiver, local_description());
1732 if (!content) {
1733 continue;
1734 }
1735 const MediaContentDescription* media_desc =
1736 content->media_description();
1737 // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run
1738 // the following steps:
1739 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
1740 // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and
1741 // transceiver's [[FiredDirection]] slot is either "sendrecv" or
1742 // "recvonly", process the removal of a remote track for the media
1743 // description, given transceiver, removeList, and muteTracks.
1744 if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
1745 (transceiver->fired_direction() &&
1746 RtpTransceiverDirectionHasRecv(
1747 *transceiver->fired_direction()))) {
1748 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
1749 &removed_streams);
1750 }
1751 // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and
1752 // [[FiredDirection]] slots to direction.
1753 transceiver->set_current_direction(media_desc->direction());
1754 transceiver->set_fired_direction(media_desc->direction());
1755 }
Harald Alvestrandcdcfab02020-09-28 13:02:071756 }
Harald Alvestrand8101e7b2022-05-23 14:57:471757 auto observer = pc_->Observer();
1758 for (const auto& transceiver : remove_list) {
1759 observer->OnRemoveTrack(transceiver->receiver());
1760 }
1761 for (const auto& stream : removed_streams) {
1762 observer->OnRemoveStream(stream);
1763 }
Harald Alvestrandcdcfab02020-09-28 13:02:071764 }
1765 } else {
1766 // Media channels will be created only when offer is set. These may use new
1767 // transports just created by PushdownTransportDescription.
1768 if (type == SdpType::kOffer) {
1769 // TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local
1770 // description is applied. Restore back to old description.
Philipp Hancke2206b632023-07-18 05:23:021771 error = CreateChannels(*local_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:071772 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561773 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1774 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:071775 return error;
1776 }
1777 }
1778 // Remove unused channels if MediaContentDescription is rejected.
Harald Alvestranda474fbf2020-10-01 16:47:231779 RemoveUnusedChannels(local_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:071780 }
1781
1782 error = UpdateSessionState(type, cricket::CS_LOCAL,
Henrik Boströmf8187e02021-04-26 19:04:261783 local_description()->description(),
1784 bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:071785 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561786 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1787 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:071788 return error;
1789 }
1790
Tomas Gunnarsson0dd75392022-01-17 18:19:561791 // Now that we have a local description, we can push down remote candidates.
1792 UseCandidatesInRemoteDescription();
Harald Alvestrandcdcfab02020-09-28 13:02:071793
1794 pending_ice_restarts_.clear();
Harald Alvestranda474fbf2020-10-01 16:47:231795 if (session_error() != SessionError::kNone) {
1796 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg());
Harald Alvestrandcdcfab02020-09-28 13:02:071797 }
1798
1799 // If setting the description decided our SSL role, allocate any necessary
1800 // SCTP sids.
Tommic61eee22023-03-22 07:25:381801 AllocateSctpSids();
Harald Alvestrandcdcfab02020-09-28 13:02:071802
Philipp Hancke2206b632023-07-18 05:23:021803 // Validate SSRCs, we do not allow duplicates.
1804 if (ConfiguredForMedia()) {
1805 std::set<uint32_t> used_ssrcs;
1806 for (const auto& content : local_description()->description()->contents()) {
1807 for (const auto& stream : content.media_description()->streams()) {
1808 for (uint32_t ssrc : stream.ssrcs) {
1809 auto result = used_ssrcs.insert(ssrc);
1810 if (!result.second) {
1811 LOG_AND_RETURN_ERROR(
1812 RTCErrorType::INVALID_PARAMETER,
1813 "Duplicate ssrc " + rtc::ToString(ssrc) + " is not allowed");
1814 }
1815 }
1816 }
1817 }
1818 }
1819
Harald Alvestrandcdcfab02020-09-28 13:02:071820 if (IsUnifiedPlan()) {
Harald Alvestrand8101e7b2022-05-23 14:57:471821 if (ConfiguredForMedia()) {
1822 // We must use List and not ListInternal here because
1823 // transceivers()->StableState() is indexed by the non-internal refptr.
1824 for (const auto& transceiver_ext : transceivers()->List()) {
1825 auto transceiver = transceiver_ext->internal();
1826 if (transceiver->stopped()) {
1827 continue;
1828 }
1829 const ContentInfo* content =
1830 FindMediaSectionForTransceiver(transceiver, local_description());
1831 if (!content) {
1832 continue;
1833 }
1834 cricket::ChannelInterface* channel = transceiver->channel();
1835 if (content->rejected || !channel || channel->local_streams().empty()) {
1836 // 0 is a special value meaning "this sender has no associated send
1837 // stream". Need to call this so the sender won't attempt to configure
1838 // a no longer existing stream and run into DCHECKs in the lower
1839 // layers.
1840 transceiver->sender_internal()->SetSsrc(0);
1841 } else {
1842 // Get the StreamParams from the channel which could generate SSRCs.
1843 const std::vector<StreamParams>& streams = channel->local_streams();
1844 transceiver->sender_internal()->set_stream_ids(
1845 streams[0].stream_ids());
1846 auto encodings =
1847 transceiver->sender_internal()->init_send_encodings();
1848 transceiver->sender_internal()->SetSsrc(streams[0].first_ssrc());
1849 if (!encodings.empty()) {
1850 transceivers()
1851 ->StableState(transceiver_ext)
1852 ->SetInitSendEncodings(encodings);
1853 }
Eldar Rello950d6b92021-04-06 19:38:001854 }
Harald Alvestrandcdcfab02020-09-28 13:02:071855 }
1856 }
1857 } else {
1858 // Plan B semantics.
1859
1860 // Update state and SSRC of local MediaStreams and DataChannels based on the
1861 // local session description.
1862 const cricket::ContentInfo* audio_content =
1863 GetFirstAudioContent(local_description()->description());
1864 if (audio_content) {
1865 if (audio_content->rejected) {
Harald Alvestranda474fbf2020-10-01 16:47:231866 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
Harald Alvestrandcdcfab02020-09-28 13:02:071867 } else {
Philipp Hancked0f0f382023-11-23 19:21:051868 const cricket::MediaContentDescription* audio_desc =
1869 audio_content->media_description();
Harald Alvestranda474fbf2020-10-01 16:47:231870 UpdateLocalSenders(audio_desc->streams(), audio_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:071871 }
1872 }
1873
1874 const cricket::ContentInfo* video_content =
1875 GetFirstVideoContent(local_description()->description());
1876 if (video_content) {
1877 if (video_content->rejected) {
Harald Alvestranda474fbf2020-10-01 16:47:231878 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
Harald Alvestrandcdcfab02020-09-28 13:02:071879 } else {
Philipp Hancked0f0f382023-11-23 19:21:051880 const cricket::MediaContentDescription* video_desc =
1881 video_content->media_description();
Harald Alvestranda474fbf2020-10-01 16:47:231882 UpdateLocalSenders(video_desc->streams(), video_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:071883 }
1884 }
1885 }
1886
Harald Alvestrand7af57c62021-04-16 11:12:141887 // This function does nothing with data content.
Harald Alvestrandcdcfab02020-09-28 13:02:071888
1889 if (type == SdpType::kAnswer &&
1890 local_ice_credentials_to_replace_->SatisfiesIceRestart(
1891 *current_local_description_)) {
1892 local_ice_credentials_to_replace_->ClearIceCredentials();
1893 }
1894
Philipp Hancke49e55872023-03-30 11:51:391895 if (IsUnifiedPlan()) {
1896 UpdateRtpHeaderExtensionPreferencesFromSdpMunging(
1897 local_description()->description(), transceivers());
1898 }
1899
Harald Alvestrandcdcfab02020-09-28 13:02:071900 return RTCError::OK();
1901}
1902
1903void SdpOfferAnswerHandler::SetRemoteDescription(
1904 SetSessionDescriptionObserver* observer,
1905 SessionDescriptionInterface* desc_ptr) {
1906 RTC_DCHECK_RUN_ON(signaling_thread());
1907 // Chain this operation. If asynchronous operations are pending on the chain,
1908 // this operation will be queued to be invoked, otherwise the contents of the
1909 // lambda will execute immediately.
1910 operations_chain_->ChainOperation(
1911 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1912 observer_refptr =
1913 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1914 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1915 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 20:30:231916 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071917 if (!this_weak_ptr) {
1918 // For consistency with SetSessionDescriptionObserverAdapter whose
1919 // posted messages doesn't get processed when the PC is destroyed, we
Artem Titov880fa812021-07-30 20:30:231920 // do not inform `observer_refptr` that the operation failed.
Harald Alvestrandcdcfab02020-09-28 13:02:071921 operations_chain_callback();
1922 return;
1923 }
1924 // SetSessionDescriptionObserverAdapter takes care of making sure the
Artem Titov880fa812021-07-30 20:30:231925 // `observer_refptr` is invoked in a posted message.
Harald Alvestrandcdcfab02020-09-28 13:02:071926 this_weak_ptr->DoSetRemoteDescription(
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041927 std::make_unique<RemoteDescriptionOperation>(
1928 this_weak_ptr.get(), std::move(desc),
1929 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1930 this_weak_ptr, observer_refptr),
1931 std::move(operations_chain_callback)));
Harald Alvestrandcdcfab02020-09-28 13:02:071932 });
1933}
1934
1935void SdpOfferAnswerHandler::SetRemoteDescription(
1936 std::unique_ptr<SessionDescriptionInterface> desc,
1937 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer) {
1938 RTC_DCHECK_RUN_ON(signaling_thread());
1939 // Chain this operation. If asynchronous operations are pending on the chain,
1940 // this operation will be queued to be invoked, otherwise the contents of the
1941 // lambda will execute immediately.
1942 operations_chain_->ChainOperation(
1943 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1944 desc = std::move(desc)](
1945 std::function<void()> operations_chain_callback) mutable {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041946 if (!observer) {
1947 RTC_DLOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
1948 operations_chain_callback();
1949 return;
1950 }
1951
Artem Titov880fa812021-07-30 20:30:231952 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071953 if (!this_weak_ptr) {
1954 observer->OnSetRemoteDescriptionComplete(RTCError(
1955 RTCErrorType::INTERNAL_ERROR,
1956 "SetRemoteDescription failed because the session was shut down"));
1957 operations_chain_callback();
1958 return;
1959 }
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041960
1961 this_weak_ptr->DoSetRemoteDescription(
1962 std::make_unique<RemoteDescriptionOperation>(
1963 this_weak_ptr.get(), std::move(desc), std::move(observer),
1964 std::move(operations_chain_callback)));
Harald Alvestrandcdcfab02020-09-28 13:02:071965 });
1966}
1967
Tomas Gunnarsson0dd75392022-01-17 18:19:561968RTCError SdpOfferAnswerHandler::ReplaceRemoteDescription(
Henrik Boströmf8187e02021-04-26 19:04:261969 std::unique_ptr<SessionDescriptionInterface> desc,
Tomas Gunnarsson0dd75392022-01-17 18:19:561970 SdpType sdp_type,
1971 std::unique_ptr<SessionDescriptionInterface>* replaced_description) {
1972 RTC_DCHECK(replaced_description);
1973 if (sdp_type == SdpType::kAnswer) {
1974 *replaced_description = pending_remote_description_
1975 ? std::move(pending_remote_description_)
1976 : std::move(current_remote_description_);
1977 current_remote_description_ = std::move(desc);
1978 pending_remote_description_ = nullptr;
1979 current_local_description_ = std::move(pending_local_description_);
1980 } else {
1981 *replaced_description = std::move(pending_remote_description_);
1982 pending_remote_description_ = std::move(desc);
1983 }
1984
1985 // The session description to apply now must be accessed by
1986 // `remote_description()`.
1987 const cricket::SessionDescription* session_desc =
1988 remote_description()->description();
1989
Tommic5605202024-01-11 21:15:271990 const auto* local = local_description();
1991
Danil Chapovalov9e09a1f2022-09-08 16:38:101992 // NOTE: This will perform a BlockingCall() to the network thread.
Tommic5605202024-01-11 21:15:271993 return transport_controller_s()->SetRemoteDescription(
1994 sdp_type, local ? local->description() : nullptr, session_desc);
Tomas Gunnarsson0dd75392022-01-17 18:19:561995}
1996
1997void SdpOfferAnswerHandler::ApplyRemoteDescription(
1998 std::unique_ptr<RemoteDescriptionOperation> operation) {
Markus Handell518669d2021-06-07 11:30:461999 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyRemoteDescription");
Harald Alvestrandcdcfab02020-09-28 13:02:072000 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson0dd75392022-01-17 18:19:562001 RTC_DCHECK(operation->description());
Harald Alvestrandcdcfab02020-09-28 13:02:072002
Philipp Hancke7baa63f2022-09-01 13:39:502003 // Invalidate the stats caches to make sure that they get
2004 // updated next time getStats() gets called, as updating the session
2005 // description affects the stats.
2006 pc_->ClearStatsCache();
Harald Alvestrandcdcfab02020-09-28 13:02:072007
Philipp Hancke43df03d2023-05-02 09:28:102008 if (!operation->ReplaceRemoteDescriptionAndCheckError())
Tomas Gunnarsson0dd75392022-01-17 18:19:562009 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072010
Tomas Gunnarsson0dd75392022-01-17 18:19:562011 if (!operation->UpdateChannels())
2012 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072013
2014 // NOTE: Candidates allocation will be initiated only when
2015 // SetLocalDescription is called.
Tomas Gunnarsson0dd75392022-01-17 18:19:562016 if (!operation->UpdateSessionState())
2017 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072018
Tomas Gunnarsson0dd75392022-01-17 18:19:562019 if (!operation->UseCandidatesInRemoteDescription())
2020 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072021
Tomas Gunnarsson0dd75392022-01-17 18:19:562022 if (operation->old_remote_description()) {
Harald Alvestrandcdcfab02020-09-28 13:02:072023 for (const cricket::ContentInfo& content :
Tomas Gunnarsson0dd75392022-01-17 18:19:562024 operation->old_remote_description()->description()->contents()) {
Harald Alvestrandcdcfab02020-09-28 13:02:072025 // Check if this new SessionDescription contains new ICE ufrag and
2026 // password that indicates the remote peer requests an ICE restart.
2027 // TODO(deadbeef): When we start storing both the current and pending
2028 // remote description, this should reset pending_ice_restarts and compare
2029 // against the current description.
Tomas Gunnarsson0dd75392022-01-17 18:19:562030 if (CheckForRemoteIceRestart(operation->old_remote_description(),
2031 remote_description(), content.name)) {
2032 if (operation->type() == SdpType::kOffer) {
Harald Alvestrandcdcfab02020-09-28 13:02:072033 pending_ice_restarts_.insert(content.name);
2034 }
2035 } else {
2036 // We retain all received candidates only if ICE is not restarted.
2037 // When ICE is restarted, all previous candidates belong to an old
2038 // generation and should not be kept.
2039 // TODO(deadbeef): This goes against the W3C spec which says the remote
2040 // description should only contain candidates from the last set remote
2041 // description plus any candidates added since then. We should remove
2042 // this once we're sure it won't break anything.
2043 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
Tomas Gunnarsson0dd75392022-01-17 18:19:562044 operation->old_remote_description(), content.name,
2045 mutable_remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:072046 }
2047 }
2048 }
2049
Tomas Gunnarsson0dd75392022-01-17 18:19:562050 if (operation->HaveSessionError())
2051 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072052
2053 // Set the the ICE connection state to connecting since the connection may
2054 // become writable with peer reflexive candidates before any remote candidate
2055 // is signaled.
2056 // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix
2057 // is to have a new signal the indicates a change in checking state from the
2058 // transport and expose a new checking() member from transport that can be
2059 // read to determine the current checking state. The existing SignalConnecting
2060 // actually means "gathering candidates", so cannot be be used here.
2061 if (remote_description()->GetType() != SdpType::kOffer &&
2062 remote_description()->number_of_mediasections() > 0u &&
Harald Alvestrand5b661302022-01-28 13:08:342063 pc_->ice_connection_state_internal() ==
Harald Alvestrandcdcfab02020-09-28 13:02:072064 PeerConnectionInterface::kIceConnectionNew) {
2065 pc_->SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
2066 }
2067
2068 // If setting the description decided our SSL role, allocate any necessary
2069 // SCTP sids.
Tommic61eee22023-03-22 07:25:382070 AllocateSctpSids();
Harald Alvestrandcdcfab02020-09-28 13:02:072071
Tomas Gunnarsson0dd75392022-01-17 18:19:562072 if (operation->unified_plan()) {
2073 ApplyRemoteDescriptionUpdateTransceiverState(operation->type());
Harald Alvestrandcdcfab02020-09-28 13:02:072074 }
Philipp Hancke6f0f1582023-12-12 12:15:032075 remote_peer_supports_msid_ =
2076 remote_description()->description()->msid_signaling() !=
2077 cricket::kMsidSignalingNotUsed;
Harald Alvestrandcdcfab02020-09-28 13:02:072078
Tomas Gunnarsson0dd75392022-01-17 18:19:562079 if (!operation->unified_plan()) {
Tomas Gunnarssonb625edf2022-01-07 16:22:142080 PlanBUpdateSendersAndReceivers(
Philipp Hancke6f0f1582023-12-12 12:15:032081 GetFirstAudioContent(remote_description()->description()),
2082 GetFirstAudioContentDescription(remote_description()->description()),
2083 GetFirstVideoContent(remote_description()->description()),
2084 GetFirstVideoContentDescription(remote_description()->description()));
Harald Alvestrandcdcfab02020-09-28 13:02:072085 }
2086
Tomas Gunnarsson0dd75392022-01-17 18:19:562087 if (operation->type() == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042088 if (local_ice_credentials_to_replace_->SatisfiesIceRestart(
2089 *current_local_description_)) {
2090 local_ice_credentials_to_replace_->ClearIceCredentials();
2091 }
2092
2093 RemoveStoppedTransceivers();
Harald Alvestrandcdcfab02020-09-28 13:02:072094 }
2095
Tomas Gunnarsson0dd75392022-01-17 18:19:562096 // Consider the operation complete at this point.
2097 operation->SignalCompletion();
2098
2099 SetRemoteDescriptionPostProcess(operation->type() == SdpType::kAnswer);
Harald Alvestrandcdcfab02020-09-28 13:02:072100}
2101
Tomas Gunnarsson651586c2022-01-07 18:33:122102void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState(
2103 SdpType sdp_type) {
2104 RTC_DCHECK_RUN_ON(signaling_thread());
2105 RTC_DCHECK(IsUnifiedPlan());
Harald Alvestrand8101e7b2022-05-23 14:57:472106 if (!ConfiguredForMedia()) {
2107 return;
2108 }
Tomas Gunnarsson651586c2022-01-07 18:33:122109 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
2110 now_receiving_transceivers;
2111 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
2112 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
2113 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
2114 for (const auto& transceiver_ext : transceivers()->List()) {
2115 const auto transceiver = transceiver_ext->internal();
2116 const ContentInfo* content =
2117 FindMediaSectionForTransceiver(transceiver, remote_description());
2118 if (!content) {
2119 continue;
2120 }
2121 const MediaContentDescription* media_desc = content->media_description();
2122 RtpTransceiverDirection local_direction =
2123 RtpTransceiverDirectionReversed(media_desc->direction());
Henrik Boström0a162762022-05-02 13:47:522124 // Remember the previous remote streams if this is a remote offer. This
2125 // makes it possible to rollback modifications to the streams.
2126 if (sdp_type == SdpType::kOffer) {
2127 transceivers()
2128 ->StableState(transceiver_ext)
2129 ->SetRemoteStreamIds(transceiver->receiver()->stream_ids());
2130 }
Tomas Gunnarsson651586c2022-01-07 18:33:122131 // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the
2132 // RTCSessionDescription: Set the associated remote streams given
2133 // transceiver.[[Receiver]], msids, addList, and removeList".
2134 // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription
2135 if (RtpTransceiverDirectionHasRecv(local_direction)) {
2136 std::vector<std::string> stream_ids;
2137 if (!media_desc->streams().empty()) {
2138 // The remote description has signaled the stream IDs.
2139 stream_ids = media_desc->streams()[0].stream_ids();
2140 }
Tomas Gunnarsson651586c2022-01-07 18:33:122141
2142 RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name
2143 << " (" << GetStreamIdsString(stream_ids) << ").";
2144 SetAssociatedRemoteStreams(transceiver->receiver_internal(), stream_ids,
2145 &added_streams, &removed_streams);
2146 // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6
2147 // "Set the RTCSessionDescription: If direction is sendrecv or recvonly,
2148 // and transceiver's current direction is neither sendrecv nor recvonly,
2149 // process the addition of a remote track for the media description.
2150 if (!transceiver->fired_direction() ||
2151 !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) {
2152 RTC_LOG(LS_INFO) << "Processing the addition of a remote track for MID="
2153 << content->name << ".";
2154 // Since the transceiver is passed to the user in an
2155 // OnTrack event, we must use the proxied transceiver.
2156 now_receiving_transceivers.push_back(transceiver_ext);
2157 }
2158 }
2159 // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's
2160 // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the
2161 // removal of a remote track for the media description, given transceiver,
2162 // removeList, and muteTracks.
2163 if (!RtpTransceiverDirectionHasRecv(local_direction) &&
2164 (transceiver->fired_direction() &&
2165 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) {
2166 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
2167 &removed_streams);
2168 }
2169 // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction.
Henrik Boström0a162762022-05-02 13:47:522170 if (sdp_type == SdpType::kOffer) {
2171 // Remember the previous fired direction if this is a remote offer. This
2172 // makes it possible to rollback modifications to [[FiredDirection]],
2173 // which is necessary for "ontrack" to fire in or after rollback.
2174 transceivers()
2175 ->StableState(transceiver_ext)
2176 ->SetFiredDirection(transceiver->fired_direction());
2177 }
Tomas Gunnarsson651586c2022-01-07 18:33:122178 transceiver->set_fired_direction(local_direction);
2179 // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run
2180 // the following steps:
2181 if (sdp_type == SdpType::kPrAnswer || sdp_type == SdpType::kAnswer) {
2182 // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to
2183 // direction.
2184 transceiver->set_current_direction(local_direction);
2185 // 2.2.8.1.11.[3-6]: Set the transport internal slots.
2186 if (transceiver->mid()) {
2187 auto dtls_transport = LookupDtlsTransportByMid(
Harald Alvestrandbc32c562022-02-09 12:08:472188 context_->network_thread(), transport_controller_s(),
Harald Alvestrand66c40362022-01-28 17:41:302189 *transceiver->mid());
Tomas Gunnarsson651586c2022-01-07 18:33:122190 transceiver->sender_internal()->set_transport(dtls_transport);
2191 transceiver->receiver_internal()->set_transport(dtls_transport);
2192 }
2193 }
2194 // 2.2.8.1.12: If the media description is rejected, and transceiver is
2195 // not already stopped, stop the RTCRtpTransceiver transceiver.
2196 if (content->rejected && !transceiver->stopped()) {
2197 RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name
2198 << " since the media section was rejected.";
2199 transceiver->StopTransceiverProcedure();
2200 }
2201 if (!content->rejected && RtpTransceiverDirectionHasRecv(local_direction)) {
2202 if (!media_desc->streams().empty() &&
2203 media_desc->streams()[0].has_ssrcs()) {
2204 uint32_t ssrc = media_desc->streams()[0].first_ssrc();
2205 transceiver->receiver_internal()->SetupMediaChannel(ssrc);
2206 } else {
2207 transceiver->receiver_internal()->SetupUnsignaledMediaChannel();
2208 }
2209 }
2210 }
2211 // Once all processing has finished, fire off callbacks.
2212 auto observer = pc_->Observer();
2213 for (const auto& transceiver : now_receiving_transceivers) {
Henrik Boströmf7859892022-07-04 12:36:372214 pc_->legacy_stats()->AddTrack(transceiver->receiver()->track().get());
Tomas Gunnarsson651586c2022-01-07 18:33:122215 observer->OnTrack(transceiver);
2216 observer->OnAddTrack(transceiver->receiver(),
2217 transceiver->receiver()->streams());
2218 }
2219 for (const auto& stream : added_streams) {
2220 observer->OnAddStream(stream);
2221 }
2222 for (const auto& transceiver : remove_list) {
2223 observer->OnRemoveTrack(transceiver->receiver());
2224 }
2225 for (const auto& stream : removed_streams) {
2226 observer->OnRemoveStream(stream);
2227 }
2228}
2229
Tomas Gunnarssonb625edf2022-01-07 16:22:142230void SdpOfferAnswerHandler::PlanBUpdateSendersAndReceivers(
2231 const cricket::ContentInfo* audio_content,
2232 const cricket::AudioContentDescription* audio_desc,
2233 const cricket::ContentInfo* video_content,
2234 const cricket::VideoContentDescription* video_desc) {
2235 RTC_DCHECK_RUN_ON(signaling_thread());
2236 RTC_DCHECK(!IsUnifiedPlan());
2237
2238 // We wait to signal new streams until we finish processing the description,
2239 // since only at that point will new streams have all their tracks.
2240 rtc::scoped_refptr<StreamCollection> new_streams(StreamCollection::Create());
2241
2242 // TODO(steveanton): When removing RTP senders/receivers in response to a
2243 // rejected media section, there is some cleanup logic that expects the
2244 // voice/ video channel to still be set. But in this method the voice/video
2245 // channel would have been destroyed by the SetRemoteDescription caller
2246 // above so the cleanup that relies on them fails to run. The RemoveSenders
2247 // calls should be moved to right before the DestroyChannel calls to fix
2248 // this.
2249
2250 // Find all audio rtp streams and create corresponding remote AudioTracks
2251 // and MediaStreams.
2252 if (audio_content) {
2253 if (audio_content->rejected) {
2254 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
2255 } else {
2256 bool default_audio_track_needed =
2257 !remote_peer_supports_msid_ &&
2258 RtpTransceiverDirectionHasSend(audio_desc->direction());
2259 UpdateRemoteSendersList(GetActiveStreams(audio_desc),
2260 default_audio_track_needed, audio_desc->type(),
Niels Möllerafb246b2022-04-20 12:26:502261 new_streams.get());
Tomas Gunnarssonb625edf2022-01-07 16:22:142262 }
2263 }
2264
2265 // Find all video rtp streams and create corresponding remote VideoTracks
2266 // and MediaStreams.
2267 if (video_content) {
2268 if (video_content->rejected) {
2269 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
2270 } else {
2271 bool default_video_track_needed =
2272 !remote_peer_supports_msid_ &&
2273 RtpTransceiverDirectionHasSend(video_desc->direction());
2274 UpdateRemoteSendersList(GetActiveStreams(video_desc),
2275 default_video_track_needed, video_desc->type(),
Niels Möllerafb246b2022-04-20 12:26:502276 new_streams.get());
Tomas Gunnarssonb625edf2022-01-07 16:22:142277 }
2278 }
2279
2280 // Iterate new_streams and notify the observer about new MediaStreams.
2281 auto observer = pc_->Observer();
2282 for (size_t i = 0; i < new_streams->count(); ++i) {
2283 MediaStreamInterface* new_stream = new_streams->at(i);
Henrik Boströmf7859892022-07-04 12:36:372284 pc_->legacy_stats()->AddStream(new_stream);
Tomas Gunnarssonb625edf2022-01-07 16:22:142285 observer->OnAddStream(rtc::scoped_refptr<MediaStreamInterface>(new_stream));
2286 }
2287
2288 UpdateEndedRemoteMediaStreams();
2289}
2290
Harald Alvestrandcdcfab02020-09-28 13:02:072291void SdpOfferAnswerHandler::DoSetLocalDescription(
2292 std::unique_ptr<SessionDescriptionInterface> desc,
2293 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
2294 RTC_DCHECK_RUN_ON(signaling_thread());
2295 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetLocalDescription");
2296
2297 if (!observer) {
2298 RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL.";
2299 return;
2300 }
2301
2302 if (!desc) {
2303 observer->OnSetLocalDescriptionComplete(
2304 RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL."));
2305 return;
2306 }
2307
2308 // If a session error has occurred the PeerConnection is in a possibly
2309 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:232310 if (session_error() != SessionError::kNone) {
2311 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:072312 RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message;
2313 observer->OnSetLocalDescriptionComplete(
2314 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2315 return;
2316 }
2317
2318 // For SLD we support only explicit rollback.
2319 if (desc->GetType() == SdpType::kRollback) {
2320 if (IsUnifiedPlan()) {
2321 observer->OnSetLocalDescriptionComplete(Rollback(desc->GetType()));
2322 } else {
2323 observer->OnSetLocalDescriptionComplete(
2324 RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2325 "Rollback not supported in Plan B"));
2326 }
2327 return;
2328 }
2329
Henrik Boströmf8187e02021-04-26 19:04:262330 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid =
2331 GetBundleGroupsByMid(desc->description());
2332 RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL,
2333 bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:072334 if (!error.ok()) {
2335 std::string error_message = GetSetDescriptionErrorMessage(
2336 cricket::CS_LOCAL, desc->GetType(), error);
2337 RTC_LOG(LS_ERROR) << error_message;
2338 observer->OnSetLocalDescriptionComplete(
Harald Alvestrand85ea9652023-10-06 11:32:412339 RTCError(error.type(), std::move(error_message)));
Harald Alvestrandcdcfab02020-09-28 13:02:072340 return;
2341 }
2342
2343 // Grab the description type before moving ownership to ApplyLocalDescription,
2344 // which may destroy it before returning.
2345 const SdpType type = desc->GetType();
2346
Henrik Boströmf8187e02021-04-26 19:04:262347 error = ApplyLocalDescription(std::move(desc), bundle_groups_by_mid);
Artem Titov880fa812021-07-30 20:30:232348 // `desc` may be destroyed at this point.
Harald Alvestrandcdcfab02020-09-28 13:02:072349
2350 if (!error.ok()) {
2351 // If ApplyLocalDescription fails, the PeerConnection could be in an
2352 // inconsistent state, so act conservatively here and set the session error
2353 // so that future calls to SetLocalDescription/SetRemoteDescription fail.
Harald Alvestranda474fbf2020-10-01 16:47:232354 SetSessionError(SessionError::kContent, error.message());
Harald Alvestrandcdcfab02020-09-28 13:02:072355 std::string error_message =
2356 GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error);
2357 RTC_LOG(LS_ERROR) << error_message;
2358 observer->OnSetLocalDescriptionComplete(
2359 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2360 return;
2361 }
2362 RTC_DCHECK(local_description());
2363
2364 if (local_description()->GetType() == SdpType::kAnswer) {
Harald Alvestranda474fbf2020-10-01 16:47:232365 RemoveStoppedTransceivers();
Harald Alvestrandcdcfab02020-09-28 13:02:072366
2367 // TODO(deadbeef): We already had to hop to the network thread for
2368 // MaybeStartGathering...
Danil Chapovalov9e09a1f2022-09-08 16:38:102369 context_->network_thread()->BlockingCall(
2370 [this] { port_allocator()->DiscardCandidatePool(); });
Harald Alvestrandcdcfab02020-09-28 13:02:072371 }
2372
2373 observer->OnSetLocalDescriptionComplete(RTCError::OK());
Harald Alvestrand44d0dff2020-10-09 05:43:532374 pc_->NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED);
Harald Alvestrandcdcfab02020-09-28 13:02:072375
2376 // Check if negotiation is needed. We must do this after informing the
2377 // observer that SetLocalDescription() has completed to ensure negotiation is
2378 // not needed prior to the promise resolving.
2379 if (IsUnifiedPlan()) {
2380 bool was_negotiation_needed = is_negotiation_needed_;
2381 UpdateNegotiationNeeded();
2382 if (signaling_state() == PeerConnectionInterface::kStable &&
2383 was_negotiation_needed && is_negotiation_needed_) {
2384 // Legacy version.
2385 pc_->Observer()->OnRenegotiationNeeded();
2386 // Spec-compliant version; the event may get invalidated before firing.
2387 GenerateNegotiationNeededEvent();
2388 }
2389 }
2390
2391 // MaybeStartGathering needs to be called after informing the observer so that
2392 // we don't signal any candidates before signaling that SetLocalDescription
2393 // completed.
Harald Alvestrandbc32c562022-02-09 12:08:472394 transport_controller_s()->MaybeStartGathering();
Harald Alvestrandcdcfab02020-09-28 13:02:072395}
2396
2397void SdpOfferAnswerHandler::DoCreateOffer(
2398 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2399 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2400 RTC_DCHECK_RUN_ON(signaling_thread());
2401 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateOffer");
2402
2403 if (!observer) {
2404 RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
2405 return;
2406 }
2407
2408 if (pc_->IsClosed()) {
2409 std::string error = "CreateOffer called when PeerConnection is closed.";
2410 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:092411 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502412 observer.get(),
2413 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:072414 return;
2415 }
2416
2417 // If a session error has occurred the PeerConnection is in a possibly
2418 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:232419 if (session_error() != SessionError::kNone) {
2420 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:072421 RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message;
Harald Alvestrand1090e442020-10-05 07:01:092422 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502423 observer.get(),
Harald Alvestrandcdcfab02020-09-28 13:02:072424 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2425 return;
2426 }
2427
2428 if (!ValidateOfferAnswerOptions(options)) {
2429 std::string error = "CreateOffer called with invalid options.";
2430 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:092431 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502432 observer.get(),
2433 RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:072434 return;
2435 }
2436
2437 // Legacy handling for offer_to_receive_audio and offer_to_receive_video.
2438 // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions".
2439 if (IsUnifiedPlan()) {
Harald Alvestranda474fbf2020-10-01 16:47:232440 RTCError error = HandleLegacyOfferOptions(options);
Harald Alvestrandcdcfab02020-09-28 13:02:072441 if (!error.ok()) {
Harald Alvestrand1090e442020-10-05 07:01:092442 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502443 observer.get(), std::move(error));
Harald Alvestrandcdcfab02020-09-28 13:02:072444 return;
2445 }
2446 }
2447
2448 cricket::MediaSessionOptions session_options;
Harald Alvestrandc06e3742020-10-01 10:23:332449 GetOptionsForOffer(options, &session_options);
Niels Möllerafb246b2022-04-20 12:26:502450 webrtc_session_desc_factory_->CreateOffer(observer.get(), options,
2451 session_options);
Harald Alvestrandcdcfab02020-09-28 13:02:072452}
2453
2454void SdpOfferAnswerHandler::CreateAnswer(
2455 CreateSessionDescriptionObserver* observer,
2456 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
Markus Handell518669d2021-06-07 11:30:462457 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateAnswer");
Harald Alvestrandcdcfab02020-09-28 13:02:072458 RTC_DCHECK_RUN_ON(signaling_thread());
2459 // Chain this operation. If asynchronous operations are pending on the chain,
2460 // this operation will be queued to be invoked, otherwise the contents of the
2461 // lambda will execute immediately.
2462 operations_chain_->ChainOperation(
2463 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2464 observer_refptr =
2465 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
2466 options](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 20:30:232467 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:072468 if (!this_weak_ptr) {
2469 observer_refptr->OnFailure(RTCError(
2470 RTCErrorType::INTERNAL_ERROR,
2471 "CreateAnswer failed because the session was shut down"));
2472 operations_chain_callback();
2473 return;
2474 }
2475 // The operation completes asynchronously when the wrapper is invoked.
Niels Möllerb7aac6f52021-08-23 13:48:062476 auto observer_wrapper = rtc::make_ref_counted<
2477 CreateSessionDescriptionObserverOperationWrapper>(
2478 std::move(observer_refptr), std::move(operations_chain_callback));
Harald Alvestrandcdcfab02020-09-28 13:02:072479 this_weak_ptr->DoCreateAnswer(options, observer_wrapper);
2480 });
2481}
2482
2483void SdpOfferAnswerHandler::DoCreateAnswer(
2484 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2485 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2486 RTC_DCHECK_RUN_ON(signaling_thread());
2487 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateAnswer");
2488 if (!observer) {
2489 RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
2490 return;
2491 }
2492
2493 // If a session error has occurred the PeerConnection is in a possibly
2494 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:232495 if (session_error() != SessionError::kNone) {
2496 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:072497 RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message;
Harald Alvestrand1090e442020-10-05 07:01:092498 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502499 observer.get(),
Harald Alvestrandcdcfab02020-09-28 13:02:072500 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2501 return;
2502 }
2503
2504 if (!(signaling_state_ == PeerConnectionInterface::kHaveRemoteOffer ||
2505 signaling_state_ == PeerConnectionInterface::kHaveLocalPrAnswer)) {
2506 std::string error =
2507 "PeerConnection cannot create an answer in a state other than "
2508 "have-remote-offer or have-local-pranswer.";
2509 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:092510 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502511 observer.get(),
2512 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:072513 return;
2514 }
2515
2516 // The remote description should be set if we're in the right state.
2517 RTC_DCHECK(remote_description());
2518
2519 if (IsUnifiedPlan()) {
2520 if (options.offer_to_receive_audio !=
2521 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2522 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not "
2523 "supported with Unified Plan semantics. Use the "
2524 "RtpTransceiver API instead.";
2525 }
2526 if (options.offer_to_receive_video !=
2527 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2528 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not "
2529 "supported with Unified Plan semantics. Use the "
2530 "RtpTransceiver API instead.";
2531 }
2532 }
2533
2534 cricket::MediaSessionOptions session_options;
Harald Alvestrandc06e3742020-10-01 10:23:332535 GetOptionsForAnswer(options, &session_options);
Niels Möllerafb246b2022-04-20 12:26:502536 webrtc_session_desc_factory_->CreateAnswer(observer.get(), session_options);
Harald Alvestrandcdcfab02020-09-28 13:02:072537}
2538
2539void SdpOfferAnswerHandler::DoSetRemoteDescription(
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042540 std::unique_ptr<RemoteDescriptionOperation> operation) {
Harald Alvestrandcdcfab02020-09-28 13:02:072541 RTC_DCHECK_RUN_ON(signaling_thread());
2542 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetRemoteDescription");
2543
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042544 if (!operation->ok())
Harald Alvestrandcdcfab02020-09-28 13:02:072545 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072546
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042547 if (operation->HaveSessionError())
Harald Alvestrandcdcfab02020-09-28 13:02:072548 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072549
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042550 if (operation->MaybeRollback())
Harald Alvestrandcdcfab02020-09-28 13:02:072551 return;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042552
2553 operation->ReportOfferAnswerUma();
2554
2555 // Handle remote descriptions missing a=mid lines for interop with legacy
2556 // end points.
2557 FillInMissingRemoteMids(operation->description());
2558 if (!operation->IsDescriptionValid())
Harald Alvestrandcdcfab02020-09-28 13:02:072559 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072560
Tomas Gunnarsson0dd75392022-01-17 18:19:562561 ApplyRemoteDescription(std::move(operation));
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042562}
Harald Alvestrandcdcfab02020-09-28 13:02:072563
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042564// Called after a DoSetRemoteDescription operation completes.
2565void SdpOfferAnswerHandler::SetRemoteDescriptionPostProcess(bool was_answer) {
Harald Alvestrandcdcfab02020-09-28 13:02:072566 RTC_DCHECK(remote_description());
2567
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042568 if (was_answer) {
Harald Alvestrandcdcfab02020-09-28 13:02:072569 // TODO(deadbeef): We already had to hop to the network thread for
2570 // MaybeStartGathering...
Danil Chapovalov9e09a1f2022-09-08 16:38:102571 context_->network_thread()->BlockingCall(
2572 [this] { port_allocator()->DiscardCandidatePool(); });
Harald Alvestrandcdcfab02020-09-28 13:02:072573 }
2574
Harald Alvestrand44d0dff2020-10-09 05:43:532575 pc_->NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED);
Harald Alvestrandcdcfab02020-09-28 13:02:072576
2577 // Check if negotiation is needed. We must do this after informing the
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042578 // observer that SetRemoteDescription() has completed to ensure negotiation
2579 // is not needed prior to the promise resolving.
Harald Alvestrandcdcfab02020-09-28 13:02:072580 if (IsUnifiedPlan()) {
2581 bool was_negotiation_needed = is_negotiation_needed_;
2582 UpdateNegotiationNeeded();
2583 if (signaling_state() == PeerConnectionInterface::kStable &&
2584 was_negotiation_needed && is_negotiation_needed_) {
2585 // Legacy version.
2586 pc_->Observer()->OnRenegotiationNeeded();
2587 // Spec-compliant version; the event may get invalidated before firing.
2588 GenerateNegotiationNeededEvent();
2589 }
2590 }
2591}
2592
2593void SdpOfferAnswerHandler::SetAssociatedRemoteStreams(
2594 rtc::scoped_refptr<RtpReceiverInternal> receiver,
2595 const std::vector<std::string>& stream_ids,
2596 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* added_streams,
2597 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
2598 RTC_DCHECK_RUN_ON(signaling_thread());
2599 std::vector<rtc::scoped_refptr<MediaStreamInterface>> media_streams;
2600 for (const std::string& stream_id : stream_ids) {
Niels Möllere7cc8832022-01-04 14:20:032601 rtc::scoped_refptr<MediaStreamInterface> stream(
2602 remote_streams_->find(stream_id));
Harald Alvestrandcdcfab02020-09-28 13:02:072603 if (!stream) {
2604 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
2605 MediaStream::Create(stream_id));
Harald Alvestrand6f04b6532020-10-09 11:42:172606 remote_streams_->AddStream(stream);
Harald Alvestrandcdcfab02020-09-28 13:02:072607 added_streams->push_back(stream);
2608 }
2609 media_streams.push_back(stream);
2610 }
2611 // Special case: "a=msid" missing, use random stream ID.
2612 if (media_streams.empty() &&
2613 !(remote_description()->description()->msid_signaling() &
2614 cricket::kMsidSignalingMediaSection)) {
2615 if (!missing_msid_default_stream_) {
2616 missing_msid_default_stream_ = MediaStreamProxy::Create(
2617 rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid()));
2618 added_streams->push_back(missing_msid_default_stream_);
2619 }
2620 media_streams.push_back(missing_msid_default_stream_);
2621 }
2622 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
2623 receiver->streams();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042624 // SetStreams() will add/remove the receiver's track to/from the streams.
2625 // This differs from the spec - the spec uses an "addList" and "removeList"
2626 // to update the stream-track relationships in a later step. We do this
2627 // earlier, changing the order of things, but the end-result is the same.
Harald Alvestrandcdcfab02020-09-28 13:02:072628 // TODO(hbos): When we remove remote_streams(), use set_stream_ids()
2629 // instead. https://crbug.com/webrtc/9480
2630 receiver->SetStreams(media_streams);
Harald Alvestranda474fbf2020-10-01 16:47:232631 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
Harald Alvestrandcdcfab02020-09-28 13:02:072632}
2633
2634bool SdpOfferAnswerHandler::AddIceCandidate(
2635 const IceCandidateInterface* ice_candidate) {
Tomas Gunnarsson2efb8a52021-04-01 14:26:572636 const AddIceCandidateResult result = AddIceCandidateInternal(ice_candidate);
2637 NoteAddIceCandidateResult(result);
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042638 // If the return value is kAddIceCandidateFailNotReady, the candidate has
2639 // been added, although not 'ready', but that's a success.
Tomas Gunnarsson2efb8a52021-04-01 14:26:572640 return result == kAddIceCandidateSuccess ||
2641 result == kAddIceCandidateFailNotReady;
2642}
2643
2644AddIceCandidateResult SdpOfferAnswerHandler::AddIceCandidateInternal(
2645 const IceCandidateInterface* ice_candidate) {
Harald Alvestrandcdcfab02020-09-28 13:02:072646 RTC_DCHECK_RUN_ON(signaling_thread());
2647 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
2648 if (pc_->IsClosed()) {
2649 RTC_LOG(LS_ERROR) << "AddIceCandidate: PeerConnection is closed.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572650 return kAddIceCandidateFailClosed;
Harald Alvestrandcdcfab02020-09-28 13:02:072651 }
2652
2653 if (!remote_description()) {
2654 RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added "
2655 "without any remote session description.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572656 return kAddIceCandidateFailNoRemoteDescription;
Harald Alvestrandcdcfab02020-09-28 13:02:072657 }
2658
2659 if (!ice_candidate) {
2660 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572661 return kAddIceCandidateFailNullCandidate;
Harald Alvestrandcdcfab02020-09-28 13:02:072662 }
2663
2664 bool valid = false;
Harald Alvestrandbc9ca252020-10-05 13:08:412665 bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid);
Harald Alvestrandcdcfab02020-09-28 13:02:072666 if (!valid) {
Tomas Gunnarsson2efb8a52021-04-01 14:26:572667 return kAddIceCandidateFailNotValid;
Harald Alvestrandcdcfab02020-09-28 13:02:072668 }
2669
2670 // Add this candidate to the remote session description.
2671 if (!mutable_remote_description()->AddCandidate(ice_candidate)) {
2672 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572673 return kAddIceCandidateFailInAddition;
Harald Alvestrandcdcfab02020-09-28 13:02:072674 }
2675
Tomas Gunnarsson2efb8a52021-04-01 14:26:572676 if (!ready) {
Harald Alvestrandcdcfab02020-09-28 13:02:072677 RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572678 return kAddIceCandidateFailNotReady;
Harald Alvestrandcdcfab02020-09-28 13:02:072679 }
Tomas Gunnarsson2efb8a52021-04-01 14:26:572680
2681 if (!UseCandidate(ice_candidate)) {
2682 return kAddIceCandidateFailNotUsable;
2683 }
2684
2685 pc_->NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED);
2686
2687 return kAddIceCandidateSuccess;
Harald Alvestrandcdcfab02020-09-28 13:02:072688}
2689
2690void SdpOfferAnswerHandler::AddIceCandidate(
2691 std::unique_ptr<IceCandidateInterface> candidate,
2692 std::function<void(RTCError)> callback) {
Markus Handell518669d2021-06-07 11:30:462693 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
Harald Alvestrandcdcfab02020-09-28 13:02:072694 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042695 // Chain this operation. If asynchronous operations are pending on the
2696 // chain, this operation will be queued to be invoked, otherwise the
2697 // contents of the lambda will execute immediately.
Harald Alvestrandcdcfab02020-09-28 13:02:072698 operations_chain_->ChainOperation(
2699 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2700 candidate = std::move(candidate), callback = std::move(callback)](
2701 std::function<void()> operations_chain_callback) {
Tomas Gunnarsson2efb8a52021-04-01 14:26:572702 auto result =
2703 this_weak_ptr
2704 ? this_weak_ptr->AddIceCandidateInternal(candidate.get())
2705 : kAddIceCandidateFailClosed;
2706 NoteAddIceCandidateResult(result);
2707 operations_chain_callback();
Henrik Boström347488e2022-01-21 14:18:082708 switch (result) {
2709 case AddIceCandidateResult::kAddIceCandidateSuccess:
2710 case AddIceCandidateResult::kAddIceCandidateFailNotReady:
2711 // Success!
2712 callback(RTCError::OK());
2713 break;
2714 case AddIceCandidateResult::kAddIceCandidateFailClosed:
2715 // Note that the spec says to just abort without resolving the
2716 // promise in this case, but this layer must return an RTCError.
2717 callback(RTCError(
2718 RTCErrorType::INVALID_STATE,
2719 "AddIceCandidate failed because the session was shut down"));
2720 break;
2721 case AddIceCandidateResult::kAddIceCandidateFailNoRemoteDescription:
2722 // Spec: "If remoteDescription is null return a promise rejected
2723 // with a newly created InvalidStateError."
2724 callback(RTCError(RTCErrorType::INVALID_STATE,
2725 "The remote description was null"));
2726 break;
2727 case AddIceCandidateResult::kAddIceCandidateFailNullCandidate:
2728 // TODO(https://crbug.com/935898): Handle end-of-candidates instead
2729 // of treating null candidate as an error.
2730 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2731 "Error processing ICE candidate"));
2732 break;
2733 case AddIceCandidateResult::kAddIceCandidateFailNotValid:
2734 case AddIceCandidateResult::kAddIceCandidateFailInAddition:
2735 case AddIceCandidateResult::kAddIceCandidateFailNotUsable:
2736 // Spec: "If candidate could not be successfully added [...] Reject
2737 // p with a newly created OperationError and abort these steps."
2738 // UNSUPPORTED_OPERATION maps to OperationError.
2739 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2740 "Error processing ICE candidate"));
2741 break;
2742 default:
2743 RTC_DCHECK_NOTREACHED();
Harald Alvestrandcdcfab02020-09-28 13:02:072744 }
Harald Alvestrandcdcfab02020-09-28 13:02:072745 });
2746}
2747
2748bool SdpOfferAnswerHandler::RemoveIceCandidates(
2749 const std::vector<cricket::Candidate>& candidates) {
2750 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveIceCandidates");
2751 RTC_DCHECK_RUN_ON(signaling_thread());
2752 if (pc_->IsClosed()) {
2753 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: PeerConnection is closed.";
2754 return false;
2755 }
2756
2757 if (!remote_description()) {
2758 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed "
2759 "without any remote session description.";
2760 return false;
2761 }
2762
2763 if (candidates.empty()) {
2764 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty.";
2765 return false;
2766 }
2767
2768 size_t number_removed =
2769 mutable_remote_description()->RemoveCandidates(candidates);
2770 if (number_removed != candidates.size()) {
2771 RTC_LOG(LS_ERROR)
2772 << "RemoveIceCandidates: Failed to remove candidates. Requested "
2773 << candidates.size() << " but only " << number_removed
2774 << " are removed.";
2775 }
2776
2777 // Remove the candidates from the transport controller.
Harald Alvestrandbc32c562022-02-09 12:08:472778 RTCError error = transport_controller_s()->RemoveRemoteCandidates(candidates);
Harald Alvestrandcdcfab02020-09-28 13:02:072779 if (!error.ok()) {
2780 RTC_LOG(LS_ERROR)
2781 << "RemoveIceCandidates: Error when removing remote candidates: "
2782 << error.message();
2783 }
2784 return true;
2785}
2786
2787void SdpOfferAnswerHandler::AddLocalIceCandidate(
2788 const JsepIceCandidate* candidate) {
2789 RTC_DCHECK_RUN_ON(signaling_thread());
2790 if (local_description()) {
2791 mutable_local_description()->AddCandidate(candidate);
2792 }
2793}
2794
2795void SdpOfferAnswerHandler::RemoveLocalIceCandidates(
2796 const std::vector<cricket::Candidate>& candidates) {
2797 RTC_DCHECK_RUN_ON(signaling_thread());
2798 if (local_description()) {
2799 mutable_local_description()->RemoveCandidates(candidates);
2800 }
2801}
2802
2803const SessionDescriptionInterface* SdpOfferAnswerHandler::local_description()
2804 const {
2805 RTC_DCHECK_RUN_ON(signaling_thread());
2806 return pending_local_description_ ? pending_local_description_.get()
2807 : current_local_description_.get();
2808}
2809
2810const SessionDescriptionInterface* SdpOfferAnswerHandler::remote_description()
2811 const {
2812 RTC_DCHECK_RUN_ON(signaling_thread());
2813 return pending_remote_description_ ? pending_remote_description_.get()
2814 : current_remote_description_.get();
2815}
2816
2817const SessionDescriptionInterface*
2818SdpOfferAnswerHandler::current_local_description() const {
2819 RTC_DCHECK_RUN_ON(signaling_thread());
2820 return current_local_description_.get();
2821}
2822
2823const SessionDescriptionInterface*
2824SdpOfferAnswerHandler::current_remote_description() const {
2825 RTC_DCHECK_RUN_ON(signaling_thread());
2826 return current_remote_description_.get();
2827}
2828
2829const SessionDescriptionInterface*
2830SdpOfferAnswerHandler::pending_local_description() const {
2831 RTC_DCHECK_RUN_ON(signaling_thread());
2832 return pending_local_description_.get();
2833}
2834
2835const SessionDescriptionInterface*
2836SdpOfferAnswerHandler::pending_remote_description() const {
2837 RTC_DCHECK_RUN_ON(signaling_thread());
2838 return pending_remote_description_.get();
2839}
2840
2841PeerConnectionInterface::SignalingState SdpOfferAnswerHandler::signaling_state()
2842 const {
2843 RTC_DCHECK_RUN_ON(signaling_thread());
2844 return signaling_state_;
2845}
2846
2847void SdpOfferAnswerHandler::ChangeSignalingState(
2848 PeerConnectionInterface::SignalingState signaling_state) {
Markus Handell518669d2021-06-07 11:30:462849 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ChangeSignalingState");
Harald Alvestrandcdcfab02020-09-28 13:02:072850 RTC_DCHECK_RUN_ON(signaling_thread());
2851 if (signaling_state_ == signaling_state) {
2852 return;
2853 }
2854 RTC_LOG(LS_INFO) << "Session: " << pc_->session_id() << " Old state: "
Harald Alvestrand31b03e92021-11-02 10:54:382855 << PeerConnectionInterface::AsString(signaling_state_)
Harald Alvestrandcdcfab02020-09-28 13:02:072856 << " New state: "
Harald Alvestrand31b03e92021-11-02 10:54:382857 << PeerConnectionInterface::AsString(signaling_state);
Harald Alvestrandcdcfab02020-09-28 13:02:072858 signaling_state_ = signaling_state;
2859 pc_->Observer()->OnSignalingChange(signaling_state_);
2860}
2861
2862RTCError SdpOfferAnswerHandler::UpdateSessionState(
2863 SdpType type,
2864 cricket::ContentSource source,
Henrik Boströmf8187e02021-04-26 19:04:262865 const cricket::SessionDescription* description,
2866 const std::map<std::string, const cricket::ContentGroup*>&
2867 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:072868 RTC_DCHECK_RUN_ON(signaling_thread());
2869
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042870 // If there's already a pending error then no state transition should
2871 // happen. But all call-sites should be verifying this before calling us!
Harald Alvestranda474fbf2020-10-01 16:47:232872 RTC_DCHECK(session_error() == SessionError::kNone);
Harald Alvestrandcdcfab02020-09-28 13:02:072873
Taylor Brandstetterd0acbd82021-01-25 21:44:552874 // If this is answer-ish we're ready to let media flow.
2875 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
2876 EnableSending();
2877 }
2878
Harald Alvestrandcdcfab02020-09-28 13:02:072879 // Update the signaling state according to the specified state machine (see
2880 // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum).
2881 if (type == SdpType::kOffer) {
2882 ChangeSignalingState(source == cricket::CS_LOCAL
2883 ? PeerConnectionInterface::kHaveLocalOffer
2884 : PeerConnectionInterface::kHaveRemoteOffer);
2885 } else if (type == SdpType::kPrAnswer) {
2886 ChangeSignalingState(source == cricket::CS_LOCAL
2887 ? PeerConnectionInterface::kHaveLocalPrAnswer
2888 : PeerConnectionInterface::kHaveRemotePrAnswer);
2889 } else {
Harald Alvestrandcdcfab02020-09-28 13:02:072890 RTC_DCHECK(type == SdpType::kAnswer);
2891 ChangeSignalingState(PeerConnectionInterface::kStable);
Harald Alvestrand8101e7b2022-05-23 14:57:472892 if (ConfiguredForMedia()) {
2893 transceivers()->DiscardStableStates();
2894 }
Harald Alvestrandcdcfab02020-09-28 13:02:072895 }
2896
2897 // Update internal objects according to the session description's media
2898 // descriptions.
Tommicc7a3682021-05-04 12:59:382899 return PushdownMediaDescription(type, source, bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:072900}
2901
2902bool SdpOfferAnswerHandler::ShouldFireNegotiationNeededEvent(
2903 uint32_t event_id) {
2904 RTC_DCHECK_RUN_ON(signaling_thread());
2905 // Plan B? Always fire to conform with useless legacy behavior.
2906 if (!IsUnifiedPlan()) {
2907 return true;
2908 }
2909 // The event ID has been invalidated. Either negotiation is no longer needed
2910 // or a newer negotiation needed event has been generated.
2911 if (event_id != negotiation_needed_event_id_) {
2912 return false;
2913 }
2914 // The chain is no longer empty, update negotiation needed when it becomes
2915 // empty. This should generate a newer negotiation needed event, making this
2916 // one obsolete.
2917 if (!operations_chain_->IsEmpty()) {
2918 // Since we just suppressed an event that would have been fired, if
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042919 // negotiation is still needed by the time the chain becomes empty again,
2920 // we must make sure to generate another event if negotiation is needed
2921 // then. This happens when `is_negotiation_needed_` goes from false to
2922 // true, so we set it to false until UpdateNegotiationNeeded() is called.
Harald Alvestrandcdcfab02020-09-28 13:02:072923 is_negotiation_needed_ = false;
2924 update_negotiation_needed_on_empty_chain_ = true;
2925 return false;
2926 }
2927 // We must not fire if the signaling state is no longer "stable". If
2928 // negotiation is still needed when we return to "stable", a new negotiation
2929 // needed event will be generated, so this one can safely be suppressed.
2930 if (signaling_state_ != PeerConnectionInterface::kStable) {
2931 return false;
2932 }
2933 // All checks have passed - please fire "negotiationneeded" now!
2934 return true;
2935}
2936
Harald Alvestrand6f04b6532020-10-09 11:42:172937rtc::scoped_refptr<StreamCollectionInterface>
2938SdpOfferAnswerHandler::local_streams() {
2939 RTC_DCHECK_RUN_ON(signaling_thread());
2940 RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified "
2941 "Plan SdpSemantics. Please use GetSenders "
2942 "instead.";
2943 return local_streams_;
2944}
2945
2946rtc::scoped_refptr<StreamCollectionInterface>
2947SdpOfferAnswerHandler::remote_streams() {
2948 RTC_DCHECK_RUN_ON(signaling_thread());
2949 RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified "
2950 "Plan SdpSemantics. Please use GetReceivers "
2951 "instead.";
2952 return remote_streams_;
2953}
2954
2955bool SdpOfferAnswerHandler::AddStream(MediaStreamInterface* local_stream) {
2956 RTC_DCHECK_RUN_ON(signaling_thread());
2957 RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan "
2958 "SdpSemantics. Please use AddTrack instead.";
2959 if (pc_->IsClosed()) {
2960 return false;
2961 }
Niels Möllerafb246b2022-04-20 12:26:502962 if (!CanAddLocalMediaStream(local_streams_.get(), local_stream)) {
Harald Alvestrand6f04b6532020-10-09 11:42:172963 return false;
2964 }
2965
Niels Möllere7cc8832022-01-04 14:20:032966 local_streams_->AddStream(
2967 rtc::scoped_refptr<MediaStreamInterface>(local_stream));
Mirko Bonadei9ff450d2021-08-02 08:56:332968 auto observer = std::make_unique<MediaStreamObserver>(
2969 local_stream,
2970 [this](AudioTrackInterface* audio_track,
2971 MediaStreamInterface* media_stream) {
2972 RTC_DCHECK_RUN_ON(signaling_thread());
2973 OnAudioTrackAdded(audio_track, media_stream);
2974 },
2975 [this](AudioTrackInterface* audio_track,
2976 MediaStreamInterface* media_stream) {
2977 RTC_DCHECK_RUN_ON(signaling_thread());
2978 OnAudioTrackRemoved(audio_track, media_stream);
2979 },
2980 [this](VideoTrackInterface* video_track,
2981 MediaStreamInterface* media_stream) {
2982 RTC_DCHECK_RUN_ON(signaling_thread());
2983 OnVideoTrackAdded(video_track, media_stream);
2984 },
2985 [this](VideoTrackInterface* video_track,
2986 MediaStreamInterface* media_stream) {
2987 RTC_DCHECK_RUN_ON(signaling_thread());
2988 OnVideoTrackRemoved(video_track, media_stream);
2989 });
2990 stream_observers_.push_back(std::move(observer));
Harald Alvestrand6f04b6532020-10-09 11:42:172991
2992 for (const auto& track : local_stream->GetAudioTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:052993 rtp_manager()->AddAudioTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:172994 }
2995 for (const auto& track : local_stream->GetVideoTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:052996 rtp_manager()->AddVideoTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:172997 }
2998
Henrik Boströmf7859892022-07-04 12:36:372999 pc_->legacy_stats()->AddStream(local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:173000 UpdateNegotiationNeeded();
3001 return true;
3002}
3003
3004void SdpOfferAnswerHandler::RemoveStream(MediaStreamInterface* local_stream) {
3005 RTC_DCHECK_RUN_ON(signaling_thread());
3006 RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified "
3007 "Plan SdpSemantics. Please use RemoveTrack "
3008 "instead.";
3009 TRACE_EVENT0("webrtc", "PeerConnection::RemoveStream");
3010 if (!pc_->IsClosed()) {
3011 for (const auto& track : local_stream->GetAudioTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:053012 rtp_manager()->RemoveAudioTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:173013 }
3014 for (const auto& track : local_stream->GetVideoTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:053015 rtp_manager()->RemoveVideoTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:173016 }
3017 }
3018 local_streams_->RemoveStream(local_stream);
3019 stream_observers_.erase(
3020 std::remove_if(
3021 stream_observers_.begin(), stream_observers_.end(),
3022 [local_stream](const std::unique_ptr<MediaStreamObserver>& observer) {
3023 return observer->stream()->id().compare(local_stream->id()) == 0;
3024 }),
3025 stream_observers_.end());
3026
3027 if (pc_->IsClosed()) {
3028 return;
3029 }
3030 UpdateNegotiationNeeded();
3031}
3032
Harald Alvestrand763f5a92020-10-22 10:39:403033void SdpOfferAnswerHandler::OnAudioTrackAdded(AudioTrackInterface* track,
3034 MediaStreamInterface* stream) {
3035 if (pc_->IsClosed()) {
3036 return;
3037 }
3038 rtp_manager()->AddAudioTrack(track, stream);
3039 UpdateNegotiationNeeded();
3040}
3041
3042void SdpOfferAnswerHandler::OnAudioTrackRemoved(AudioTrackInterface* track,
3043 MediaStreamInterface* stream) {
3044 if (pc_->IsClosed()) {
3045 return;
3046 }
3047 rtp_manager()->RemoveAudioTrack(track, stream);
3048 UpdateNegotiationNeeded();
3049}
3050
3051void SdpOfferAnswerHandler::OnVideoTrackAdded(VideoTrackInterface* track,
3052 MediaStreamInterface* stream) {
3053 if (pc_->IsClosed()) {
3054 return;
3055 }
3056 rtp_manager()->AddVideoTrack(track, stream);
3057 UpdateNegotiationNeeded();
3058}
3059
3060void SdpOfferAnswerHandler::OnVideoTrackRemoved(VideoTrackInterface* track,
3061 MediaStreamInterface* stream) {
3062 if (pc_->IsClosed()) {
3063 return;
3064 }
3065 rtp_manager()->RemoveVideoTrack(track, stream);
3066 UpdateNegotiationNeeded();
3067}
3068
Harald Alvestrandcdcfab02020-09-28 13:02:073069RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) {
Markus Handell518669d2021-06-07 11:30:463070 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::Rollback");
Harald Alvestrandcdcfab02020-09-28 13:02:073071 auto state = signaling_state();
3072 if (state != PeerConnectionInterface::kHaveLocalOffer &&
3073 state != PeerConnectionInterface::kHaveRemoteOffer) {
Philipp Hanckeb81bf532023-07-18 09:03:393074 LOG_AND_RETURN_ERROR(
3075 RTCErrorType::INVALID_STATE,
3076 (rtc::StringBuilder("Called in wrong signalingState: ")
3077 << (PeerConnectionInterface::AsString(signaling_state())))
3078 .Release());
Harald Alvestrandcdcfab02020-09-28 13:02:073079 }
3080 RTC_DCHECK_RUN_ON(signaling_thread());
3081 RTC_DCHECK(IsUnifiedPlan());
Henrik Boström0a162762022-05-02 13:47:523082 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
3083 now_receiving_transceivers;
Harald Alvestrandcdcfab02020-09-28 13:02:073084 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_added_streams;
3085 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_removed_streams;
3086 std::vector<rtc::scoped_refptr<RtpReceiverInterface>> removed_receivers;
3087
Harald Alvestrande15fb152020-10-19 13:28:053088 for (auto&& transceivers_stable_state_pair : transceivers()->StableStates()) {
Harald Alvestrandcdcfab02020-09-28 13:02:073089 auto transceiver = transceivers_stable_state_pair.first;
3090 auto state = transceivers_stable_state_pair.second;
3091
Henrik Boström0a162762022-05-02 13:47:523092 if (state.did_set_fired_direction()) {
3093 // If this rollback triggers going from not receiving to receving again,
3094 // we need to fire "ontrack".
3095 bool previously_fired_direction_is_recv =
3096 transceiver->fired_direction().has_value() &&
3097 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction());
3098 bool currently_fired_direction_is_recv =
3099 state.fired_direction().has_value() &&
3100 RtpTransceiverDirectionHasRecv(state.fired_direction().value());
3101 if (!previously_fired_direction_is_recv &&
3102 currently_fired_direction_is_recv) {
3103 now_receiving_transceivers.push_back(transceiver);
3104 }
3105 transceiver->internal()->set_fired_direction(state.fired_direction());
3106 }
3107
Harald Alvestrandcdcfab02020-09-28 13:02:073108 if (state.remote_stream_ids()) {
3109 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
3110 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
3111 SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(),
3112 state.remote_stream_ids().value(),
3113 &added_streams, &removed_streams);
3114 all_added_streams.insert(all_added_streams.end(), added_streams.begin(),
3115 added_streams.end());
3116 all_removed_streams.insert(all_removed_streams.end(),
3117 removed_streams.begin(),
3118 removed_streams.end());
3119 if (!state.has_m_section() && !state.newly_created()) {
3120 continue;
3121 }
3122 }
3123
Henrik Boström0a162762022-05-02 13:47:523124 // Due to the above `continue` statement, the below code only runs if there
3125 // is a change in mid association (has_m_section), if the transceiver was
3126 // newly created (newly_created) or if remote streams were not set.
3127
Harald Alvestrandcdcfab02020-09-28 13:02:073128 RTC_DCHECK(transceiver->internal()->mid().has_value());
Harald Alvestrand19ebabc2022-04-28 13:31:173129 transceiver->internal()->ClearChannel();
Harald Alvestrandcdcfab02020-09-28 13:02:073130
3131 if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer &&
3132 transceiver->receiver()) {
3133 removed_receivers.push_back(transceiver->receiver());
3134 }
3135 if (state.newly_created()) {
3136 if (transceiver->internal()->reused_for_addtrack()) {
3137 transceiver->internal()->set_created_by_addtrack(true);
3138 } else {
Henrik Boströma8ad11d2022-04-27 09:54:013139 transceiver->internal()->StopTransceiverProcedure();
Harald Alvestrande15fb152020-10-19 13:28:053140 transceivers()->Remove(transceiver);
Harald Alvestrandcdcfab02020-09-28 13:02:073141 }
3142 }
Eldar Rello950d6b92021-04-06 19:38:003143 if (state.init_send_encodings()) {
3144 transceiver->internal()->sender_internal()->set_init_send_encodings(
3145 state.init_send_encodings().value());
3146 }
Harald Alvestrandcdcfab02020-09-28 13:02:073147 transceiver->internal()->sender_internal()->set_transport(nullptr);
3148 transceiver->internal()->receiver_internal()->set_transport(nullptr);
Florent Castellib3d424c2023-03-15 15:47:433149 if (state.has_m_section()) {
3150 transceiver->internal()->set_mid(state.mid());
3151 transceiver->internal()->set_mline_index(state.mline_index());
3152 }
Harald Alvestrandcdcfab02020-09-28 13:02:073153 }
Harald Alvestrandbc32c562022-02-09 12:08:473154 RTCError e = transport_controller_s()->RollbackTransports();
Taylor Brandstetter8591eff2021-08-11 21:56:383155 if (!e.ok()) {
3156 return e;
3157 }
Harald Alvestrande15fb152020-10-19 13:28:053158 transceivers()->DiscardStableStates();
Harald Alvestrandcdcfab02020-09-28 13:02:073159 pending_local_description_.reset();
3160 pending_remote_description_.reset();
3161 ChangeSignalingState(PeerConnectionInterface::kStable);
3162
3163 // Once all processing has finished, fire off callbacks.
Henrik Boström0a162762022-05-02 13:47:523164 for (const auto& transceiver : now_receiving_transceivers) {
3165 pc_->Observer()->OnTrack(transceiver);
3166 pc_->Observer()->OnAddTrack(transceiver->receiver(),
3167 transceiver->receiver()->streams());
3168 }
Harald Alvestrandcdcfab02020-09-28 13:02:073169 for (const auto& receiver : removed_receivers) {
3170 pc_->Observer()->OnRemoveTrack(receiver);
3171 }
3172 for (const auto& stream : all_added_streams) {
3173 pc_->Observer()->OnAddStream(stream);
3174 }
3175 for (const auto& stream : all_removed_streams) {
3176 pc_->Observer()->OnRemoveStream(stream);
3177 }
3178
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043179 // The assumption is that in case of implicit rollback
3180 // UpdateNegotiationNeeded gets called in SetRemoteDescription.
Harald Alvestrandcdcfab02020-09-28 13:02:073181 if (desc_type == SdpType::kRollback) {
3182 UpdateNegotiationNeeded();
3183 if (is_negotiation_needed_) {
3184 // Legacy version.
3185 pc_->Observer()->OnRenegotiationNeeded();
3186 // Spec-compliant version; the event may get invalidated before firing.
3187 GenerateNegotiationNeededEvent();
3188 }
3189 }
3190 return RTCError::OK();
3191}
3192
3193bool SdpOfferAnswerHandler::IsUnifiedPlan() const {
Harald Alvestrandcdcfab02020-09-28 13:02:073194 return pc_->IsUnifiedPlan();
3195}
3196
3197void SdpOfferAnswerHandler::OnOperationsChainEmpty() {
3198 RTC_DCHECK_RUN_ON(signaling_thread());
3199 if (pc_->IsClosed() || !update_negotiation_needed_on_empty_chain_)
3200 return;
3201 update_negotiation_needed_on_empty_chain_ = false;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043202 // Firing when chain is empty is only supported in Unified Plan to avoid
3203 // Plan B regressions. (In Plan B, onnegotiationneeded is already broken
3204 // anyway, so firing it even more might just be confusing.)
Harald Alvestrandcdcfab02020-09-28 13:02:073205 if (IsUnifiedPlan()) {
3206 UpdateNegotiationNeeded();
3207 }
3208}
3209
Tommi335d0842023-03-25 09:56:183210absl::optional<bool> SdpOfferAnswerHandler::is_caller() const {
Harald Alvestrandcdcfab02020-09-28 13:02:073211 RTC_DCHECK_RUN_ON(signaling_thread());
3212 return is_caller_;
3213}
3214
3215bool SdpOfferAnswerHandler::HasNewIceCredentials() {
3216 RTC_DCHECK_RUN_ON(signaling_thread());
3217 return local_ice_credentials_to_replace_->HasIceCredentials();
3218}
3219
3220bool SdpOfferAnswerHandler::IceRestartPending(
3221 const std::string& content_name) const {
3222 RTC_DCHECK_RUN_ON(signaling_thread());
3223 return pending_ice_restarts_.find(content_name) !=
3224 pending_ice_restarts_.end();
3225}
3226
Harald Alvestrandf01bd6c2020-10-23 13:30:463227bool SdpOfferAnswerHandler::NeedsIceRestart(
3228 const std::string& content_name) const {
Tommic3257d02021-02-10 17:40:083229 return pc_->NeedsIceRestart(content_name);
Harald Alvestrandf01bd6c2020-10-23 13:30:463230}
3231
3232absl::optional<rtc::SSLRole> SdpOfferAnswerHandler::GetDtlsRole(
3233 const std::string& mid) const {
Harald Alvestrandbc32c562022-02-09 12:08:473234 RTC_DCHECK_RUN_ON(signaling_thread());
3235 return transport_controller_s()->GetDtlsRole(mid);
Harald Alvestrandf01bd6c2020-10-23 13:30:463236}
3237
Harald Alvestrandcdcfab02020-09-28 13:02:073238void SdpOfferAnswerHandler::UpdateNegotiationNeeded() {
3239 RTC_DCHECK_RUN_ON(signaling_thread());
3240 if (!IsUnifiedPlan()) {
3241 pc_->Observer()->OnRenegotiationNeeded();
3242 GenerateNegotiationNeededEvent();
3243 return;
3244 }
3245
3246 // In the spec, a task is queued here to run the following steps - this is
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043247 // meant to ensure we do not fire onnegotiationneeded prematurely if
3248 // multiple changes are being made at once. In order to support Chromium's
Harald Alvestrandcdcfab02020-09-28 13:02:073249 // implementation where the JavaScript representation of the PeerConnection
3250 // lives on a separate thread though, the queuing of a task is instead
3251 // performed by the PeerConnectionObserver posting from the signaling thread
3252 // to the JavaScript main thread that negotiation is needed. And because the
3253 // Operations Chain lives on the WebRTC signaling thread,
3254 // ShouldFireNegotiationNeededEvent() must be called before firing the event
3255 // to ensure the Operations Chain is still empty and the event has not been
3256 // invalidated.
3257
3258 // If connection's [[IsClosed]] slot is true, abort these steps.
3259 if (pc_->IsClosed())
3260 return;
3261
3262 // If connection's signaling state is not "stable", abort these steps.
3263 if (signaling_state() != PeerConnectionInterface::kStable)
3264 return;
3265
3266 // NOTE
3267 // The negotiation-needed flag will be updated once the state transitions to
3268 // "stable", as part of the steps for setting an RTCSessionDescription.
3269
3270 // If the result of checking if negotiation is needed is false, clear the
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043271 // negotiation-needed flag by setting connection's [[NegotiationNeeded]]
3272 // slot to false, and abort these steps.
Harald Alvestrandcdcfab02020-09-28 13:02:073273 bool is_negotiation_needed = CheckIfNegotiationIsNeeded();
3274 if (!is_negotiation_needed) {
3275 is_negotiation_needed_ = false;
3276 // Invalidate any negotiation needed event that may previosuly have been
3277 // generated.
3278 ++negotiation_needed_event_id_;
3279 return;
3280 }
3281
3282 // If connection's [[NegotiationNeeded]] slot is already true, abort these
3283 // steps.
3284 if (is_negotiation_needed_)
3285 return;
3286
3287 // Set connection's [[NegotiationNeeded]] slot to true.
3288 is_negotiation_needed_ = true;
3289
3290 // Queue a task that runs the following steps:
3291 // If connection's [[IsClosed]] slot is true, abort these steps.
3292 // If connection's [[NegotiationNeeded]] slot is false, abort these steps.
3293 // Fire an event named negotiationneeded at connection.
3294 pc_->Observer()->OnRenegotiationNeeded();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043295 // Fire the spec-compliant version; when ShouldFireNegotiationNeededEvent()
3296 // is used in the task queued by the observer, this event will only fire
3297 // when the chain is empty.
Harald Alvestrandcdcfab02020-09-28 13:02:073298 GenerateNegotiationNeededEvent();
3299}
3300
Tommic61eee22023-03-22 07:25:383301void SdpOfferAnswerHandler::AllocateSctpSids() {
3302 RTC_DCHECK_RUN_ON(signaling_thread());
3303 if (!local_description() || !remote_description()) {
3304 RTC_DLOG(LS_VERBOSE)
3305 << "Local and Remote descriptions must be applied to get the "
3306 "SSL Role of the SCTP transport.";
3307 return;
3308 }
3309
Tommif9e13f82023-04-06 19:21:453310 absl::optional<rtc::SSLRole> guessed_role = GuessSslRole();
3311 network_thread()->BlockingCall(
3312 [&, data_channel_controller = data_channel_controller()] {
3313 RTC_DCHECK_RUN_ON(network_thread());
3314 absl::optional<rtc::SSLRole> role = pc_->GetSctpSslRole_n();
3315 if (!role)
3316 role = guessed_role;
3317 if (role)
3318 data_channel_controller->AllocateSctpSids(*role);
3319 });
Tommic61eee22023-03-22 07:25:383320}
3321
Tommi335d0842023-03-25 09:56:183322absl::optional<rtc::SSLRole> SdpOfferAnswerHandler::GuessSslRole() const {
3323 RTC_DCHECK_RUN_ON(signaling_thread());
3324 if (!pc_->sctp_mid())
3325 return absl::nullopt;
3326
3327 // TODO(bugs.webrtc.org/13668): This guesswork is guessing wrong (returning
3328 // SSL_CLIENT = ACTIVE) if remote offer has role ACTIVE, but we'll be able
3329 // to detect that by looking at the SDP.
3330 //
3331 // The phases of establishing an SCTP session are:
3332 //
3333 // Offerer:
3334 //
3335 // * Before negotiation: Neither is_caller nor sctp_mid exists.
3336 // * After setting an offer as local description: is_caller is known (true),
3337 // sctp_mid is known, but we don't know the SSL role for sure (or if we'll
3338 // eventually get an SCTP session).
3339 // * After setting an answer as the remote description: We know is_caller,
3340 // sctp_mid and that we'll get the SCTP channel established (m-section
3341 // wasn't rejected).
3342 // * Special case: The SCTP m-section was rejected: Close datachannels.
3343 // * We MAY know the SSL role if we offered actpass and got back active or
3344 // passive; if the other end is a webrtc implementation, it will be active.
3345 // * After the TLS handshake: We have a definitive answer on the SSL role.
3346 //
3347 // Answerer:
3348 //
3349 // * After setting an offer as remote description: We know is_caller (false).
3350 // * If there was an SCTP session, we know the SCTP mid. We also know the
3351 // SSL role, since if the remote offer was actpass or passive, we'll answer
3352 // active, and if the remote offer was active, we're passive.
3353 // * Special case: No SCTP m= line. We don't know for sure if the remote
3354 // doesn't support it or just didn't offer it. Not sure what we do in this
3355 // case (logic would suggest fire a `negotiationneeded` event and generate a
3356 // subsequent offer, but this needs to be tested).
3357 // * After the TLS handshake: We know that TLS obeyed the protocol. There
3358 // should be an error surfaced somewhere if it didn't.
3359 // * "Guessing" should always be correct if we get an SCTP session and are not
3360 // the offerer.
3361
3362 return is_caller() ? rtc::SSL_SERVER : rtc::SSL_CLIENT;
3363}
3364
Harald Alvestrandcdcfab02020-09-28 13:02:073365bool SdpOfferAnswerHandler::CheckIfNegotiationIsNeeded() {
3366 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043367 // 1. If any implementation-specific negotiation is required, as described
3368 // at the start of this section, return true.
Harald Alvestrandcdcfab02020-09-28 13:02:073369
3370 // 2. If connection.[[LocalIceCredentialsToReplace]] is not empty, return
3371 // true.
3372 if (local_ice_credentials_to_replace_->HasIceCredentials()) {
3373 return true;
3374 }
3375
3376 // 3. Let description be connection.[[CurrentLocalDescription]].
3377 const SessionDescriptionInterface* description = current_local_description();
3378 if (!description)
3379 return true;
3380
3381 // 4. If connection has created any RTCDataChannels, and no m= section in
3382 // description has been negotiated yet for data, return true.
Tommi91160ef2023-03-29 23:09:453383 if (data_channel_controller()->HasUsedDataChannels()) {
Philipp Hancke522380f2023-05-09 07:41:033384 const cricket::ContentInfo* data_content =
3385 cricket::GetFirstDataContent(description->description()->contents());
3386 if (!data_content) {
Harald Alvestrandcdcfab02020-09-28 13:02:073387 return true;
Philipp Hancke522380f2023-05-09 07:41:033388 }
3389 // The remote end might have rejected the data content.
3390 const cricket::ContentInfo* remote_data_content =
3391 current_remote_description()
3392 ? current_remote_description()->description()->GetContentByName(
3393 data_content->name)
3394 : nullptr;
3395 if (remote_data_content && remote_data_content->rejected) {
3396 return true;
3397 }
Harald Alvestrandcdcfab02020-09-28 13:02:073398 }
Harald Alvestrand8101e7b2022-05-23 14:57:473399 if (!ConfiguredForMedia()) {
3400 return false;
3401 }
Harald Alvestrandcdcfab02020-09-28 13:02:073402
3403 // 5. For each transceiver in connection's set of transceivers, perform the
3404 // following checks:
Harald Alvestrand85466662021-04-19 21:21:363405 for (const auto& transceiver : transceivers()->ListInternal()) {
Harald Alvestrandcdcfab02020-09-28 13:02:073406 const ContentInfo* current_local_msection =
Harald Alvestrand85466662021-04-19 21:21:363407 FindTransceiverMSection(transceiver, description);
Harald Alvestrandcdcfab02020-09-28 13:02:073408
Harald Alvestrand85466662021-04-19 21:21:363409 const ContentInfo* current_remote_msection =
3410 FindTransceiverMSection(transceiver, current_remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:073411
3412 // 5.4 If transceiver is stopped and is associated with an m= section,
3413 // but the associated m= section is not yet rejected in
3414 // connection.[[CurrentLocalDescription]] or
3415 // connection.[[CurrentRemoteDescription]], return true.
3416 if (transceiver->stopped()) {
3417 RTC_DCHECK(transceiver->stopping());
3418 if (current_local_msection && !current_local_msection->rejected &&
3419 ((current_remote_msection && !current_remote_msection->rejected) ||
3420 !current_remote_msection)) {
3421 return true;
3422 }
3423 continue;
3424 }
3425
3426 // 5.1 If transceiver.[[Stopping]] is true and transceiver.[[Stopped]] is
3427 // false, return true.
3428 if (transceiver->stopping() && !transceiver->stopped())
3429 return true;
3430
3431 // 5.2 If transceiver isn't stopped and isn't yet associated with an m=
3432 // section in description, return true.
3433 if (!current_local_msection)
3434 return true;
3435
3436 const MediaContentDescription* current_local_media_description =
3437 current_local_msection->media_description();
3438 // 5.3 If transceiver isn't stopped and is associated with an m= section
3439 // in description then perform the following checks:
3440
3441 // 5.3.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the
3442 // associated m= section in description either doesn't contain a single
3443 // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this
3444 // m= section, or the MSID values themselves, differ from what is in
3445 // transceiver.sender.[[AssociatedMediaStreamIds]], return true.
3446 if (RtpTransceiverDirectionHasSend(transceiver->direction())) {
3447 if (current_local_media_description->streams().size() == 0)
3448 return true;
3449
3450 std::vector<std::string> msection_msids;
3451 for (const auto& stream : current_local_media_description->streams()) {
3452 for (const std::string& msid : stream.stream_ids())
3453 msection_msids.push_back(msid);
3454 }
3455
3456 std::vector<std::string> transceiver_msids =
3457 transceiver->sender()->stream_ids();
3458 if (msection_msids.size() != transceiver_msids.size())
3459 return true;
3460
3461 absl::c_sort(transceiver_msids);
3462 absl::c_sort(msection_msids);
3463 if (transceiver_msids != msection_msids)
3464 return true;
3465 }
3466
3467 // 5.3.2 If description is of type "offer", and the direction of the
3468 // associated m= section in neither connection.[[CurrentLocalDescription]]
3469 // nor connection.[[CurrentRemoteDescription]] matches
3470 // transceiver.[[Direction]], return true.
3471 if (description->GetType() == SdpType::kOffer) {
3472 if (!current_remote_description())
3473 return true;
3474
3475 if (!current_remote_msection)
3476 return true;
3477
3478 RtpTransceiverDirection current_local_direction =
3479 current_local_media_description->direction();
3480 RtpTransceiverDirection current_remote_direction =
3481 current_remote_msection->media_description()->direction();
3482 if (transceiver->direction() != current_local_direction &&
3483 transceiver->direction() !=
3484 RtpTransceiverDirectionReversed(current_remote_direction)) {
3485 return true;
3486 }
3487 }
3488
3489 // 5.3.3 If description is of type "answer", and the direction of the
3490 // associated m= section in the description does not match
3491 // transceiver.[[Direction]] intersected with the offered direction (as
3492 // described in [JSEP] (section 5.3.1.)), return true.
3493 if (description->GetType() == SdpType::kAnswer) {
3494 if (!remote_description())
3495 return true;
3496
3497 const ContentInfo* offered_remote_msection =
Harald Alvestrand85466662021-04-19 21:21:363498 FindTransceiverMSection(transceiver, remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:073499
3500 RtpTransceiverDirection offered_direction =
3501 offered_remote_msection
3502 ? offered_remote_msection->media_description()->direction()
3503 : RtpTransceiverDirection::kInactive;
3504
3505 if (current_local_media_description->direction() !=
3506 (RtpTransceiverDirectionIntersection(
3507 transceiver->direction(),
3508 RtpTransceiverDirectionReversed(offered_direction)))) {
3509 return true;
3510 }
3511 }
3512 }
Harald Alvestrandcdcfab02020-09-28 13:02:073513 // If all the preceding checks were performed and true was not returned,
3514 // nothing remains to be negotiated; return false.
3515 return false;
3516}
3517
3518void SdpOfferAnswerHandler::GenerateNegotiationNeededEvent() {
3519 RTC_DCHECK_RUN_ON(signaling_thread());
3520 ++negotiation_needed_event_id_;
3521 pc_->Observer()->OnNegotiationNeededEvent(negotiation_needed_event_id_);
3522}
3523
3524RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
3525 const SessionDescriptionInterface* sdesc,
Henrik Boströmf8187e02021-04-26 19:04:263526 cricket::ContentSource source,
3527 const std::map<std::string, const cricket::ContentGroup*>&
3528 bundle_groups_by_mid) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043529 // An assumption is that a check for session error is done at a higher level.
3530 RTC_DCHECK_EQ(SessionError::kNone, session_error());
Harald Alvestrandcdcfab02020-09-28 13:02:073531
3532 if (!sdesc || !sdesc->description()) {
Philipp Hanckeb81bf532023-07-18 09:03:393533 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
Harald Alvestrandcdcfab02020-09-28 13:02:073534 }
3535
3536 SdpType type = sdesc->GetType();
Harald Alvestrandc06e3742020-10-01 10:23:333537 if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) ||
3538 (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) {
Philipp Hanckeb81bf532023-07-18 09:03:393539 LOG_AND_RETURN_ERROR(
3540 RTCErrorType::INVALID_STATE,
3541 (rtc::StringBuilder("Called in wrong state: ")
3542 << PeerConnectionInterface::AsString(signaling_state()))
3543 .Release());
Harald Alvestrandcdcfab02020-09-28 13:02:073544 }
3545
3546 RTCError error = ValidateMids(*sdesc->description());
3547 if (!error.ok()) {
3548 return error;
3549 }
3550
3551 // Verify crypto settings.
3552 std::string crypto_error;
Harald Alvestrand974044e2024-02-08 13:15:513553 if (pc_->dtls_enabled()) {
Harald Alvestrand0d018412021-11-04 13:52:313554 RTCError crypto_error = VerifyCrypto(
3555 sdesc->description(), pc_->dtls_enabled(), bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:073556 if (!crypto_error.ok()) {
3557 return crypto_error;
3558 }
3559 }
3560
3561 // Verify ice-ufrag and ice-pwd.
Henrik Boströmf8187e02021-04-26 19:04:263562 if (!VerifyIceUfragPwdPresent(sdesc->description(), bundle_groups_by_mid)) {
Philipp Hanckeb81bf532023-07-18 09:03:393563 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
3564 kSdpWithoutIceUfragPwd);
Harald Alvestrandcdcfab02020-09-28 13:02:073565 }
3566
Philipp Hanckea2f5d452022-12-19 10:04:063567 // Validate that there are no collisions of bundled payload types.
Philipp Hanckef0ea56a2022-11-28 16:48:043568 error = ValidateBundledPayloadTypes(*sdesc->description());
3569 // TODO(bugs.webrtc.org/14420): actually reject.
3570 RTC_HISTOGRAM_BOOLEAN("WebRTC.PeerConnection.ValidBundledPayloadTypes",
3571 error.ok());
3572
Philipp Hanckea2f5d452022-12-19 10:04:063573 // Validate that there are no collisions of bundled header extensions ids.
3574 error = ValidateBundledRtpHeaderExtensions(*sdesc->description());
Philipp Hancke745641e2023-08-22 14:05:383575 if (!error.ok()) {
Philipp Hancke4bf52382023-05-25 14:11:363576 return error;
3577 }
Philipp Hanckea2f5d452022-12-19 10:04:063578
Philipp Hancke1f1b0b32023-08-11 07:32:503579 // TODO(crbug.com/1459124): remove killswitch after rollout.
3580 error = ValidateSsrcGroups(*sdesc->description());
3581 if (!error.ok() &&
3582 !pc_->trials().IsDisabled("WebRTC-PreventSsrcGroupsWithUnexpectedSize")) {
3583 return error;
3584 }
3585
Henrik Boströmf8187e02021-04-26 19:04:263586 if (!pc_->ValidateBundleSettings(sdesc->description(),
3587 bundle_groups_by_mid)) {
Philipp Hanckeb81bf532023-07-18 09:03:393588 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
3589 kBundleWithoutRtcpMux);
Harald Alvestrandcdcfab02020-09-28 13:02:073590 }
3591
Philipp Hanckeb64615a2023-09-14 14:23:313592 error = ValidatePayloadTypes(*sdesc->description());
3593 if (!error.ok()) {
3594 return error;
3595 }
3596
Harald Alvestrandcdcfab02020-09-28 13:02:073597 // TODO(skvlad): When the local rtcp-mux policy is Require, reject any
3598 // m-lines that do not rtcp-mux enabled.
3599
3600 // Verify m-lines in Answer when compared against Offer.
3601 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043602 // With an answer we want to compare the new answer session description
3603 // with the offer's session description from the current negotiation.
Harald Alvestrandcdcfab02020-09-28 13:02:073604 const cricket::SessionDescription* offer_desc =
3605 (source == cricket::CS_LOCAL) ? remote_description()->description()
3606 : local_description()->description();
3607 if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) ||
3608 !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(),
3609 type)) {
Philipp Hanckeb81bf532023-07-18 09:03:393610 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
3611 kMlineMismatchInAnswer);
Harald Alvestrandcdcfab02020-09-28 13:02:073612 }
3613 } else {
3614 // The re-offers should respect the order of m= sections in current
3615 // description. See RFC3264 Section 8 paragraph 4 for more details.
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043616 // With a re-offer, either the current local or current remote
3617 // descriptions could be the most up to date, so we would like to check
3618 // against both of them if they exist. It could be the case that one of
3619 // them has a 0 port for a media section, but the other does not. This is
3620 // important to check against in the case that we are recycling an m=
3621 // section.
Harald Alvestrandcdcfab02020-09-28 13:02:073622 const cricket::SessionDescription* current_desc = nullptr;
3623 const cricket::SessionDescription* secondary_current_desc = nullptr;
3624 if (local_description()) {
3625 current_desc = local_description()->description();
3626 if (remote_description()) {
3627 secondary_current_desc = remote_description()->description();
3628 }
3629 } else if (remote_description()) {
3630 current_desc = remote_description()->description();
3631 }
3632 if (current_desc &&
3633 !MediaSectionsInSameOrder(*current_desc, secondary_current_desc,
3634 *sdesc->description(), type)) {
Philipp Hanckeb81bf532023-07-18 09:03:393635 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
3636 kMlineMismatchInSubsequentOffer);
Harald Alvestrandcdcfab02020-09-28 13:02:073637 }
3638 }
3639
3640 if (IsUnifiedPlan()) {
3641 // Ensure that each audio and video media section has at most one
3642 // "StreamParams". This will return an error if receiving a session
3643 // description from a "Plan B" endpoint which adds multiple tracks of the
3644 // same type. With Unified Plan, there can only be at most one track per
3645 // media section.
3646 for (const ContentInfo& content : sdesc->description()->contents()) {
3647 const MediaContentDescription& desc = *content.media_description();
3648 if ((desc.type() == cricket::MEDIA_TYPE_AUDIO ||
3649 desc.type() == cricket::MEDIA_TYPE_VIDEO) &&
3650 desc.streams().size() > 1u) {
Philipp Hanckeb81bf532023-07-18 09:03:393651 LOG_AND_RETURN_ERROR(
Tomas Gunnarsson0dd75392022-01-17 18:19:563652 RTCErrorType::INVALID_PARAMETER,
3653 "Media section has more than one track specified with a=ssrc lines "
3654 "which is not supported with Unified Plan.");
Harald Alvestrandcdcfab02020-09-28 13:02:073655 }
3656 }
Philipp Hancke34887262023-06-01 17:07:503657 // Validate spec-simulcast which only works if the remote end negotiated the
3658 // mid and rid header extension.
3659 error = ValidateRtpHeaderExtensionsForSpecSimulcast(*sdesc->description());
3660 if (!error.ok()) {
3661 return error;
3662 }
Harald Alvestrandcdcfab02020-09-28 13:02:073663 }
3664
3665 return RTCError::OK();
3666}
3667
3668RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels(
3669 cricket::ContentSource source,
3670 const SessionDescriptionInterface& new_session,
3671 const SessionDescriptionInterface* old_local_description,
Henrik Boströmf8187e02021-04-26 19:04:263672 const SessionDescriptionInterface* old_remote_description,
3673 const std::map<std::string, const cricket::ContentGroup*>&
3674 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:463675 TRACE_EVENT0("webrtc",
3676 "SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels");
Harald Alvestrandcdcfab02020-09-28 13:02:073677 RTC_DCHECK_RUN_ON(signaling_thread());
3678 RTC_DCHECK(IsUnifiedPlan());
3679
Harald Alvestrandcdcfab02020-09-28 13:02:073680 if (new_session.GetType() == SdpType::kOffer) {
Henrik Boströmf8187e02021-04-26 19:04:263681 // If the BUNDLE policy is max-bundle, then we know for sure that all
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043682 // transports will be bundled from the start. Return an error if
3683 // max-bundle is specified but the session description does not have a
3684 // BUNDLE group.
Henrik Boströmf8187e02021-04-26 19:04:263685 if (pc_->configuration()->bundle_policy ==
3686 PeerConnectionInterface::kBundlePolicyMaxBundle &&
3687 bundle_groups_by_mid.empty()) {
Philipp Hanckeb81bf532023-07-18 09:03:393688 LOG_AND_RETURN_ERROR(
Tomas Gunnarsson0dd75392022-01-17 18:19:563689 RTCErrorType::INVALID_PARAMETER,
3690 "max-bundle configured but session description has no BUNDLE group");
Harald Alvestrandcdcfab02020-09-28 13:02:073691 }
Harald Alvestrandcdcfab02020-09-28 13:02:073692 }
3693
3694 const ContentInfos& new_contents = new_session.description()->contents();
3695 for (size_t i = 0; i < new_contents.size(); ++i) {
3696 const cricket::ContentInfo& new_content = new_contents[i];
3697 cricket::MediaType media_type = new_content.media_description()->type();
Harald Alvestrandbc9ca252020-10-05 13:08:413698 mid_generator_.AddKnownId(new_content.name);
Henrik Boströmf8187e02021-04-26 19:04:263699 auto it = bundle_groups_by_mid.find(new_content.name);
3700 const cricket::ContentGroup* bundle_group =
3701 it != bundle_groups_by_mid.end() ? it->second : nullptr;
Harald Alvestrandcdcfab02020-09-28 13:02:073702 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
3703 media_type == cricket::MEDIA_TYPE_VIDEO) {
3704 const cricket::ContentInfo* old_local_content = nullptr;
3705 if (old_local_description &&
3706 i < old_local_description->description()->contents().size()) {
3707 old_local_content =
3708 &old_local_description->description()->contents()[i];
3709 }
3710 const cricket::ContentInfo* old_remote_content = nullptr;
3711 if (old_remote_description &&
3712 i < old_remote_description->description()->contents().size()) {
3713 old_remote_content =
3714 &old_remote_description->description()->contents()[i];
3715 }
Harald Alvestrandcdcfab02020-09-28 13:02:073716 auto transceiver_or_error =
3717 AssociateTransceiver(source, new_session.GetType(), i, new_content,
3718 old_local_content, old_remote_content);
3719 if (!transceiver_or_error.ok()) {
Henrik Boströma8ad11d2022-04-27 09:54:013720 // In the case where a transceiver is rejected locally prior to being
3721 // associated, we don't expect to find a transceiver, but might find it
3722 // in the case where state is still "stopping", not "stopped".
Harald Alvestrand09bd9ba2020-10-09 08:13:303723 if (new_content.rejected) {
3724 continue;
3725 }
Harald Alvestrandcdcfab02020-09-28 13:02:073726 return transceiver_or_error.MoveError();
3727 }
3728 auto transceiver = transceiver_or_error.MoveValue();
3729 RTCError error =
3730 UpdateTransceiverChannel(transceiver, new_content, bundle_group);
Henrik Boströma8ad11d2022-04-27 09:54:013731 // Handle locally rejected content. This code path is only needed for apps
3732 // that SDP munge. Remote rejected content is handled in
3733 // ApplyRemoteDescriptionUpdateTransceiverState().
3734 if (source == cricket::ContentSource::CS_LOCAL && new_content.rejected) {
3735 // Local offer.
3736 if (new_session.GetType() == SdpType::kOffer) {
3737 // If the RtpTransceiver API was used, it would already have made the
3738 // transceiver stopping. But if the rejection was caused by SDP
3739 // munging then we need to ensure the transceiver is stopping here.
3740 if (!transceiver->internal()->stopping()) {
3741 transceiver->internal()->StopStandard();
3742 }
3743 RTC_DCHECK(transceiver->internal()->stopping());
3744 } else {
3745 // Local answer.
3746 RTC_DCHECK(new_session.GetType() == SdpType::kAnswer ||
3747 new_session.GetType() == SdpType::kPrAnswer);
3748 // When RtpTransceiver API is used, rejection happens in the offer and
3749 // the transceiver will already be stopped at local answer time
3750 // (calling stop between SRD(offer) and SLD(answer) would not reject
3751 // the content in the answer - instead this would trigger a follow-up
3752 // O/A exchange). So if the content was rejected but the transceiver
3753 // is not already stopped, SDP munging has happened and we need to
3754 // ensure the transceiver is stopped.
3755 if (!transceiver->internal()->stopped()) {
3756 transceiver->internal()->StopTransceiverProcedure();
3757 }
3758 RTC_DCHECK(transceiver->internal()->stopped());
3759 }
3760 }
Harald Alvestrandcdcfab02020-09-28 13:02:073761 if (!error.ok()) {
3762 return error;
3763 }
3764 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
Tommi840cf782023-10-21 14:47:563765 const auto data_mid = pc_->sctp_mid();
3766 if (data_mid && new_content.name != data_mid.value()) {
Harald Alvestrandcdcfab02020-09-28 13:02:073767 // Ignore all but the first data section.
3768 RTC_LOG(LS_INFO) << "Ignoring data media section with MID="
3769 << new_content.name;
3770 continue;
3771 }
Tommi840cf782023-10-21 14:47:563772 RTCError error =
3773 UpdateDataChannelTransport(source, new_content, bundle_group);
Harald Alvestrandcdcfab02020-09-28 13:02:073774 if (!error.ok()) {
3775 return error;
3776 }
Philipp Hancke4e8c1152020-10-13 10:43:153777 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
3778 RTC_LOG(LS_INFO) << "Ignoring unsupported media type";
Harald Alvestrandcdcfab02020-09-28 13:02:073779 } else {
Philipp Hanckeb81bf532023-07-18 09:03:393780 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
3781 "Unknown section type.");
Harald Alvestrandcdcfab02020-09-28 13:02:073782 }
3783 }
3784
3785 return RTCError::OK();
3786}
3787
3788RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
3789SdpOfferAnswerHandler::AssociateTransceiver(
3790 cricket::ContentSource source,
3791 SdpType type,
3792 size_t mline_index,
3793 const ContentInfo& content,
3794 const ContentInfo* old_local_content,
3795 const ContentInfo* old_remote_content) {
Markus Handell518669d2021-06-07 11:30:463796 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AssociateTransceiver");
Harald Alvestrandcdcfab02020-09-28 13:02:073797 RTC_DCHECK(IsUnifiedPlan());
Tomas Gunnarsson36992362020-10-05 19:41:363798#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:073799 // If this is an offer then the m= section might be recycled. If the m=
3800 // section is being recycled (defined as: rejected in the current local or
3801 // remote description and not rejected in new description), the transceiver
Harald Alvestrande15fb152020-10-19 13:28:053802 // should have been removed by RemoveStoppedtransceivers()->
Harald Alvestrandcdcfab02020-09-28 13:02:073803 if (IsMediaSectionBeingRecycled(type, content, old_local_content,
3804 old_remote_content)) {
3805 const std::string& old_mid =
3806 (old_local_content && old_local_content->rejected)
3807 ? old_local_content->name
3808 : old_remote_content->name;
Harald Alvestrande15fb152020-10-19 13:28:053809 auto old_transceiver = transceivers()->FindByMid(old_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:073810 // The transceiver should be disassociated in RemoveStoppedTransceivers()
3811 RTC_DCHECK(!old_transceiver);
3812 }
Tomas Gunnarsson36992362020-10-05 19:41:363813#endif
3814
Harald Alvestrandcdcfab02020-09-28 13:02:073815 const MediaContentDescription* media_desc = content.media_description();
Harald Alvestrande15fb152020-10-19 13:28:053816 auto transceiver = transceivers()->FindByMid(content.name);
Harald Alvestrandcdcfab02020-09-28 13:02:073817 if (source == cricket::CS_LOCAL) {
3818 // Find the RtpTransceiver that corresponds to this m= section, using the
3819 // mapping between transceivers and m= section indices established when
3820 // creating the offer.
3821 if (!transceiver) {
Harald Alvestrande15fb152020-10-19 13:28:053822 transceiver = transceivers()->FindByMLineIndex(mline_index);
Harald Alvestrandcdcfab02020-09-28 13:02:073823 }
3824 if (!transceiver) {
Harald Alvestrand09bd9ba2020-10-09 08:13:303825 // This may happen normally when media sections are rejected.
Philipp Hanckeb81bf532023-07-18 09:03:393826 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
3827 "Transceiver not found based on m-line index");
Harald Alvestrandcdcfab02020-09-28 13:02:073828 }
3829 } else {
3830 RTC_DCHECK_EQ(source, cricket::CS_REMOTE);
3831 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers
3832 // of the same type...
3833 // When simulcast is requested, a transceiver cannot be associated because
3834 // AddTrack cannot be called to initialize it.
3835 if (!transceiver &&
3836 RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
3837 !media_desc->HasSimulcast()) {
Harald Alvestrandc06e3742020-10-01 10:23:333838 transceiver = FindAvailableTransceiverToReceive(media_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:073839 }
3840 // If no RtpTransceiver was found in the previous step, create one with a
3841 // recvonly direction.
3842 if (!transceiver) {
3843 RTC_LOG(LS_INFO) << "Adding "
3844 << cricket::MediaTypeToString(media_desc->type())
3845 << " transceiver for MID=" << content.name
3846 << " at i=" << mline_index
3847 << " in response to the remote description.";
3848 std::string sender_id = rtc::CreateRandomUuid();
3849 std::vector<RtpEncodingParameters> send_encodings =
3850 GetSendEncodingsFromRemoteDescription(*media_desc);
Harald Alvestrande15fb152020-10-19 13:28:053851 auto sender = rtp_manager()->CreateSender(media_desc->type(), sender_id,
3852 nullptr, {}, send_encodings);
Harald Alvestrandcdcfab02020-09-28 13:02:073853 std::string receiver_id;
3854 if (!media_desc->streams().empty()) {
3855 receiver_id = media_desc->streams()[0].id;
3856 } else {
3857 receiver_id = rtc::CreateRandomUuid();
3858 }
Harald Alvestrande15fb152020-10-19 13:28:053859 auto receiver =
3860 rtp_manager()->CreateReceiver(media_desc->type(), receiver_id);
3861 transceiver = rtp_manager()->CreateAndAddTransceiver(sender, receiver);
Harald Alvestrandcdcfab02020-09-28 13:02:073862 transceiver->internal()->set_direction(
3863 RtpTransceiverDirection::kRecvOnly);
3864 if (type == SdpType::kOffer) {
Harald Alvestrande15fb152020-10-19 13:28:053865 transceivers()->StableState(transceiver)->set_newly_created();
Harald Alvestrandcdcfab02020-09-28 13:02:073866 }
3867 }
Tomas Gunnarsson36992362020-10-05 19:41:363868
3869 RTC_DCHECK(transceiver);
3870
Harald Alvestrandcdcfab02020-09-28 13:02:073871 // Check if the offer indicated simulcast but the answer rejected it.
3872 // This can happen when simulcast is not supported on the remote party.
Lennart Grahl0d0ed762021-05-17 14:06:373873 if (SimulcastIsRejected(old_local_content, *media_desc,
3874 pc_->GetCryptoOptions()
3875 .srtp.enable_encrypted_rtp_header_extensions)) {
Harald Alvestrandcdcfab02020-09-28 13:02:073876 RTCError error =
3877 DisableSimulcastInSender(transceiver->internal()->sender_internal());
3878 if (!error.ok()) {
3879 RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast.";
3880 return std::move(error);
3881 }
3882 }
3883 }
Tomas Gunnarsson36992362020-10-05 19:41:363884
Harald Alvestrandcdcfab02020-09-28 13:02:073885 if (transceiver->media_type() != media_desc->type()) {
Philipp Hanckeb81bf532023-07-18 09:03:393886 LOG_AND_RETURN_ERROR(
3887 RTCErrorType::INVALID_PARAMETER,
3888 "Transceiver type does not match media description type.");
Harald Alvestrandcdcfab02020-09-28 13:02:073889 }
Tomas Gunnarsson36992362020-10-05 19:41:363890
Harald Alvestrandcdcfab02020-09-28 13:02:073891 if (media_desc->HasSimulcast()) {
3892 std::vector<SimulcastLayer> layers =
3893 source == cricket::CS_LOCAL
3894 ? media_desc->simulcast_description().send_layers().GetAllLayers()
3895 : media_desc->simulcast_description()
3896 .receive_layers()
3897 .GetAllLayers();
3898 RTCError error = UpdateSimulcastLayerStatusInSender(
3899 layers, transceiver->internal()->sender_internal());
3900 if (!error.ok()) {
3901 RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers.";
3902 return std::move(error);
3903 }
3904 }
3905 if (type == SdpType::kOffer) {
3906 bool state_changes = transceiver->internal()->mid() != content.name ||
3907 transceiver->internal()->mline_index() != mline_index;
3908 if (state_changes) {
Harald Alvestrandbc9ca252020-10-05 13:08:413909 transceivers()
Harald Alvestrande15fb152020-10-19 13:28:053910 ->StableState(transceiver)
Harald Alvestrand38b768c2020-09-29 11:54:053911 ->SetMSectionIfUnset(transceiver->internal()->mid(),
3912 transceiver->internal()->mline_index());
Harald Alvestrandcdcfab02020-09-28 13:02:073913 }
3914 }
3915 // Associate the found or created RtpTransceiver with the m= section by
3916 // setting the value of the RtpTransceiver's mid property to the MID of the m=
3917 // section, and establish a mapping between the transceiver and the index of
3918 // the m= section.
3919 transceiver->internal()->set_mid(content.name);
3920 transceiver->internal()->set_mline_index(mline_index);
3921 return std::move(transceiver);
3922}
3923
Harald Alvestrandcdcfab02020-09-28 13:02:073924RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel(
3925 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
3926 transceiver,
3927 const cricket::ContentInfo& content,
3928 const cricket::ContentGroup* bundle_group) {
Markus Handell518669d2021-06-07 11:30:463929 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateTransceiverChannel");
Harald Alvestrandcdcfab02020-09-28 13:02:073930 RTC_DCHECK(IsUnifiedPlan());
3931 RTC_DCHECK(transceiver);
3932 cricket::ChannelInterface* channel = transceiver->internal()->channel();
3933 if (content.rejected) {
3934 if (channel) {
Harald Alvestrand19ebabc2022-04-28 13:31:173935 transceiver->internal()->ClearChannel();
Harald Alvestrandcdcfab02020-09-28 13:02:073936 }
3937 } else {
3938 if (!channel) {
Harald Alvestrand8f429922022-05-04 10:32:303939 auto error = transceiver->internal()->CreateChannel(
3940 content.name, pc_->call_ptr(), pc_->configuration()->media_config,
3941 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
3942 video_options(), video_bitrate_allocator_factory_.get(),
3943 [&](absl::string_view mid) {
Harald Alvestrand3af79d12022-04-29 15:04:583944 RTC_DCHECK_RUN_ON(network_thread());
3945 return transport_controller_n()->GetRtpTransport(mid);
3946 });
Harald Alvestrand8f429922022-05-04 10:32:303947 if (!error.ok()) {
3948 return error;
3949 }
Harald Alvestrandcdcfab02020-09-28 13:02:073950 }
3951 }
3952 return RTCError::OK();
3953}
3954
Tommi840cf782023-10-21 14:47:563955RTCError SdpOfferAnswerHandler::UpdateDataChannelTransport(
Harald Alvestrandcdcfab02020-09-28 13:02:073956 cricket::ContentSource source,
3957 const cricket::ContentInfo& content,
3958 const cricket::ContentGroup* bundle_group) {
Harald Alvestrandcdcfab02020-09-28 13:02:073959 if (content.rejected) {
Florent Castellidcb9ffc2021-06-29 12:58:233960 RTC_LOG(LS_INFO) << "Rejected data channel transport with mid="
3961 << content.mid();
3962
3963 rtc::StringBuilder sb;
3964 sb << "Rejected data channel transport with mid=" << content.mid();
3965 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
3966 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
Tommi840cf782023-10-21 14:47:563967 pc_->DestroyDataChannelTransport(error);
3968 } else if (!pc_->CreateDataChannelTransport(content.name)) {
Philipp Hanckeb81bf532023-07-18 09:03:393969 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
3970 "Failed to create data channel.");
Harald Alvestrandcdcfab02020-09-28 13:02:073971 }
3972 return RTCError::OK();
3973}
3974
Harald Alvestrandc06e3742020-10-01 10:23:333975bool SdpOfferAnswerHandler::ExpectSetLocalDescription(SdpType type) {
3976 PeerConnectionInterface::SignalingState state = signaling_state();
3977 if (type == SdpType::kOffer) {
3978 return (state == PeerConnectionInterface::kStable) ||
3979 (state == PeerConnectionInterface::kHaveLocalOffer);
3980 } else {
3981 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3982 return (state == PeerConnectionInterface::kHaveRemoteOffer) ||
3983 (state == PeerConnectionInterface::kHaveLocalPrAnswer);
3984 }
3985}
3986
3987bool SdpOfferAnswerHandler::ExpectSetRemoteDescription(SdpType type) {
3988 PeerConnectionInterface::SignalingState state = signaling_state();
3989 if (type == SdpType::kOffer) {
3990 return (state == PeerConnectionInterface::kStable) ||
3991 (state == PeerConnectionInterface::kHaveRemoteOffer);
3992 } else {
3993 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3994 return (state == PeerConnectionInterface::kHaveLocalOffer) ||
3995 (state == PeerConnectionInterface::kHaveRemotePrAnswer);
3996 }
3997}
3998
3999void SdpOfferAnswerHandler::FillInMissingRemoteMids(
4000 cricket::SessionDescription* new_remote_description) {
4001 RTC_DCHECK_RUN_ON(signaling_thread());
4002 RTC_DCHECK(new_remote_description);
4003 const cricket::ContentInfos no_infos;
4004 const cricket::ContentInfos& local_contents =
4005 (local_description() ? local_description()->description()->contents()
4006 : no_infos);
4007 const cricket::ContentInfos& remote_contents =
4008 (remote_description() ? remote_description()->description()->contents()
4009 : no_infos);
4010 for (size_t i = 0; i < new_remote_description->contents().size(); ++i) {
4011 cricket::ContentInfo& content = new_remote_description->contents()[i];
4012 if (!content.name.empty()) {
4013 continue;
4014 }
4015 std::string new_mid;
4016 absl::string_view source_explanation;
4017 if (IsUnifiedPlan()) {
4018 if (i < local_contents.size()) {
4019 new_mid = local_contents[i].name;
4020 source_explanation = "from the matching local media section";
4021 } else if (i < remote_contents.size()) {
4022 new_mid = remote_contents[i].name;
4023 source_explanation = "from the matching previous remote media section";
4024 } else {
Harald Alvestrandbc9ca252020-10-05 13:08:414025 new_mid = mid_generator_.GenerateString();
Harald Alvestrandc06e3742020-10-01 10:23:334026 source_explanation = "generated just now";
4027 }
4028 } else {
4029 new_mid = std::string(
4030 GetDefaultMidForPlanB(content.media_description()->type()));
4031 source_explanation = "to match pre-existing behavior";
4032 }
4033 RTC_DCHECK(!new_mid.empty());
4034 content.name = new_mid;
4035 new_remote_description->transport_infos()[i].content_name = new_mid;
4036 RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i
4037 << " is missing an a=mid line. Filling in the value '"
4038 << new_mid << "' " << source_explanation << ".";
4039 }
4040}
4041
4042rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
4043SdpOfferAnswerHandler::FindAvailableTransceiverToReceive(
4044 cricket::MediaType media_type) const {
4045 RTC_DCHECK_RUN_ON(signaling_thread());
4046 RTC_DCHECK(IsUnifiedPlan());
4047 // From JSEP section 5.10 (Applying a Remote Description):
4048 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of
4049 // the same type that were added to the PeerConnection by addTrack and are not
4050 // associated with any m= section and are not stopped, find the first such
4051 // RtpTransceiver.
Harald Alvestrande15fb152020-10-19 13:28:054052 for (auto transceiver : transceivers()->List()) {
Harald Alvestrandc06e3742020-10-01 10:23:334053 if (transceiver->media_type() == media_type &&
4054 transceiver->internal()->created_by_addtrack() && !transceiver->mid() &&
4055 !transceiver->stopped()) {
4056 return transceiver;
4057 }
4058 }
4059 return nullptr;
4060}
4061
4062const cricket::ContentInfo*
4063SdpOfferAnswerHandler::FindMediaSectionForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:364064 const RtpTransceiver* transceiver,
Harald Alvestrandc06e3742020-10-01 10:23:334065 const SessionDescriptionInterface* sdesc) const {
4066 RTC_DCHECK_RUN_ON(signaling_thread());
4067 RTC_DCHECK(transceiver);
4068 RTC_DCHECK(sdesc);
4069 if (IsUnifiedPlan()) {
Harald Alvestrand85466662021-04-19 21:21:364070 if (!transceiver->mid()) {
Harald Alvestrandc06e3742020-10-01 10:23:334071 // This transceiver is not associated with a media section yet.
4072 return nullptr;
4073 }
Harald Alvestrand85466662021-04-19 21:21:364074 return sdesc->description()->GetContentByName(*transceiver->mid());
Harald Alvestrandc06e3742020-10-01 10:23:334075 } else {
4076 // Plan B only allows at most one audio and one video section, so use the
4077 // first media section of that type.
4078 return cricket::GetFirstMediaContent(sdesc->description()->contents(),
4079 transceiver->media_type());
4080 }
4081}
4082
4083void SdpOfferAnswerHandler::GetOptionsForOffer(
4084 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4085 cricket::MediaSessionOptions* session_options) {
4086 RTC_DCHECK_RUN_ON(signaling_thread());
4087 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
4088
4089 if (IsUnifiedPlan()) {
4090 GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options);
4091 } else {
4092 GetOptionsForPlanBOffer(offer_answer_options, session_options);
4093 }
4094
Harald Alvestrandc06e3742020-10-01 10:23:334095 // Apply ICE restart flag and renomination flag.
4096 bool ice_restart = offer_answer_options.ice_restart || HasNewIceCredentials();
4097 for (auto& options : session_options->media_description_options) {
4098 options.transport_options.ice_restart = ice_restart;
4099 options.transport_options.enable_ice_renomination =
4100 pc_->configuration()->enable_ice_renomination;
4101 }
4102
Harald Alvestrandbc9ca252020-10-05 13:08:414103 session_options->rtcp_cname = rtcp_cname_;
Harald Alvestrandc06e3742020-10-01 10:23:334104 session_options->crypto_options = pc_->GetCryptoOptions();
4105 session_options->pooled_ice_credentials =
Danil Chapovalov9e09a1f2022-09-08 16:38:104106 context_->network_thread()->BlockingCall(
Niels Möller4bab23f2021-01-18 08:24:334107 [this] { return port_allocator()->GetPooledIceCredentials(); });
Harald Alvestrandc06e3742020-10-01 10:23:334108 session_options->offer_extmap_allow_mixed =
4109 pc_->configuration()->offer_extmap_allow_mixed;
4110
4111 // Allow fallback for using obsolete SCTP syntax.
Artem Titov880fa812021-07-30 20:30:234112 // Note that the default in `session_options` is true, while
4113 // the default in `options` is false.
Harald Alvestrandc06e3742020-10-01 10:23:334114 session_options->use_obsolete_sctp_sdp =
4115 offer_answer_options.use_obsolete_sctp_sdp;
4116}
4117
4118void SdpOfferAnswerHandler::GetOptionsForPlanBOffer(
4119 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4120 cricket::MediaSessionOptions* session_options) {
Harald Alvestrand8101e7b2022-05-23 14:57:474121 bool offer_new_data_description =
Harald Alvestrand5da3eb02023-03-15 20:39:424122 data_channel_controller()->HasUsedDataChannels();
Harald Alvestrand8101e7b2022-05-23 14:57:474123 bool send_audio = false;
4124 bool send_video = false;
4125 bool recv_audio = false;
4126 bool recv_video = false;
4127 if (ConfiguredForMedia()) {
4128 // Figure out transceiver directional preferences.
4129 send_audio =
4130 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
4131 send_video =
4132 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
Harald Alvestrandc06e3742020-10-01 10:23:334133
Harald Alvestrand8101e7b2022-05-23 14:57:474134 // By default, generate sendrecv/recvonly m= sections.
4135 recv_audio = true;
4136 recv_video = true;
4137 }
Harald Alvestrandc06e3742020-10-01 10:23:334138 // By default, only offer a new m= section if we have media to send with it.
4139 bool offer_new_audio_description = send_audio;
4140 bool offer_new_video_description = send_video;
Harald Alvestrand8101e7b2022-05-23 14:57:474141 if (ConfiguredForMedia()) {
4142 // The "offer_to_receive_X" options allow those defaults to be overridden.
4143 if (offer_answer_options.offer_to_receive_audio !=
4144 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
4145 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
4146 offer_new_audio_description =
4147 offer_new_audio_description ||
4148 (offer_answer_options.offer_to_receive_audio > 0);
4149 }
4150 if (offer_answer_options.offer_to_receive_video !=
4151 RTCOfferAnswerOptions::kUndefined) {
4152 recv_video = (offer_answer_options.offer_to_receive_video > 0);
4153 offer_new_video_description =
4154 offer_new_video_description ||
4155 (offer_answer_options.offer_to_receive_video > 0);
4156 }
Harald Alvestrandc06e3742020-10-01 10:23:334157 }
Harald Alvestrandc06e3742020-10-01 10:23:334158 absl::optional<size_t> audio_index;
4159 absl::optional<size_t> video_index;
4160 absl::optional<size_t> data_index;
4161 // If a current description exists, generate m= sections in the same order,
4162 // using the first audio/video/data section that appears and rejecting
4163 // extraneous ones.
4164 if (local_description()) {
Harald Alvestrandbc9ca252020-10-05 13:08:414165 GenerateMediaDescriptionOptions(
Harald Alvestrandc06e3742020-10-01 10:23:334166 local_description(),
4167 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
4168 RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
4169 &audio_index, &video_index, &data_index, session_options);
4170 }
4171
Harald Alvestrand8101e7b2022-05-23 14:57:474172 if (ConfiguredForMedia()) {
4173 // Add audio/video/data m= sections to the end if needed.
4174 if (!audio_index && offer_new_audio_description) {
4175 cricket::MediaDescriptionOptions options(
4176 cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
4177 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false);
4178 options.header_extensions =
4179 media_engine()->voice().GetRtpHeaderExtensions();
4180 session_options->media_description_options.push_back(options);
4181 audio_index = session_options->media_description_options.size() - 1;
4182 }
4183 if (!video_index && offer_new_video_description) {
4184 cricket::MediaDescriptionOptions options(
4185 cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
4186 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false);
4187 options.header_extensions =
4188 media_engine()->video().GetRtpHeaderExtensions();
4189 session_options->media_description_options.push_back(options);
4190 video_index = session_options->media_description_options.size() - 1;
4191 }
4192 cricket::MediaDescriptionOptions* audio_media_description_options =
4193 !audio_index
4194 ? nullptr
4195 : &session_options->media_description_options[*audio_index];
4196 cricket::MediaDescriptionOptions* video_media_description_options =
4197 !video_index
4198 ? nullptr
4199 : &session_options->media_description_options[*video_index];
4200
4201 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
4202 audio_media_description_options,
4203 video_media_description_options,
4204 offer_answer_options.num_simulcast_layers);
Harald Alvestrandc06e3742020-10-01 10:23:334205 }
4206 if (!data_index && offer_new_data_description) {
4207 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414208 GetMediaDescriptionOptionsForActiveData(cricket::CN_DATA));
Harald Alvestrandc06e3742020-10-01 10:23:334209 }
Harald Alvestrandc06e3742020-10-01 10:23:334210}
4211
4212void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanOffer(
4213 const RTCOfferAnswerOptions& offer_answer_options,
4214 cricket::MediaSessionOptions* session_options) {
4215 // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial
4216 // Offers) and 5.2.2 (Subsequent Offers).
4217 RTC_DCHECK_EQ(session_options->media_description_options.size(), 0);
4218 const ContentInfos no_infos;
4219 const ContentInfos& local_contents =
4220 (local_description() ? local_description()->description()->contents()
4221 : no_infos);
4222 const ContentInfos& remote_contents =
4223 (remote_description() ? remote_description()->description()->contents()
4224 : no_infos);
4225 // The mline indices that can be recycled. New transceivers should reuse these
4226 // slots first.
4227 std::queue<size_t> recycleable_mline_indices;
4228 // First, go through each media section that exists in either the local or
4229 // remote description and generate a media section in this offer for the
4230 // associated transceiver. If a media section can be recycled, generate a
4231 // default, rejected media section here that can be later overwritten.
4232 for (size_t i = 0;
4233 i < std::max(local_contents.size(), remote_contents.size()); ++i) {
Artem Titov880fa812021-07-30 20:30:234234 // Either `local_content` or `remote_content` is non-null.
Harald Alvestrandc06e3742020-10-01 10:23:334235 const ContentInfo* local_content =
4236 (i < local_contents.size() ? &local_contents[i] : nullptr);
4237 const ContentInfo* current_local_content =
4238 GetContentByIndex(current_local_description(), i);
4239 const ContentInfo* remote_content =
4240 (i < remote_contents.size() ? &remote_contents[i] : nullptr);
4241 const ContentInfo* current_remote_content =
4242 GetContentByIndex(current_remote_description(), i);
4243 bool had_been_rejected =
4244 (current_local_content && current_local_content->rejected) ||
4245 (current_remote_content && current_remote_content->rejected);
4246 const std::string& mid =
4247 (local_content ? local_content->name : remote_content->name);
4248 cricket::MediaType media_type =
4249 (local_content ? local_content->media_description()->type()
4250 : remote_content->media_description()->type());
4251 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
4252 media_type == cricket::MEDIA_TYPE_VIDEO) {
4253 // A media section is considered eligible for recycling if it is marked as
4254 // rejected in either the current local or current remote description.
Harald Alvestrande15fb152020-10-19 13:28:054255 auto transceiver = transceivers()->FindByMid(mid);
Harald Alvestrandc06e3742020-10-01 10:23:334256 if (!transceiver) {
4257 // No associated transceiver. The media section has been stopped.
4258 recycleable_mline_indices.push(i);
4259 session_options->media_description_options.push_back(
4260 cricket::MediaDescriptionOptions(media_type, mid,
4261 RtpTransceiverDirection::kInactive,
4262 /*stopped=*/true));
4263 } else {
4264 // NOTE: a stopping transceiver should be treated as a stopped one in
4265 // createOffer as specified in
4266 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
4267 if (had_been_rejected && transceiver->stopping()) {
4268 session_options->media_description_options.push_back(
4269 cricket::MediaDescriptionOptions(
4270 transceiver->media_type(), mid,
4271 RtpTransceiverDirection::kInactive,
4272 /*stopped=*/true));
4273 recycleable_mline_indices.push(i);
4274 } else {
4275 session_options->media_description_options.push_back(
4276 GetMediaDescriptionOptionsForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:364277 transceiver->internal(), mid,
Harald Alvestrandc06e3742020-10-01 10:23:334278 /*is_create_offer=*/true));
4279 // CreateOffer shouldn't really cause any state changes in
4280 // PeerConnection, but we need a way to match new transceivers to new
4281 // media sections in SetLocalDescription and JSEP specifies this is
4282 // done by recording the index of the media section generated for the
4283 // transceiver in the offer.
4284 transceiver->internal()->set_mline_index(i);
4285 }
4286 }
Philipp Hancke4e8c1152020-10-13 10:43:154287 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4288 RTC_DCHECK(local_content->rejected);
4289 session_options->media_description_options.push_back(
4290 cricket::MediaDescriptionOptions(media_type, mid,
4291 RtpTransceiverDirection::kInactive,
4292 /*stopped=*/true));
Harald Alvestrandc06e3742020-10-01 10:23:334293 } else {
4294 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4295 if (had_been_rejected) {
4296 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414297 GetMediaDescriptionOptionsForRejectedData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:334298 } else {
Tommi840cf782023-10-21 14:47:564299 const auto data_mid = pc_->sctp_mid();
4300 RTC_CHECK(data_mid);
4301 if (mid == data_mid.value()) {
Harald Alvestrandc06e3742020-10-01 10:23:334302 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414303 GetMediaDescriptionOptionsForActiveData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:334304 } else {
4305 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414306 GetMediaDescriptionOptionsForRejectedData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:334307 }
4308 }
4309 }
4310 }
4311
4312 // Next, look for transceivers that are newly added (that is, are not stopped
4313 // and not associated). Reuse media sections marked as recyclable first,
4314 // otherwise append to the end of the offer. New media sections should be
4315 // added in the order they were added to the PeerConnection.
Harald Alvestrand8101e7b2022-05-23 14:57:474316 if (ConfiguredForMedia()) {
4317 for (const auto& transceiver : transceivers()->ListInternal()) {
4318 if (transceiver->mid() || transceiver->stopping()) {
4319 continue;
4320 }
4321 size_t mline_index;
4322 if (!recycleable_mline_indices.empty()) {
4323 mline_index = recycleable_mline_indices.front();
4324 recycleable_mline_indices.pop();
4325 session_options->media_description_options[mline_index] =
4326 GetMediaDescriptionOptionsForTransceiver(
4327 transceiver, mid_generator_.GenerateString(),
4328 /*is_create_offer=*/true);
4329 } else {
4330 mline_index = session_options->media_description_options.size();
4331 session_options->media_description_options.push_back(
4332 GetMediaDescriptionOptionsForTransceiver(
4333 transceiver, mid_generator_.GenerateString(),
4334 /*is_create_offer=*/true));
4335 }
4336 // See comment above for why CreateOffer changes the transceiver's state.
4337 transceiver->set_mline_index(mline_index);
Harald Alvestrandc06e3742020-10-01 10:23:334338 }
Harald Alvestrandc06e3742020-10-01 10:23:334339 }
Harald Alvestrand5da3eb02023-03-15 20:39:424340 // Lastly, add a m-section if we have requested local data channels and an
4341 // m section does not already exist.
Tommi840cf782023-10-21 14:47:564342 if (!pc_->sctp_mid() && data_channel_controller()->HasDataChannels()) {
Philipp Hancke522380f2023-05-09 07:41:034343 // Attempt to recycle a stopped m-line.
Tommi840cf782023-10-21 14:47:564344 // TODO(crbug.com/1442604): sctp_mid() should return the mid if one was
Philipp Hancke522380f2023-05-09 07:41:034345 // ever created but rejected.
4346 bool recycled = false;
4347 for (size_t i = 0; i < session_options->media_description_options.size();
4348 i++) {
4349 auto media_description = session_options->media_description_options[i];
4350 if (media_description.type == cricket::MEDIA_TYPE_DATA &&
4351 media_description.stopped) {
4352 session_options->media_description_options[i] =
4353 GetMediaDescriptionOptionsForActiveData(media_description.mid);
4354 recycled = true;
4355 break;
4356 }
4357 }
4358 if (!recycled) {
4359 session_options->media_description_options.push_back(
4360 GetMediaDescriptionOptionsForActiveData(
4361 mid_generator_.GenerateString()));
4362 }
Harald Alvestrandc06e3742020-10-01 10:23:334363 }
4364}
4365
4366void SdpOfferAnswerHandler::GetOptionsForAnswer(
4367 const RTCOfferAnswerOptions& offer_answer_options,
4368 cricket::MediaSessionOptions* session_options) {
4369 RTC_DCHECK_RUN_ON(signaling_thread());
4370 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
4371
4372 if (IsUnifiedPlan()) {
4373 GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options);
4374 } else {
4375 GetOptionsForPlanBAnswer(offer_answer_options, session_options);
4376 }
4377
Harald Alvestrandc06e3742020-10-01 10:23:334378 // Apply ICE renomination flag.
4379 for (auto& options : session_options->media_description_options) {
4380 options.transport_options.enable_ice_renomination =
4381 pc_->configuration()->enable_ice_renomination;
4382 }
4383
Harald Alvestrandbc9ca252020-10-05 13:08:414384 session_options->rtcp_cname = rtcp_cname_;
Harald Alvestrandc06e3742020-10-01 10:23:334385 session_options->crypto_options = pc_->GetCryptoOptions();
4386 session_options->pooled_ice_credentials =
Danil Chapovalov9e09a1f2022-09-08 16:38:104387 context_->network_thread()->BlockingCall(
Niels Möller4bab23f2021-01-18 08:24:334388 [this] { return port_allocator()->GetPooledIceCredentials(); });
Harald Alvestrandc06e3742020-10-01 10:23:334389}
4390
4391void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer(
4392 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4393 cricket::MediaSessionOptions* session_options) {
Harald Alvestrand8101e7b2022-05-23 14:57:474394 bool send_audio = false;
4395 bool recv_audio = false;
4396 bool send_video = false;
4397 bool recv_video = false;
Harald Alvestrandc06e3742020-10-01 10:23:334398
Harald Alvestrand8101e7b2022-05-23 14:57:474399 if (ConfiguredForMedia()) {
4400 // Figure out transceiver directional preferences.
4401 send_audio =
4402 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
4403 send_video =
4404 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
Harald Alvestrandc06e3742020-10-01 10:23:334405
Harald Alvestrand8101e7b2022-05-23 14:57:474406 // By default, generate sendrecv/recvonly m= sections. The direction is also
4407 // restricted by the direction in the offer.
4408 recv_audio = true;
4409 recv_video = true;
4410
4411 // The "offer_to_receive_X" options allow those defaults to be overridden.
4412 if (offer_answer_options.offer_to_receive_audio !=
4413 RTCOfferAnswerOptions::kUndefined) {
4414 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
4415 }
4416 if (offer_answer_options.offer_to_receive_video !=
4417 RTCOfferAnswerOptions::kUndefined) {
4418 recv_video = (offer_answer_options.offer_to_receive_video > 0);
4419 }
Harald Alvestrandc06e3742020-10-01 10:23:334420 }
4421
4422 absl::optional<size_t> audio_index;
4423 absl::optional<size_t> video_index;
4424 absl::optional<size_t> data_index;
4425
4426 // Generate m= sections that match those in the offer.
4427 // Note that mediasession.cc will handle intersection our preferred
4428 // direction with the offered direction.
Harald Alvestrandbc9ca252020-10-05 13:08:414429 GenerateMediaDescriptionOptions(
Harald Alvestrandc06e3742020-10-01 10:23:334430 remote_description(),
4431 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
4432 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index,
4433 &video_index, &data_index, session_options);
4434
4435 cricket::MediaDescriptionOptions* audio_media_description_options =
4436 !audio_index ? nullptr
4437 : &session_options->media_description_options[*audio_index];
4438 cricket::MediaDescriptionOptions* video_media_description_options =
4439 !video_index ? nullptr
4440 : &session_options->media_description_options[*video_index];
4441
Harald Alvestrand8101e7b2022-05-23 14:57:474442 if (ConfiguredForMedia()) {
4443 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
4444 audio_media_description_options,
4445 video_media_description_options,
4446 offer_answer_options.num_simulcast_layers);
4447 }
Harald Alvestrandc06e3742020-10-01 10:23:334448}
4449
4450void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanAnswer(
4451 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4452 cricket::MediaSessionOptions* session_options) {
4453 // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial
4454 // Answers) and 5.3.2 (Subsequent Answers).
4455 RTC_DCHECK(remote_description());
4456 RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer);
4457 for (const ContentInfo& content :
4458 remote_description()->description()->contents()) {
4459 cricket::MediaType media_type = content.media_description()->type();
4460 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
4461 media_type == cricket::MEDIA_TYPE_VIDEO) {
Harald Alvestrande15fb152020-10-19 13:28:054462 auto transceiver = transceivers()->FindByMid(content.name);
Harald Alvestrandc06e3742020-10-01 10:23:334463 if (transceiver) {
4464 session_options->media_description_options.push_back(
4465 GetMediaDescriptionOptionsForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:364466 transceiver->internal(), content.name,
Harald Alvestrandc06e3742020-10-01 10:23:334467 /*is_create_offer=*/false));
4468 } else {
4469 // This should only happen with rejected transceivers.
4470 RTC_DCHECK(content.rejected);
4471 session_options->media_description_options.push_back(
4472 cricket::MediaDescriptionOptions(media_type, content.name,
4473 RtpTransceiverDirection::kInactive,
4474 /*stopped=*/true));
4475 }
Philipp Hancke4e8c1152020-10-13 10:43:154476 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4477 RTC_DCHECK(content.rejected);
4478 session_options->media_description_options.push_back(
4479 cricket::MediaDescriptionOptions(media_type, content.name,
4480 RtpTransceiverDirection::kInactive,
4481 /*stopped=*/true));
Harald Alvestrandc06e3742020-10-01 10:23:334482 } else {
4483 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4484 // Reject all data sections if data channels are disabled.
4485 // Reject a data section if it has already been rejected.
4486 // Reject all data sections except for the first one.
Tommi840cf782023-10-21 14:47:564487 if (content.rejected || content.name != *(pc_->sctp_mid())) {
Harald Alvestrandc06e3742020-10-01 10:23:334488 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414489 GetMediaDescriptionOptionsForRejectedData(content.name));
Harald Alvestrandc06e3742020-10-01 10:23:334490 } else {
4491 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414492 GetMediaDescriptionOptionsForActiveData(content.name));
Harald Alvestrandc06e3742020-10-01 10:23:334493 }
4494 }
4495 }
4496}
4497
Harald Alvestranda474fbf2020-10-01 16:47:234498const char* SdpOfferAnswerHandler::SessionErrorToString(
4499 SessionError error) const {
4500 switch (error) {
4501 case SessionError::kNone:
4502 return "ERROR_NONE";
4503 case SessionError::kContent:
4504 return "ERROR_CONTENT";
4505 case SessionError::kTransport:
4506 return "ERROR_TRANSPORT";
4507 }
Artem Titovd3251962021-11-15 15:57:074508 RTC_DCHECK_NOTREACHED();
Harald Alvestranda474fbf2020-10-01 16:47:234509 return "";
4510}
4511
4512std::string SdpOfferAnswerHandler::GetSessionErrorMsg() {
4513 RTC_DCHECK_RUN_ON(signaling_thread());
4514 rtc::StringBuilder desc;
4515 desc << kSessionError << SessionErrorToString(session_error()) << ". ";
4516 desc << kSessionErrorDesc << session_error_desc() << ".";
4517 return desc.Release();
4518}
4519
4520void SdpOfferAnswerHandler::SetSessionError(SessionError error,
4521 const std::string& error_desc) {
4522 RTC_DCHECK_RUN_ON(signaling_thread());
4523 if (error != session_error_) {
4524 session_error_ = error;
4525 session_error_desc_ = error_desc;
4526 }
4527}
4528
4529RTCError SdpOfferAnswerHandler::HandleLegacyOfferOptions(
4530 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
4531 RTC_DCHECK_RUN_ON(signaling_thread());
4532 RTC_DCHECK(IsUnifiedPlan());
4533
4534 if (options.offer_to_receive_audio == 0) {
4535 RemoveRecvDirectionFromReceivingTransceiversOfType(
4536 cricket::MEDIA_TYPE_AUDIO);
4537 } else if (options.offer_to_receive_audio == 1) {
4538 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO);
4539 } else if (options.offer_to_receive_audio > 1) {
4540 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4541 "offer_to_receive_audio > 1 is not supported.");
4542 }
4543
4544 if (options.offer_to_receive_video == 0) {
4545 RemoveRecvDirectionFromReceivingTransceiversOfType(
4546 cricket::MEDIA_TYPE_VIDEO);
4547 } else if (options.offer_to_receive_video == 1) {
4548 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO);
4549 } else if (options.offer_to_receive_video > 1) {
4550 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4551 "offer_to_receive_video > 1 is not supported.");
4552 }
4553
4554 return RTCError::OK();
4555}
4556
4557void SdpOfferAnswerHandler::RemoveRecvDirectionFromReceivingTransceiversOfType(
4558 cricket::MediaType media_type) {
4559 for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) {
4560 RtpTransceiverDirection new_direction =
4561 RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false);
4562 if (new_direction != transceiver->direction()) {
4563 RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type)
4564 << " transceiver (MID="
4565 << transceiver->mid().value_or("<not set>") << ") from "
4566 << RtpTransceiverDirectionToString(
4567 transceiver->direction())
4568 << " to "
4569 << RtpTransceiverDirectionToString(new_direction)
4570 << " since CreateOffer specified offer_to_receive=0";
4571 transceiver->internal()->set_direction(new_direction);
4572 }
4573 }
4574}
4575
4576void SdpOfferAnswerHandler::AddUpToOneReceivingTransceiverOfType(
4577 cricket::MediaType media_type) {
4578 RTC_DCHECK_RUN_ON(signaling_thread());
4579 if (GetReceivingTransceiversOfType(media_type).empty()) {
4580 RTC_LOG(LS_INFO)
4581 << "Adding one recvonly " << cricket::MediaTypeToString(media_type)
4582 << " transceiver since CreateOffer specified offer_to_receive=1";
4583 RtpTransceiverInit init;
4584 init.direction = RtpTransceiverDirection::kRecvOnly;
4585 pc_->AddTransceiver(media_type, nullptr, init,
4586 /*update_negotiation_needed=*/false);
4587 }
4588}
4589
4590std::vector<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
4591SdpOfferAnswerHandler::GetReceivingTransceiversOfType(
4592 cricket::MediaType media_type) {
4593 std::vector<
4594 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
4595 receiving_transceivers;
Harald Alvestrande15fb152020-10-19 13:28:054596 for (const auto& transceiver : transceivers()->List()) {
Harald Alvestranda474fbf2020-10-01 16:47:234597 if (!transceiver->stopped() && transceiver->media_type() == media_type &&
4598 RtpTransceiverDirectionHasRecv(transceiver->direction())) {
4599 receiving_transceivers.push_back(transceiver);
4600 }
4601 }
4602 return receiving_transceivers;
4603}
4604
4605void SdpOfferAnswerHandler::ProcessRemovalOfRemoteTrack(
4606 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
4607 transceiver,
4608 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>* remove_list,
4609 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4610 RTC_DCHECK(transceiver->mid());
4611 RTC_LOG(LS_INFO) << "Processing the removal of a track for MID="
4612 << *transceiver->mid();
4613 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
4614 transceiver->internal()->receiver_internal()->streams();
4615 // This will remove the remote track from the streams.
4616 transceiver->internal()->receiver_internal()->set_stream_ids({});
4617 remove_list->push_back(transceiver);
4618 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
4619}
4620
4621void SdpOfferAnswerHandler::RemoveRemoteStreamsIfEmpty(
4622 const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& remote_streams,
4623 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4624 RTC_DCHECK_RUN_ON(signaling_thread());
4625 // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of
4626 // streams, see if the stream was removed by checking if this was the last
4627 // receiver with that stream ID.
4628 for (const auto& remote_stream : remote_streams) {
4629 if (remote_stream->GetAudioTracks().empty() &&
4630 remote_stream->GetVideoTracks().empty()) {
Niels Möllerafb246b2022-04-20 12:26:504631 remote_streams_->RemoveStream(remote_stream.get());
Harald Alvestranda474fbf2020-10-01 16:47:234632 removed_streams->push_back(remote_stream);
4633 }
4634 }
4635}
4636
4637void SdpOfferAnswerHandler::RemoveSenders(cricket::MediaType media_type) {
4638 RTC_DCHECK_RUN_ON(signaling_thread());
4639 UpdateLocalSenders(std::vector<cricket::StreamParams>(), media_type);
4640 UpdateRemoteSendersList(std::vector<cricket::StreamParams>(), false,
4641 media_type, nullptr);
4642}
4643
4644void SdpOfferAnswerHandler::UpdateLocalSenders(
4645 const std::vector<cricket::StreamParams>& streams,
4646 cricket::MediaType media_type) {
Markus Handell518669d2021-06-07 11:30:464647 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateLocalSenders");
Harald Alvestranda474fbf2020-10-01 16:47:234648 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrande15fb152020-10-19 13:28:054649 std::vector<RtpSenderInfo>* current_senders =
4650 rtp_manager()->GetLocalSenderInfos(media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234651
4652 // Find removed tracks. I.e., tracks where the track id, stream id or ssrc
4653 // don't match the new StreamParam.
4654 for (auto sender_it = current_senders->begin();
4655 sender_it != current_senders->end();
4656 /* incremented manually */) {
Harald Alvestrande15fb152020-10-19 13:28:054657 const RtpSenderInfo& info = *sender_it;
Harald Alvestranda474fbf2020-10-01 16:47:234658 const cricket::StreamParams* params =
4659 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4660 if (!params || params->id != info.sender_id ||
4661 params->first_stream_id() != info.stream_id) {
Harald Alvestrande15fb152020-10-19 13:28:054662 rtp_manager()->OnLocalSenderRemoved(info, media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234663 sender_it = current_senders->erase(sender_it);
4664 } else {
4665 ++sender_it;
4666 }
4667 }
4668
4669 // Find new and active senders.
4670 for (const cricket::StreamParams& params : streams) {
Artem Titovcfea2182021-08-09 23:22:314671 // The sync_label is the MediaStream label and the `stream.id` is the
Harald Alvestranda474fbf2020-10-01 16:47:234672 // sender id.
4673 const std::string& stream_id = params.first_stream_id();
4674 const std::string& sender_id = params.id;
4675 uint32_t ssrc = params.first_ssrc();
Harald Alvestrande15fb152020-10-19 13:28:054676 const RtpSenderInfo* sender_info =
4677 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:234678 if (!sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:054679 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
4680 rtp_manager()->OnLocalSenderAdded(current_senders->back(), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234681 }
4682 }
4683}
4684
4685void SdpOfferAnswerHandler::UpdateRemoteSendersList(
4686 const cricket::StreamParamsVec& streams,
4687 bool default_sender_needed,
4688 cricket::MediaType media_type,
4689 StreamCollection* new_streams) {
Markus Handell518669d2021-06-07 11:30:464690 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateRemoteSendersList");
Harald Alvestranda474fbf2020-10-01 16:47:234691 RTC_DCHECK_RUN_ON(signaling_thread());
4692 RTC_DCHECK(!IsUnifiedPlan());
4693
Harald Alvestrande15fb152020-10-19 13:28:054694 std::vector<RtpSenderInfo>* current_senders =
4695 rtp_manager()->GetRemoteSenderInfos(media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234696
4697 // Find removed senders. I.e., senders where the sender id or ssrc don't match
4698 // the new StreamParam.
4699 for (auto sender_it = current_senders->begin();
4700 sender_it != current_senders->end();
4701 /* incremented manually */) {
Harald Alvestrande15fb152020-10-19 13:28:054702 const RtpSenderInfo& info = *sender_it;
Harald Alvestranda474fbf2020-10-01 16:47:234703 const cricket::StreamParams* params =
4704 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4705 std::string params_stream_id;
4706 if (params) {
4707 params_stream_id =
4708 (!params->first_stream_id().empty() ? params->first_stream_id()
4709 : kDefaultStreamId);
4710 }
4711 bool sender_exists = params && params->id == info.sender_id &&
4712 params_stream_id == info.stream_id;
4713 // If this is a default track, and we still need it, don't remove it.
4714 if ((info.stream_id == kDefaultStreamId && default_sender_needed) ||
4715 sender_exists) {
4716 ++sender_it;
4717 } else {
Harald Alvestrande15fb152020-10-19 13:28:054718 rtp_manager()->OnRemoteSenderRemoved(
4719 info, remote_streams_->find(info.stream_id), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234720 sender_it = current_senders->erase(sender_it);
4721 }
4722 }
4723
4724 // Find new and active senders.
4725 for (const cricket::StreamParams& params : streams) {
4726 if (!params.has_ssrcs()) {
4727 // The remote endpoint has streams, but didn't signal ssrcs. For an active
4728 // sender, this means it is coming from a Unified Plan endpoint,so we just
4729 // create a default.
4730 default_sender_needed = true;
4731 break;
4732 }
4733
Artem Titovcfea2182021-08-09 23:22:314734 // `params.id` is the sender id and the stream id uses the first of
4735 // `params.stream_ids`. The remote description could come from a Unified
Harald Alvestranda474fbf2020-10-01 16:47:234736 // Plan endpoint, with multiple or no stream_ids() signaled. Since this is
4737 // not supported in Plan B, we just take the first here and create the
4738 // default stream ID if none is specified.
4739 const std::string& stream_id =
4740 (!params.first_stream_id().empty() ? params.first_stream_id()
4741 : kDefaultStreamId);
4742 const std::string& sender_id = params.id;
4743 uint32_t ssrc = params.first_ssrc();
4744
Niels Möllere7cc8832022-01-04 14:20:034745 rtc::scoped_refptr<MediaStreamInterface> stream(
4746 remote_streams_->find(stream_id));
Harald Alvestranda474fbf2020-10-01 16:47:234747 if (!stream) {
4748 // This is a new MediaStream. Create a new remote MediaStream.
4749 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
4750 MediaStream::Create(stream_id));
Harald Alvestrand6f04b6532020-10-09 11:42:174751 remote_streams_->AddStream(stream);
Harald Alvestranda474fbf2020-10-01 16:47:234752 new_streams->AddStream(stream);
4753 }
4754
Harald Alvestrande15fb152020-10-19 13:28:054755 const RtpSenderInfo* sender_info =
4756 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:234757 if (!sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:054758 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
Niels Möllerafb246b2022-04-20 12:26:504759 rtp_manager()->OnRemoteSenderAdded(current_senders->back(), stream.get(),
Harald Alvestrande15fb152020-10-19 13:28:054760 media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234761 }
4762 }
4763
4764 // Add default sender if necessary.
4765 if (default_sender_needed) {
Niels Möllere7cc8832022-01-04 14:20:034766 rtc::scoped_refptr<MediaStreamInterface> default_stream(
4767 remote_streams_->find(kDefaultStreamId));
Harald Alvestranda474fbf2020-10-01 16:47:234768 if (!default_stream) {
4769 // Create the new default MediaStream.
4770 default_stream = MediaStreamProxy::Create(
4771 rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId));
Harald Alvestrand6f04b6532020-10-09 11:42:174772 remote_streams_->AddStream(default_stream);
Harald Alvestranda474fbf2020-10-01 16:47:234773 new_streams->AddStream(default_stream);
4774 }
4775 std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO)
4776 ? kDefaultAudioSenderId
4777 : kDefaultVideoSenderId;
Harald Alvestrande15fb152020-10-19 13:28:054778 const RtpSenderInfo* default_sender_info = rtp_manager()->FindSenderInfo(
4779 *current_senders, kDefaultStreamId, default_sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:234780 if (!default_sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:054781 current_senders->push_back(
4782 RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0));
4783 rtp_manager()->OnRemoteSenderAdded(current_senders->back(),
Niels Möllerafb246b2022-04-20 12:26:504784 default_stream.get(), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234785 }
4786 }
4787}
4788
Taylor Brandstetterd0acbd82021-01-25 21:44:554789void SdpOfferAnswerHandler::EnableSending() {
Markus Handell518669d2021-06-07 11:30:464790 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::EnableSending");
Taylor Brandstetterd0acbd82021-01-25 21:44:554791 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrand8101e7b2022-05-23 14:57:474792 if (!ConfiguredForMedia()) {
4793 return;
4794 }
Harald Alvestrand85466662021-04-19 21:21:364795 for (const auto& transceiver : transceivers()->ListInternal()) {
4796 cricket::ChannelInterface* channel = transceiver->channel();
Tommi1959f8f2021-04-26 08:20:194797 if (channel) {
Taylor Brandstetterd0acbd82021-01-25 21:44:554798 channel->Enable(true);
4799 }
4800 }
Taylor Brandstetterd0acbd82021-01-25 21:44:554801}
4802
Harald Alvestranda474fbf2020-10-01 16:47:234803RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
4804 SdpType type,
Henrik Boströmf8187e02021-04-26 19:04:264805 cricket::ContentSource source,
4806 const std::map<std::string, const cricket::ContentGroup*>&
4807 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:464808 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownMediaDescription");
Harald Alvestranda474fbf2020-10-01 16:47:234809 const SessionDescriptionInterface* sdesc =
4810 (source == cricket::CS_LOCAL ? local_description()
4811 : remote_description());
4812 RTC_DCHECK_RUN_ON(signaling_thread());
4813 RTC_DCHECK(sdesc);
4814
Harald Alvestrand8101e7b2022-05-23 14:57:474815 if (ConfiguredForMedia()) {
Danil Chapovalov9e09a1f2022-09-08 16:38:104816 // Note: This will perform a BlockingCall over to the worker thread, which
4817 // we'll also do in a loop below.
Harald Alvestrand8101e7b2022-05-23 14:57:474818 if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) {
4819 // Note that this is never expected to fail, since RtpDemuxer doesn't
4820 // return an error when changing payload type demux criteria, which is all
4821 // this does.
Philipp Hanckeb81bf532023-07-18 09:03:394822 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
4823 "Failed to update payload type demuxing state.");
Taylor Brandstetterd0acbd82021-01-25 21:44:554824 }
Tommicc7a3682021-05-04 12:59:384825
Harald Alvestrand8101e7b2022-05-23 14:57:474826 // Push down the new SDP media section for each audio/video transceiver.
4827 auto rtp_transceivers = transceivers()->ListInternal();
4828 std::vector<
4829 std::pair<cricket::ChannelInterface*, const MediaContentDescription*>>
4830 channels;
4831 for (const auto& transceiver : rtp_transceivers) {
4832 const ContentInfo* content_info =
4833 FindMediaSectionForTransceiver(transceiver, sdesc);
4834 cricket::ChannelInterface* channel = transceiver->channel();
4835 if (!channel || !content_info || content_info->rejected) {
4836 continue;
4837 }
4838 const MediaContentDescription* content_desc =
4839 content_info->media_description();
4840 if (!content_desc) {
4841 continue;
4842 }
Tommicc7a3682021-05-04 12:59:384843
Harald Alvestrand8101e7b2022-05-23 14:57:474844 transceiver->OnNegotiationUpdate(type, content_desc);
4845 channels.push_back(std::make_pair(channel, content_desc));
4846 }
4847
4848 // This for-loop of invokes helps audio impairment during re-negotiations.
4849 // One of the causes is that downstairs decoder creation is synchronous at
4850 // the moment, and that a decoder is created for each codec listed in the
4851 // SDP.
4852 //
4853 // TODO(bugs.webrtc.org/12840): consider merging the invokes again after
4854 // these projects have shipped:
4855 // - bugs.webrtc.org/12462
4856 // - crbug.com/1157227
4857 // - crbug.com/1187289
4858 for (const auto& entry : channels) {
4859 std::string error;
Jared Siskinbceec842023-04-20 21:10:514860 bool success = context_->worker_thread()->BlockingCall([&]() {
4861 return (source == cricket::CS_LOCAL)
4862 ? entry.first->SetLocalContent(entry.second, type, error)
4863 : entry.first->SetRemoteContent(entry.second, type, error);
4864 });
Harald Alvestrand8101e7b2022-05-23 14:57:474865 if (!success) {
Philipp Hanckeb81bf532023-07-18 09:03:394866 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error);
Harald Alvestrand8101e7b2022-05-23 14:57:474867 }
Harald Alvestranda474fbf2020-10-01 16:47:234868 }
4869 }
Harald Alvestranda474fbf2020-10-01 16:47:234870 // Need complete offer/answer with an SCTP m= section before starting SCTP,
4871 // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
4872 if (pc_->sctp_mid() && local_description() && remote_description()) {
Harald Alvestranda474fbf2020-10-01 16:47:234873 auto local_sctp_description = cricket::GetFirstSctpDataContentDescription(
4874 local_description()->description());
4875 auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription(
4876 remote_description()->description());
Tomas Gunnarsson92eebef2021-02-10 12:05:444877 if (local_sctp_description && remote_sctp_description) {
Harald Alvestranda474fbf2020-10-01 16:47:234878 int max_message_size;
4879 // A remote max message size of zero means "any size supported".
4880 // We configure the connection with our own max message size.
4881 if (remote_sctp_description->max_message_size() == 0) {
4882 max_message_size = local_sctp_description->max_message_size();
4883 } else {
4884 max_message_size =
4885 std::min(local_sctp_description->max_message_size(),
4886 remote_sctp_description->max_message_size());
4887 }
Tomas Gunnarsson92eebef2021-02-10 12:05:444888 pc_->StartSctpTransport(local_sctp_description->port(),
4889 remote_sctp_description->port(),
4890 max_message_size);
Harald Alvestranda474fbf2020-10-01 16:47:234891 }
4892 }
4893
4894 return RTCError::OK();
4895}
4896
4897RTCError SdpOfferAnswerHandler::PushdownTransportDescription(
4898 cricket::ContentSource source,
4899 SdpType type) {
Markus Handell518669d2021-06-07 11:30:464900 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownTransportDescription");
Harald Alvestranda474fbf2020-10-01 16:47:234901 RTC_DCHECK_RUN_ON(signaling_thread());
4902
4903 if (source == cricket::CS_LOCAL) {
4904 const SessionDescriptionInterface* sdesc = local_description();
4905 RTC_DCHECK(sdesc);
Tommic5605202024-01-11 21:15:274906 const auto* remote = remote_description();
4907 return transport_controller_s()->SetLocalDescription(
4908 type, sdesc->description(), remote ? remote->description() : nullptr);
Harald Alvestranda474fbf2020-10-01 16:47:234909 } else {
4910 const SessionDescriptionInterface* sdesc = remote_description();
4911 RTC_DCHECK(sdesc);
Tommic5605202024-01-11 21:15:274912 const auto* local = local_description();
4913 return transport_controller_s()->SetRemoteDescription(
4914 type, local ? local->description() : nullptr, sdesc->description());
Harald Alvestranda474fbf2020-10-01 16:47:234915 }
4916}
4917
4918void SdpOfferAnswerHandler::RemoveStoppedTransceivers() {
Markus Handell518669d2021-06-07 11:30:464919 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveStoppedTransceivers");
Harald Alvestranda474fbf2020-10-01 16:47:234920 RTC_DCHECK_RUN_ON(signaling_thread());
4921 // 3.2.10.1: For each transceiver in the connection's set of transceivers
4922 // run the following steps:
4923 if (!IsUnifiedPlan())
4924 return;
Harald Alvestrand8101e7b2022-05-23 14:57:474925 if (!ConfiguredForMedia()) {
4926 return;
4927 }
Harald Alvestranda474fbf2020-10-01 16:47:234928 // Traverse a copy of the transceiver list.
Harald Alvestrande15fb152020-10-19 13:28:054929 auto transceiver_list = transceivers()->List();
Harald Alvestranda474fbf2020-10-01 16:47:234930 for (auto transceiver : transceiver_list) {
4931 // 3.2.10.1.1: If transceiver is stopped, associated with an m= section
4932 // and the associated m= section is rejected in
4933 // connection.[[CurrentLocalDescription]] or
4934 // connection.[[CurrentRemoteDescription]], remove the
4935 // transceiver from the connection's set of transceivers.
4936 if (!transceiver->stopped()) {
4937 continue;
4938 }
Harald Alvestrand85466662021-04-19 21:21:364939 const ContentInfo* local_content = FindMediaSectionForTransceiver(
4940 transceiver->internal(), local_description());
4941 const ContentInfo* remote_content = FindMediaSectionForTransceiver(
4942 transceiver->internal(), remote_description());
Harald Alvestranda474fbf2020-10-01 16:47:234943 if ((local_content && local_content->rejected) ||
4944 (remote_content && remote_content->rejected)) {
4945 RTC_LOG(LS_INFO) << "Dissociating transceiver"
Tomas Gunnarsson7fa8d462021-04-16 12:28:264946 " since the media section is being recycled.";
Harald Alvestranda474fbf2020-10-01 16:47:234947 transceiver->internal()->set_mid(absl::nullopt);
4948 transceiver->internal()->set_mline_index(absl::nullopt);
Tomas Gunnarsson7fa8d462021-04-16 12:28:264949 } else if (!local_content && !remote_content) {
Harald Alvestranda474fbf2020-10-01 16:47:234950 // TODO(bugs.webrtc.org/11973): Consider if this should be removed already
4951 // See https://github.com/w3c/webrtc-pc/issues/2576
4952 RTC_LOG(LS_INFO)
4953 << "Dropping stopped transceiver that was never associated";
Harald Alvestranda474fbf2020-10-01 16:47:234954 }
Tomas Gunnarsson7fa8d462021-04-16 12:28:264955 transceivers()->Remove(transceiver);
Harald Alvestranda474fbf2020-10-01 16:47:234956 }
4957}
4958
4959void SdpOfferAnswerHandler::RemoveUnusedChannels(
4960 const SessionDescription* desc) {
4961 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrand8101e7b2022-05-23 14:57:474962 if (ConfiguredForMedia()) {
4963 // Destroy video channel first since it may have a pointer to the
4964 // voice channel.
4965 const cricket::ContentInfo* video_info =
4966 cricket::GetFirstVideoContent(desc);
4967 if (!video_info || video_info->rejected) {
4968 rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel();
4969 }
Artem Titovc6c02ef2022-05-09 08:30:094970
Harald Alvestrand8101e7b2022-05-23 14:57:474971 const cricket::ContentInfo* audio_info =
4972 cricket::GetFirstAudioContent(desc);
4973 if (!audio_info || audio_info->rejected) {
4974 rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel();
4975 }
Artem Titovc6c02ef2022-05-09 08:30:094976 }
Harald Alvestranda474fbf2020-10-01 16:47:234977 const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc);
Florent Castellidcb9ffc2021-06-29 12:58:234978 if (!data_info) {
4979 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA,
4980 "No data channel section in the description.");
4981 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
Tommi840cf782023-10-21 14:47:564982 pc_->DestroyDataChannelTransport(error);
Florent Castellidcb9ffc2021-06-29 12:58:234983 } else if (data_info->rejected) {
4984 rtc::StringBuilder sb;
4985 sb << "Rejected data channel with mid=" << data_info->name << ".";
4986
4987 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
4988 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
Tommi840cf782023-10-21 14:47:564989 pc_->DestroyDataChannelTransport(error);
Harald Alvestranda474fbf2020-10-01 16:47:234990 }
4991}
4992
Harald Alvestrandbc9ca252020-10-05 13:08:414993void SdpOfferAnswerHandler::UpdateEndedRemoteMediaStreams() {
4994 RTC_DCHECK_RUN_ON(signaling_thread());
4995 std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_to_remove;
Harald Alvestrand6f04b6532020-10-09 11:42:174996 for (size_t i = 0; i < remote_streams_->count(); ++i) {
4997 MediaStreamInterface* stream = remote_streams_->at(i);
Harald Alvestrandbc9ca252020-10-05 13:08:414998 if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) {
Niels Möllere7cc8832022-01-04 14:20:034999 streams_to_remove.push_back(
5000 rtc::scoped_refptr<MediaStreamInterface>(stream));
Harald Alvestrandbc9ca252020-10-05 13:08:415001 }
5002 }
5003
5004 for (auto& stream : streams_to_remove) {
Niels Möllerafb246b2022-04-20 12:26:505005 remote_streams_->RemoveStream(stream.get());
Harald Alvestrandbc9ca252020-10-05 13:08:415006 pc_->Observer()->OnRemoveStream(std::move(stream));
5007 }
5008}
5009
Tomas Gunnarsson0dd75392022-01-17 18:19:565010bool SdpOfferAnswerHandler::UseCandidatesInRemoteDescription() {
Harald Alvestrandbc9ca252020-10-05 13:08:415011 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson0dd75392022-01-17 18:19:565012 auto* remote_desc = remote_description();
Harald Alvestrandbc9ca252020-10-05 13:08:415013 if (!remote_desc) {
5014 return true;
5015 }
5016 bool ret = true;
5017
5018 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
5019 const IceCandidateCollection* candidates = remote_desc->candidates(m);
5020 for (size_t n = 0; n < candidates->count(); ++n) {
5021 const IceCandidateInterface* candidate = candidates->at(n);
5022 bool valid = false;
5023 if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) {
5024 if (valid) {
5025 RTC_LOG(LS_INFO)
Tomas Gunnarsson0dd75392022-01-17 18:19:565026 << "UseCandidatesInRemoteDescription: Not ready to use "
Harald Alvestrandbc9ca252020-10-05 13:08:415027 "candidate.";
5028 }
5029 continue;
5030 }
5031 ret = UseCandidate(candidate);
5032 if (!ret) {
5033 break;
5034 }
5035 }
5036 }
5037 return ret;
5038}
5039
5040bool SdpOfferAnswerHandler::UseCandidate(
5041 const IceCandidateInterface* candidate) {
5042 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson8cb97062021-02-08 17:57:045043
5044 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
5045
Harald Alvestrandbc9ca252020-10-05 13:08:415046 RTCErrorOr<const cricket::ContentInfo*> result =
5047 FindContentInfo(remote_description(), candidate);
Tomas Gunnarsson8cb97062021-02-08 17:57:045048 if (!result.ok())
Harald Alvestrandbc9ca252020-10-05 13:08:415049 return false;
Tomas Gunnarsson8cb97062021-02-08 17:57:045050
5051 const cricket::Candidate& c = candidate->candidate();
5052 RTCError error = cricket::VerifyCandidate(c);
Tomas Gunnarsson27bc6e22021-02-12 12:16:265053 if (!error.ok()) {
5054 RTC_LOG(LS_WARNING) << "Invalid candidate: " << c.ToString();
5055 return true;
5056 }
Tomas Gunnarsson8cb97062021-02-08 17:57:045057
5058 pc_->AddRemoteCandidate(result.value()->name, c);
5059
Harald Alvestrandbc9ca252020-10-05 13:08:415060 return true;
5061}
5062
5063// We need to check the local/remote description for the Transport instead of
5064// the session, because a new Transport added during renegotiation may have
5065// them unset while the session has them set from the previous negotiation.
5066// Not doing so may trigger the auto generation of transport description and
5067// mess up DTLS identity information, ICE credential, etc.
5068bool SdpOfferAnswerHandler::ReadyToUseRemoteCandidate(
5069 const IceCandidateInterface* candidate,
5070 const SessionDescriptionInterface* remote_desc,
5071 bool* valid) {
5072 RTC_DCHECK_RUN_ON(signaling_thread());
5073 *valid = true;
5074
5075 const SessionDescriptionInterface* current_remote_desc =
5076 remote_desc ? remote_desc : remote_description();
5077
5078 if (!current_remote_desc) {
5079 return false;
5080 }
5081
5082 RTCErrorOr<const cricket::ContentInfo*> result =
5083 FindContentInfo(current_remote_desc, candidate);
5084 if (!result.ok()) {
5085 RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. "
5086 << result.error().message();
5087
5088 *valid = false;
5089 return false;
5090 }
5091
Tomas Gunnarsson45de8b32021-04-02 10:33:395092 return true;
Harald Alvestrandbc9ca252020-10-05 13:08:415093}
5094
Harald Alvestrandbc9ca252020-10-05 13:08:415095RTCErrorOr<const cricket::ContentInfo*> SdpOfferAnswerHandler::FindContentInfo(
5096 const SessionDescriptionInterface* description,
5097 const IceCandidateInterface* candidate) {
Philipp Hancke31e06cb2021-02-26 08:23:535098 if (!candidate->sdp_mid().empty()) {
Harald Alvestrandbc9ca252020-10-05 13:08:415099 auto& contents = description->description()->contents();
5100 auto it = absl::c_find_if(
5101 contents, [candidate](const cricket::ContentInfo& content_info) {
5102 return content_info.mid() == candidate->sdp_mid();
5103 });
5104 if (it == contents.end()) {
Philipp Hanckeb81bf532023-07-18 09:03:395105 LOG_AND_RETURN_ERROR(
Harald Alvestrandbc9ca252020-10-05 13:08:415106 RTCErrorType::INVALID_PARAMETER,
5107 "Mid " + candidate->sdp_mid() +
5108 " specified but no media section with that mid found.");
5109 } else {
5110 return &*it;
5111 }
Philipp Hancke31e06cb2021-02-26 08:23:535112 } else if (candidate->sdp_mline_index() >= 0) {
5113 size_t mediacontent_index =
5114 static_cast<size_t>(candidate->sdp_mline_index());
5115 size_t content_size = description->description()->contents().size();
5116 if (mediacontent_index < content_size) {
5117 return &description->description()->contents()[mediacontent_index];
5118 } else {
Philipp Hanckeb81bf532023-07-18 09:03:395119 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
5120 "Media line index (" +
5121 rtc::ToString(candidate->sdp_mline_index()) +
5122 ") out of range (number of mlines: " +
5123 rtc::ToString(content_size) + ").");
Philipp Hancke31e06cb2021-02-26 08:23:535124 }
Harald Alvestrandbc9ca252020-10-05 13:08:415125 }
5126
Philipp Hanckeb81bf532023-07-18 09:03:395127 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
5128 "Neither sdp_mline_index nor sdp_mid specified.");
Harald Alvestrandbc9ca252020-10-05 13:08:415129}
5130
5131RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) {
Markus Handell518669d2021-06-07 11:30:465132 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateChannels");
Harald Alvestrandbc9ca252020-10-05 13:08:415133 // Creating the media channels. Transports should already have been created
5134 // at this point.
5135 RTC_DCHECK_RUN_ON(signaling_thread());
5136 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc);
5137 if (voice && !voice->rejected &&
Harald Alvestrande15fb152020-10-19 13:28:055138 !rtp_manager()->GetAudioTransceiver()->internal()->channel()) {
Harald Alvestrand8f429922022-05-04 10:32:305139 auto error =
5140 rtp_manager()->GetAudioTransceiver()->internal()->CreateChannel(
5141 voice->name, pc_->call_ptr(), pc_->configuration()->media_config,
5142 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
5143 video_options(), video_bitrate_allocator_factory_.get(),
5144 [&](absl::string_view mid) {
5145 RTC_DCHECK_RUN_ON(network_thread());
5146 return transport_controller_n()->GetRtpTransport(mid);
5147 });
5148 if (!error.ok()) {
5149 return error;
Harald Alvestrandbc9ca252020-10-05 13:08:415150 }
Harald Alvestrandbc9ca252020-10-05 13:08:415151 }
5152
5153 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc);
5154 if (video && !video->rejected &&
Harald Alvestrande15fb152020-10-19 13:28:055155 !rtp_manager()->GetVideoTransceiver()->internal()->channel()) {
Harald Alvestrand8f429922022-05-04 10:32:305156 auto error =
5157 rtp_manager()->GetVideoTransceiver()->internal()->CreateChannel(
5158 video->name, pc_->call_ptr(), pc_->configuration()->media_config,
5159 pc_->SrtpRequired(), pc_->GetCryptoOptions(),
5160
5161 audio_options(), video_options(),
5162 video_bitrate_allocator_factory_.get(), [&](absl::string_view mid) {
5163 RTC_DCHECK_RUN_ON(network_thread());
5164 return transport_controller_n()->GetRtpTransport(mid);
5165 });
5166 if (!error.ok()) {
5167 return error;
Harald Alvestrandbc9ca252020-10-05 13:08:415168 }
Harald Alvestrandbc9ca252020-10-05 13:08:415169 }
5170
5171 const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc);
Tommi840cf782023-10-21 14:47:565172 if (data && !data->rejected && !pc_->CreateDataChannelTransport(data->name)) {
Philipp Hanckeb81bf532023-07-18 09:03:395173 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
5174 "Failed to create data channel.");
Harald Alvestrandbc9ca252020-10-05 13:08:415175 }
5176
5177 return RTCError::OK();
5178}
5179
Tommi29190752023-10-21 14:50:095180void SdpOfferAnswerHandler::DestroyMediaChannels() {
Harald Alvestrandbc9ca252020-10-05 13:08:415181 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrande15fb152020-10-19 13:28:055182 if (!transceivers()) {
5183 return;
5184 }
Tommife041642021-04-07 08:08:285185
5186 RTC_LOG_THREAD_BLOCK_COUNT();
5187
Harald Alvestrandbc9ca252020-10-05 13:08:415188 // Destroy video channels first since they may have a pointer to a voice
5189 // channel.
Tommife041642021-04-07 08:08:285190 auto list = transceivers()->List();
5191 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
5192
5193 for (const auto& transceiver : list) {
Harald Alvestrandbc9ca252020-10-05 13:08:415194 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Harald Alvestrand19ebabc2022-04-28 13:31:175195 transceiver->internal()->ClearChannel();
Harald Alvestrandbc9ca252020-10-05 13:08:415196 }
5197 }
Tommife041642021-04-07 08:08:285198 for (const auto& transceiver : list) {
Harald Alvestrandbc9ca252020-10-05 13:08:415199 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Harald Alvestrand19ebabc2022-04-28 13:31:175200 transceiver->internal()->ClearChannel();
Harald Alvestrandbc9ca252020-10-05 13:08:415201 }
5202 }
Harald Alvestrandbc9ca252020-10-05 13:08:415203}
5204
5205void SdpOfferAnswerHandler::GenerateMediaDescriptionOptions(
5206 const SessionDescriptionInterface* session_desc,
5207 RtpTransceiverDirection audio_direction,
5208 RtpTransceiverDirection video_direction,
5209 absl::optional<size_t>* audio_index,
5210 absl::optional<size_t>* video_index,
5211 absl::optional<size_t>* data_index,
5212 cricket::MediaSessionOptions* session_options) {
5213 RTC_DCHECK_RUN_ON(signaling_thread());
5214 for (const cricket::ContentInfo& content :
5215 session_desc->description()->contents()) {
5216 if (IsAudioContent(&content)) {
5217 // If we already have an audio m= section, reject this extra one.
5218 if (*audio_index) {
5219 session_options->media_description_options.push_back(
5220 cricket::MediaDescriptionOptions(
5221 cricket::MEDIA_TYPE_AUDIO, content.name,
5222 RtpTransceiverDirection::kInactive, /*stopped=*/true));
5223 } else {
5224 bool stopped = (audio_direction == RtpTransceiverDirection::kInactive);
5225 session_options->media_description_options.push_back(
5226 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO,
5227 content.name, audio_direction,
5228 stopped));
5229 *audio_index = session_options->media_description_options.size() - 1;
5230 }
5231 session_options->media_description_options.back().header_extensions =
Harald Alvestrand35f4b4c2022-05-16 10:36:435232 media_engine()->voice().GetRtpHeaderExtensions();
Harald Alvestrandbc9ca252020-10-05 13:08:415233 } else if (IsVideoContent(&content)) {
5234 // If we already have an video m= section, reject this extra one.
5235 if (*video_index) {
5236 session_options->media_description_options.push_back(
5237 cricket::MediaDescriptionOptions(
5238 cricket::MEDIA_TYPE_VIDEO, content.name,
5239 RtpTransceiverDirection::kInactive, /*stopped=*/true));
5240 } else {
5241 bool stopped = (video_direction == RtpTransceiverDirection::kInactive);
5242 session_options->media_description_options.push_back(
5243 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO,
5244 content.name, video_direction,
5245 stopped));
5246 *video_index = session_options->media_description_options.size() - 1;
5247 }
5248 session_options->media_description_options.back().header_extensions =
Harald Alvestrand35f4b4c2022-05-16 10:36:435249 media_engine()->video().GetRtpHeaderExtensions();
Philipp Hancke4e8c1152020-10-13 10:43:155250 } else if (IsUnsupportedContent(&content)) {
5251 session_options->media_description_options.push_back(
5252 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_UNSUPPORTED,
5253 content.name,
5254 RtpTransceiverDirection::kInactive,
5255 /*stopped=*/true));
Harald Alvestrandbc9ca252020-10-05 13:08:415256 } else {
5257 RTC_DCHECK(IsDataContent(&content));
5258 // If we already have an data m= section, reject this extra one.
5259 if (*data_index) {
5260 session_options->media_description_options.push_back(
5261 GetMediaDescriptionOptionsForRejectedData(content.name));
5262 } else {
5263 session_options->media_description_options.push_back(
5264 GetMediaDescriptionOptionsForActiveData(content.name));
5265 *data_index = session_options->media_description_options.size() - 1;
5266 }
5267 }
5268 }
5269}
5270
5271cricket::MediaDescriptionOptions
5272SdpOfferAnswerHandler::GetMediaDescriptionOptionsForActiveData(
5273 const std::string& mid) const {
5274 RTC_DCHECK_RUN_ON(signaling_thread());
5275 // Direction for data sections is meaningless, but legacy endpoints might
5276 // expect sendrecv.
5277 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5278 RtpTransceiverDirection::kSendRecv,
5279 /*stopped=*/false);
Harald Alvestrandbc9ca252020-10-05 13:08:415280 return options;
5281}
5282
5283cricket::MediaDescriptionOptions
5284SdpOfferAnswerHandler::GetMediaDescriptionOptionsForRejectedData(
5285 const std::string& mid) const {
5286 RTC_DCHECK_RUN_ON(signaling_thread());
5287 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5288 RtpTransceiverDirection::kInactive,
5289 /*stopped=*/true);
Harald Alvestrandbc9ca252020-10-05 13:08:415290 return options;
5291}
5292
Taylor Brandstetterd0acbd82021-01-25 21:44:555293bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
Henrik Boströmf8187e02021-04-26 19:04:265294 cricket::ContentSource source,
5295 const std::map<std::string, const cricket::ContentGroup*>&
5296 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:465297 TRACE_EVENT0("webrtc",
5298 "SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState");
Harald Alvestrandbc9ca252020-10-05 13:08:415299 RTC_DCHECK_RUN_ON(signaling_thread());
5300 // We may need to delete any created default streams and disable creation of
5301 // new ones on the basis of payload type. This is needed to avoid SSRC
5302 // collisions in Call's RtpDemuxer, in the case that a transceiver has
5303 // created a default stream, and then some other channel gets the SSRC
Taylor Brandstetterd3ef4992020-10-16 01:22:575304 // signaled in the corresponding Unified Plan "m=" section. Specifically, we
5305 // need to disable payload type based demuxing when two bundled "m=" sections
5306 // are using the same payload type(s). For more context
Harald Alvestrandbc9ca252020-10-05 13:08:415307 // see https://bugs.chromium.org/p/webrtc/issues/detail?id=11477
5308 const SessionDescriptionInterface* sdesc =
5309 (source == cricket::CS_LOCAL ? local_description()
5310 : remote_description());
Henrik Boströmf8187e02021-04-26 19:04:265311 struct PayloadTypes {
5312 std::set<int> audio_payload_types;
5313 std::set<int> video_payload_types;
Henrik Boström4ea80f32021-06-09 08:29:505314 bool pt_demuxing_possible_audio = true;
5315 bool pt_demuxing_possible_video = true;
Henrik Boströmf8187e02021-04-26 19:04:265316 };
5317 std::map<const cricket::ContentGroup*, PayloadTypes> payload_types_by_bundle;
Henrik Boström4ea80f32021-06-09 08:29:505318 // If the MID is missing from *any* receiving m= section, this is set to true.
5319 bool mid_header_extension_missing_audio = false;
5320 bool mid_header_extension_missing_video = false;
Harald Alvestrandbc9ca252020-10-05 13:08:415321 for (auto& content_info : sdesc->description()->contents()) {
Henrik Boströmf8187e02021-04-26 19:04:265322 auto it = bundle_groups_by_mid.find(content_info.name);
5323 const cricket::ContentGroup* bundle_group =
5324 it != bundle_groups_by_mid.end() ? it->second : nullptr;
Taylor Brandstetterd3ef4992020-10-16 01:22:575325 // If this m= section isn't bundled, it's safe to demux by payload type
5326 // since other m= sections using the same payload type will also be using
5327 // different transports.
Henrik Boströmf8187e02021-04-26 19:04:265328 if (!bundle_group) {
Taylor Brandstetterd3ef4992020-10-16 01:22:575329 continue;
5330 }
Henrik Boströmf8187e02021-04-26 19:04:265331 PayloadTypes* payload_types = &payload_types_by_bundle[bundle_group];
Harald Alvestrandbc9ca252020-10-05 13:08:415332 if (content_info.rejected ||
5333 (source == cricket::ContentSource::CS_LOCAL &&
5334 !RtpTransceiverDirectionHasRecv(
5335 content_info.media_description()->direction())) ||
5336 (source == cricket::ContentSource::CS_REMOTE &&
5337 !RtpTransceiverDirectionHasSend(
5338 content_info.media_description()->direction()))) {
5339 // Ignore transceivers that are not receiving.
5340 continue;
5341 }
Philipp Hancked0f0f382023-11-23 19:21:055342 const cricket::MediaType media_type =
5343 content_info.media_description()->type();
5344 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO ||
5345 media_type == cricket::MediaType::MEDIA_TYPE_VIDEO) {
5346 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO &&
5347 !mid_header_extension_missing_audio) {
5348 mid_header_extension_missing_audio =
5349 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5350 } else if (media_type == cricket::MEDIA_TYPE_VIDEO &&
5351 !mid_header_extension_missing_video) {
5352 mid_header_extension_missing_video =
5353 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5354 }
5355 const cricket::MediaContentDescription* media_desc =
5356 content_info.media_description();
5357 for (const cricket::Codec& codec : media_desc->codecs()) {
5358 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
5359 if (payload_types->audio_payload_types.count(codec.id)) {
Taylor Brandstetterd3ef4992020-10-16 01:22:575360 // Two m= sections are using the same payload type, thus demuxing
5361 // by payload type is not possible.
Philipp Hancked0f0f382023-11-23 19:21:055362 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
5363 payload_types->pt_demuxing_possible_audio = false;
5364 }
Taylor Brandstetterd3ef4992020-10-16 01:22:575365 }
Philipp Hancked0f0f382023-11-23 19:21:055366 payload_types->audio_payload_types.insert(codec.id);
5367 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
5368 if (payload_types->video_payload_types.count(codec.id)) {
Taylor Brandstetterd3ef4992020-10-16 01:22:575369 // Two m= sections are using the same payload type, thus demuxing
5370 // by payload type is not possible.
Henrik Boström4ea80f32021-06-09 08:29:505371 payload_types->pt_demuxing_possible_video = false;
Taylor Brandstetterd3ef4992020-10-16 01:22:575372 }
Philipp Hancked0f0f382023-11-23 19:21:055373 payload_types->video_payload_types.insert(codec.id);
Taylor Brandstetterd3ef4992020-10-16 01:22:575374 }
Taylor Brandstetterd3ef4992020-10-16 01:22:575375 }
Harald Alvestrandbc9ca252020-10-05 13:08:415376 }
5377 }
Harald Alvestrandbc9ca252020-10-05 13:08:415378
Henrik Boström4ea80f32021-06-09 08:29:505379 // In Unified Plan, payload type demuxing is useful for legacy endpoints that
5380 // don't support the MID header extension, but it can also cause incorrrect
5381 // forwarding of packets when going from one m= section to multiple m=
5382 // sections in the same BUNDLE. This only happens if media arrives prior to
5383 // negotiation, but this can cause missing video and unsignalled ssrc bugs
5384 // severe enough to warrant disabling PT demuxing in such cases. Therefore, if
5385 // a MID header extension is present on all m= sections for a given kind
5386 // (audio/video) then we use that as an OK to disable payload type demuxing in
5387 // BUNDLEs of that kind. However if PT demuxing was ever turned on (e.g. MID
5388 // was ever removed on ANY m= section of that kind) then we continue to allow
5389 // PT demuxing in order to prevent disabling it in follow-up O/A exchanges and
5390 // allowing early media by PT.
5391 bool bundled_pt_demux_allowed_audio = !IsUnifiedPlan() ||
5392 mid_header_extension_missing_audio ||
5393 pt_demuxing_has_been_used_audio_;
5394 bool bundled_pt_demux_allowed_video = !IsUnifiedPlan() ||
5395 mid_header_extension_missing_video ||
5396 pt_demuxing_has_been_used_video_;
Henrik Boström4ea80f32021-06-09 08:29:505397
Tomas Gunnarsson92f9b742022-01-02 20:40:225398 // Gather all updates ahead of time so that all channels can be updated in a
Danil Chapovalov9e09a1f2022-09-08 16:38:105399 // single BlockingCall; necessary due to thread guards.
Tomas Gunnarsson92f9b742022-01-02 20:40:225400 std::vector<std::pair<bool, cricket::ChannelInterface*>> channels_to_update;
5401 for (const auto& transceiver : transceivers()->ListInternal()) {
5402 cricket::ChannelInterface* channel = transceiver->channel();
5403 const ContentInfo* content =
5404 FindMediaSectionForTransceiver(transceiver, sdesc);
5405 if (!channel || !content) {
5406 continue;
5407 }
5408
5409 const cricket::MediaType media_type = channel->media_type();
5410 if (media_type != cricket::MediaType::MEDIA_TYPE_AUDIO &&
5411 media_type != cricket::MediaType::MEDIA_TYPE_VIDEO) {
5412 continue;
5413 }
5414
5415 RtpTransceiverDirection local_direction =
5416 content->media_description()->direction();
5417 if (source == cricket::CS_REMOTE) {
5418 local_direction = RtpTransceiverDirectionReversed(local_direction);
5419 }
5420
Tomas Gunnarsson5411b172022-01-24 07:45:265421 auto bundle_it = bundle_groups_by_mid.find(channel->mid());
Tomas Gunnarsson92f9b742022-01-02 20:40:225422 const cricket::ContentGroup* bundle_group =
5423 bundle_it != bundle_groups_by_mid.end() ? bundle_it->second : nullptr;
5424 bool pt_demux_enabled = RtpTransceiverDirectionHasRecv(local_direction);
5425 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
5426 pt_demux_enabled &=
5427 !bundle_group ||
5428 (bundled_pt_demux_allowed_audio &&
5429 payload_types_by_bundle[bundle_group].pt_demuxing_possible_audio);
5430 if (pt_demux_enabled) {
5431 pt_demuxing_has_been_used_audio_ = true;
5432 }
5433 } else {
5434 RTC_DCHECK_EQ(media_type, cricket::MediaType::MEDIA_TYPE_VIDEO);
5435 pt_demux_enabled &=
5436 !bundle_group ||
5437 (bundled_pt_demux_allowed_video &&
5438 payload_types_by_bundle[bundle_group].pt_demuxing_possible_video);
5439 if (pt_demux_enabled) {
5440 pt_demuxing_has_been_used_video_ = true;
5441 }
5442 }
5443
5444 channels_to_update.emplace_back(pt_demux_enabled, transceiver->channel());
5445 }
5446
5447 if (channels_to_update.empty()) {
5448 return true;
5449 }
5450
Danil Chapovalov9e09a1f2022-09-08 16:38:105451 // TODO(bugs.webrtc.org/11993): This BlockingCall() will also block on the
5452 // network thread for every demuxer sink that needs to be updated. The demuxer
5453 // state needs to be fully (and only) managed on the network thread and once
5454 // that's the case, there's no need to stop by on the worker. Ideally we could
5455 // also do this without blocking.
5456 return context_->worker_thread()->BlockingCall([&channels_to_update]() {
5457 for (const auto& it : channels_to_update) {
5458 if (!it.second->SetPayloadTypeDemuxingEnabled(it.first)) {
5459 // Note that the state has already been irrevocably changed at this
5460 // point. Is it useful to stop the loop?
5461 return false;
5462 }
5463 }
5464 return true;
5465 });
Harald Alvestrandbc9ca252020-10-05 13:08:415466}
5467
Harald Alvestrand8101e7b2022-05-23 14:57:475468bool SdpOfferAnswerHandler::ConfiguredForMedia() const {
5469 return context_->media_engine();
5470}
5471
Harald Alvestrandcdcfab02020-09-28 13:02:075472} // namespace webrtc