blob: 35e65e01057433684ebe467d418785de6b163748 [file] [log] [blame]
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001/*
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:33 +000013#include <algorithm>
Harald Alvestrand981c5722022-02-09 12:42:27 +000014#include <cstddef>
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000015#include <iterator>
Harald Alvestrand3eaee6b2020-10-19 06:35:55 +000016#include <map>
Mirko Bonadei9ff450d2021-08-02 10:56:33 +020017#include <memory>
Harald Alvestrandc06e3742020-10-01 10:23:33 +000018#include <queue>
Harald Alvestrand981c5722022-02-09 12:42:27 +000019#include <string>
Harald Alvestrandf01bd6c2020-10-23 13:30:46 +000020#include <utility>
Harald Alvestrandc06e3742020-10-01 10:23:33 +000021
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000022#include "absl/algorithm/container.h"
Harald Alvestrand9cd199d2020-10-27 07:10:43 +000023#include "absl/memory/memory.h"
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +010024#include "absl/strings/match.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000025#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:47 +000028#include "api/dtls_transport_interface.h"
Harald Alvestrand8f429922022-05-04 10:32:30 +000029#include "api/field_trials_view.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000030#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:40 +000033#include "api/video/builtin_video_bitrate_allocator_factory.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000034#include "media/base/codec.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000035#include "media/base/rid_description.h"
Harald Alvestrand85466662021-04-19 21:21:36 +000036#include "p2p/base/ice_transport_internal.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000037#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:27 +000043#include "pc/channel_interface.h"
Harald Alvestrand85466662021-04-19 21:21:36 +000044#include "pc/dtls_transport.h"
Henrik Boströmf7859892022-07-04 14:36:37 +020045#include "pc/legacy_stats_collector.h"
Harald Alvestrandcdcfab02020-09-28 13:02:07 +000046#include "pc/media_stream.h"
Markus Handella1b82012021-05-26 18:56:30 +020047#include "pc/media_stream_proxy.h"
Harald Alvestrand8e344192022-02-08 12:55:42 +000048#include "pc/peer_connection_internal.h"
Harald Alvestrand1090e442020-10-05 07:01:09 +000049#include "pc/peer_connection_message_handler.h"
Harald Alvestrandcdcfab02020-09-28 13:02:07 +000050#include "pc/rtp_media_utils.h"
Harald Alvestrand981c5722022-02-09 12:42:27 +000051#include "pc/rtp_receiver_proxy.h"
Harald Alvestrand3eaee6b2020-10-19 06:35:55 +000052#include "pc/rtp_sender.h"
Harald Alvestrand981c5722022-02-09 12:42:27 +000053#include "pc/rtp_sender_proxy.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000054#include "pc/simulcast_description.h"
55#include "pc/usage_pattern.h"
Harald Alvestrand763f5a92020-10-22 10:39:40 +000056#include "pc/webrtc_session_description_factory.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000057#include "rtc_base/helpers.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000058#include "rtc_base/logging.h"
Harald Alvestrand763f5a92020-10-22 10:39:40 +000059#include "rtc_base/rtc_certificate.h"
Harald Alvestrand1f7eab62020-10-18 16:51:47 +000060#include "rtc_base/ssl_stream_adapter.h"
61#include "rtc_base/string_encode.h"
62#include "rtc_base/strings/string_builder.h"
Harald Alvestrandcdcfab02020-09-28 13:02:07 +000063#include "rtc_base/trace_event.h"
64#include "system_wrappers/include/metrics.h"
65
66using cricket::ContentInfo;
67using cricket::ContentInfos;
68using cricket::MediaContentDescription;
69using cricket::MediaProtocolType;
70using cricket::RidDescription;
71using cricket::RidDirection;
72using cricket::SessionDescription;
73using cricket::SimulcastDescription;
74using cricket::SimulcastLayer;
75using cricket::SimulcastLayerList;
76using cricket::StreamParams;
77using cricket::TransportInfo;
78
79using cricket::LOCAL_PORT_TYPE;
80using cricket::PRFLX_PORT_TYPE;
81using cricket::RELAY_PORT_TYPE;
82using cricket::STUN_PORT_TYPE;
83
84namespace webrtc {
85
86namespace {
87
Harald Alvestrandc06e3742020-10-01 10:23:33 +000088typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions
89 RTCOfferAnswerOptions;
90
Harald Alvestrandcdcfab02020-09-28 13:02:07 +000091// Error messages
92const char kInvalidSdp[] = "Invalid session description.";
93const char kInvalidCandidates[] = "Description contains invalid candidates.";
94const char kBundleWithoutRtcpMux[] =
95 "rtcp-mux must be enabled when BUNDLE "
96 "is enabled.";
97const char kMlineMismatchInAnswer[] =
98 "The order of m-lines in answer doesn't match order in offer. Rejecting "
99 "answer.";
100const char kMlineMismatchInSubsequentOffer[] =
101 "The order of m-lines in subsequent offer doesn't match order from "
102 "previous offer/answer.";
103const char kSdpWithoutIceUfragPwd[] =
104 "Called with SDP without ice-ufrag and ice-pwd.";
105const char kSdpWithoutDtlsFingerprint[] =
106 "Called with SDP without DTLS fingerprint.";
Harald Alvestrand0d018412021-11-04 13:52:31 +0000107const char kSdpWithoutSdesCrypto[] = "Called with SDP without SDES crypto.";
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000108
Harald Alvestranda474fbf2020-10-01 16:47:23 +0000109const char kSessionError[] = "Session error code: ";
110const char kSessionErrorDesc[] = "Session error description: ";
111
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000112// UMA metric names.
113const char kSimulcastVersionApplyLocalDescription[] =
114 "WebRTC.PeerConnection.Simulcast.ApplyLocalDescription";
115const char kSimulcastVersionApplyRemoteDescription[] =
116 "WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription";
117const char kSimulcastDisabled[] = "WebRTC.PeerConnection.Simulcast.Disabled";
118
Harald Alvestrandbc9ca252020-10-05 13:08:41 +0000119// The length of RTCP CNAMEs.
120static const int kRtcpCnameLength = 16;
121
Philipp Hancke187e9d42021-11-08 09:58:15 +0100122// The maximum length of the MID attribute.
Philipp Hancke063bb382021-11-29 12:05:38 +0100123// TODO(bugs.webrtc.org/12517) - reduce to 16 again.
124static constexpr size_t kMidMaxSize = 32;
Philipp Hancke187e9d42021-11-08 09:58:15 +0100125
Harald Alvestranda474fbf2020-10-01 16:47:23 +0000126const char kDefaultStreamId[] = "default";
127// NOTE: Duplicated in peer_connection.cc:
128static const char kDefaultAudioSenderId[] = "defaulta0";
129static const char kDefaultVideoSenderId[] = "defaultv0";
130
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000131void NoteAddIceCandidateResult(int result) {
132 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result,
133 kAddIceCandidateMax);
134}
135
Harald Alvestrand70fe7042022-01-13 10:08:37 +0000136void NoteKeyProtocol(KeyExchangeProtocolType protocol_type) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000137 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocol", protocol_type,
138 kEnumCounterKeyProtocolMax);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000139}
140
Henrik Boströmf8187e02021-04-26 21:04:26 +0200141std::map<std::string, const cricket::ContentGroup*> GetBundleGroupsByMid(
142 const SessionDescription* desc) {
143 std::vector<const cricket::ContentGroup*> bundle_groups =
144 desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
145 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid;
146 for (const cricket::ContentGroup* bundle_group : bundle_groups) {
147 for (const std::string& content_name : bundle_group->content_names()) {
148 bundle_groups_by_mid[content_name] = bundle_group;
149 }
150 }
151 return bundle_groups_by_mid;
152}
153
Artem Titov880fa812021-07-30 22:30:23 +0200154// Returns true if `new_desc` requests an ICE restart (i.e., new ufrag/pwd).
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000155bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc,
156 const SessionDescriptionInterface* new_desc,
157 const std::string& content_name) {
158 if (!old_desc) {
159 return false;
160 }
161 const SessionDescription* new_sd = new_desc->description();
162 const SessionDescription* old_sd = old_desc->description();
163 const ContentInfo* cinfo = new_sd->GetContentByName(content_name);
164 if (!cinfo || cinfo->rejected) {
165 return false;
166 }
167 // If the content isn't rejected, check if ufrag and password has changed.
168 const cricket::TransportDescription* new_transport_desc =
169 new_sd->GetTransportDescriptionByName(content_name);
170 const cricket::TransportDescription* old_transport_desc =
171 old_sd->GetTransportDescriptionByName(content_name);
172 if (!new_transport_desc || !old_transport_desc) {
173 // No transport description exists. This is not an ICE restart.
174 return false;
175 }
176 if (cricket::IceCredentialsChanged(
177 old_transport_desc->ice_ufrag, old_transport_desc->ice_pwd,
178 new_transport_desc->ice_ufrag, new_transport_desc->ice_pwd)) {
179 RTC_LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name
180 << ".";
181 return true;
182 }
183 return false;
184}
185
186// Generates a string error message for SetLocalDescription/SetRemoteDescription
187// from an RTCError.
188std::string GetSetDescriptionErrorMessage(cricket::ContentSource source,
189 SdpType type,
190 const RTCError& error) {
191 rtc::StringBuilder oss;
192 oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote")
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100193 << " " << SdpTypeToString(type) << " sdp: ";
194 RTC_DCHECK(!absl::StartsWith(error.message(), oss.str())) << error.message();
195 oss << error.message();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000196 return oss.Release();
197}
198
199std::string GetStreamIdsString(rtc::ArrayView<const std::string> stream_ids) {
200 std::string output = "streams=[";
201 const char* separator = "";
202 for (const auto& stream_id : stream_ids) {
203 output.append(separator).append(stream_id);
204 separator = ", ";
205 }
206 output.append("]");
207 return output;
208}
209
210void ReportSimulcastApiVersion(const char* name,
211 const SessionDescription& session) {
212 bool has_legacy = false;
213 bool has_spec_compliant = false;
214 for (const ContentInfo& content : session.contents()) {
215 if (!content.media_description()) {
216 continue;
217 }
218 has_spec_compliant |= content.media_description()->HasSimulcast();
219 for (const StreamParams& sp : content.media_description()->streams()) {
220 has_legacy |= sp.has_ssrc_group(cricket::kSimSsrcGroupSemantics);
221 }
222 }
223
224 if (has_legacy) {
225 RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionLegacy,
226 kSimulcastApiVersionMax);
227 }
228 if (has_spec_compliant) {
229 RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionSpecCompliant,
230 kSimulcastApiVersionMax);
231 }
232 if (!has_legacy && !has_spec_compliant) {
233 RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionNone,
234 kSimulcastApiVersionMax);
235 }
236}
237
238const ContentInfo* FindTransceiverMSection(
Harald Alvestrand85466662021-04-19 21:21:36 +0000239 RtpTransceiver* transceiver,
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000240 const SessionDescriptionInterface* session_description) {
241 return transceiver->mid()
242 ? session_description->description()->GetContentByName(
243 *transceiver->mid())
244 : nullptr;
245}
246
247// If the direction is "recvonly" or "inactive", treat the description
248// as containing no streams.
249// See: https://code.google.com/p/webrtc/issues/detail?id=5054
250std::vector<cricket::StreamParams> GetActiveStreams(
251 const cricket::MediaContentDescription* desc) {
252 return RtpTransceiverDirectionHasSend(desc->direction())
253 ? desc->streams()
254 : std::vector<cricket::StreamParams>();
255}
256
257// Logic to decide if an m= section can be recycled. This means that the new
258// m= section is not rejected, but the old local or remote m= section is
Artem Titov880fa812021-07-30 22:30:23 +0200259// rejected. `old_content_one` and `old_content_two` refer to the m= section
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000260// of the old remote and old local descriptions in no particular order.
261// We need to check both the old local and remote because either
262// could be the most current from the latest negotation.
263bool IsMediaSectionBeingRecycled(SdpType type,
264 const ContentInfo& content,
265 const ContentInfo* old_content_one,
266 const ContentInfo* old_content_two) {
267 return type == SdpType::kOffer && !content.rejected &&
268 ((old_content_one && old_content_one->rejected) ||
269 (old_content_two && old_content_two->rejected));
270}
271
Artem Titov880fa812021-07-30 22:30:23 +0200272// Verify that the order of media sections in `new_desc` matches
273// `current_desc`. The number of m= sections in `new_desc` should be no
274// less than `current_desc`. In the case of checking an answer's
275// `new_desc`, the `current_desc` is the last offer that was set as the
276// local or remote. In the case of checking an offer's `new_desc` we
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000277// check against the local and remote descriptions stored from the last
278// negotiation, because either of these could be the most up to date for
Artem Titov880fa812021-07-30 22:30:23 +0200279// possible rejected m sections. These are the `current_desc` and
280// `secondary_current_desc`.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000281bool MediaSectionsInSameOrder(const SessionDescription& current_desc,
282 const SessionDescription* secondary_current_desc,
283 const SessionDescription& new_desc,
284 const SdpType type) {
285 if (current_desc.contents().size() > new_desc.contents().size()) {
286 return false;
287 }
288
289 for (size_t i = 0; i < current_desc.contents().size(); ++i) {
290 const cricket::ContentInfo* secondary_content_info = nullptr;
291 if (secondary_current_desc &&
292 i < secondary_current_desc->contents().size()) {
293 secondary_content_info = &secondary_current_desc->contents()[i];
294 }
295 if (IsMediaSectionBeingRecycled(type, new_desc.contents()[i],
296 &current_desc.contents()[i],
297 secondary_content_info)) {
298 // For new offer descriptions, if the media section can be recycled, it's
299 // valid for the MID and media type to change.
300 continue;
301 }
302 if (new_desc.contents()[i].name != current_desc.contents()[i].name) {
303 return false;
304 }
305 const MediaContentDescription* new_desc_mdesc =
306 new_desc.contents()[i].media_description();
307 const MediaContentDescription* current_desc_mdesc =
308 current_desc.contents()[i].media_description();
309 if (new_desc_mdesc->type() != current_desc_mdesc->type()) {
310 return false;
311 }
312 }
313 return true;
314}
315
316bool MediaSectionsHaveSameCount(const SessionDescription& desc1,
317 const SessionDescription& desc2) {
318 return desc1.contents().size() == desc2.contents().size();
319}
Harald Alvestrand0d018412021-11-04 13:52:31 +0000320// Checks that each non-rejected content has SDES crypto keys or a DTLS
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000321// fingerprint, unless it's in a BUNDLE group, in which case only the
322// BUNDLE-tag section (first media section/description in the BUNDLE group)
323// needs a ufrag and pwd. Mismatches, such as replying with a DTLS fingerprint
324// to SDES keys, will be caught in JsepTransport negotiation, and backstopped
Artem Titov880fa812021-07-30 22:30:23 +0200325// by Channel's `srtp_required` check.
Henrik Boströmf8187e02021-04-26 21:04:26 +0200326RTCError VerifyCrypto(const SessionDescription* desc,
Harald Alvestrand0d018412021-11-04 13:52:31 +0000327 bool dtls_enabled,
Henrik Boströmf8187e02021-04-26 21:04:26 +0200328 const std::map<std::string, const cricket::ContentGroup*>&
329 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000330 for (const cricket::ContentInfo& content_info : desc->contents()) {
331 if (content_info.rejected) {
332 continue;
333 }
334 // Note what media is used with each crypto protocol, for all sections.
Harald Alvestrand70fe7042022-01-13 10:08:37 +0000335 NoteKeyProtocol(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls
336 : webrtc::kEnumCounterKeyProtocolSdes);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000337 const std::string& mid = content_info.name;
Henrik Boströmf8187e02021-04-26 21:04:26 +0200338 auto it = bundle_groups_by_mid.find(mid);
339 const cricket::ContentGroup* bundle =
340 it != bundle_groups_by_mid.end() ? it->second : nullptr;
341 if (bundle && mid != *(bundle->FirstContentName())) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000342 // This isn't the first media section in the BUNDLE group, so it's not
343 // required to have crypto attributes, since only the crypto attributes
344 // from the first section actually get used.
345 continue;
346 }
347
348 // If the content isn't rejected or bundled into another m= section, crypto
349 // must be present.
350 const MediaContentDescription* media = content_info.media_description();
351 const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
352 if (!media || !tinfo) {
353 // Something is not right.
354 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
355 }
Harald Alvestrand0d018412021-11-04 13:52:31 +0000356 if (dtls_enabled) {
357 if (!tinfo->description.identity_fingerprint) {
358 RTC_LOG(LS_WARNING)
359 << "Session description must have DTLS fingerprint if "
360 "DTLS enabled.";
361 return RTCError(RTCErrorType::INVALID_PARAMETER,
362 kSdpWithoutDtlsFingerprint);
363 }
364 } else {
365 if (media->cryptos().empty()) {
366 RTC_LOG(LS_WARNING)
367 << "Session description must have SDES when DTLS disabled.";
368 return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto);
369 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000370 }
371 }
372 return RTCError::OK();
373}
374
375// Checks that each non-rejected content has ice-ufrag and ice-pwd set, unless
376// it's in a BUNDLE group, in which case only the BUNDLE-tag section (first
377// media section/description in the BUNDLE group) needs a ufrag and pwd.
Henrik Boströmf8187e02021-04-26 21:04:26 +0200378bool VerifyIceUfragPwdPresent(
379 const SessionDescription* desc,
380 const std::map<std::string, const cricket::ContentGroup*>&
381 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000382 for (const cricket::ContentInfo& content_info : desc->contents()) {
383 if (content_info.rejected) {
384 continue;
385 }
386 const std::string& mid = content_info.name;
Henrik Boströmf8187e02021-04-26 21:04:26 +0200387 auto it = bundle_groups_by_mid.find(mid);
388 const cricket::ContentGroup* bundle =
389 it != bundle_groups_by_mid.end() ? it->second : nullptr;
390 if (bundle && mid != *(bundle->FirstContentName())) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000391 // This isn't the first media section in the BUNDLE group, so it's not
392 // required to have ufrag/password, since only the ufrag/password from
393 // the first section actually get used.
394 continue;
395 }
396
397 // If the content isn't rejected or bundled into another m= section,
398 // ice-ufrag and ice-pwd must be present.
399 const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
400 if (!tinfo) {
401 // Something is not right.
402 RTC_LOG(LS_ERROR) << kInvalidSdp;
403 return false;
404 }
405 if (tinfo->description.ice_ufrag.empty() ||
406 tinfo->description.ice_pwd.empty()) {
407 RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
408 return false;
409 }
410 }
411 return true;
412}
413
Harald Alvestrand85466662021-04-19 21:21:36 +0000414RTCError ValidateMids(const cricket::SessionDescription& description) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000415 std::set<std::string> mids;
Philipp Hancke063bb382021-11-29 12:05:38 +0100416 size_t max_length = 0;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000417 for (const cricket::ContentInfo& content : description.contents()) {
418 if (content.name.empty()) {
419 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
420 "A media section is missing a MID attribute.");
421 }
Philipp Hancke063bb382021-11-29 12:05:38 +0100422 max_length = std::max(max_length, content.name.size());
Philipp Hancke187e9d42021-11-08 09:58:15 +0100423 if (content.name.size() > kMidMaxSize) {
424 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
425 "The MID attribute exceeds the maximum supported "
Philipp Hancke063bb382021-11-29 12:05:38 +0100426 "length of 32 characters.");
Philipp Hancke187e9d42021-11-08 09:58:15 +0100427 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000428 if (!mids.insert(content.name).second) {
429 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
430 "Duplicate a=mid value '" + content.name + "'.");
431 }
432 }
Philipp Hancke063bb382021-11-29 12:05:38 +0100433 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.PeerConnection.Mid.Size", max_length, 0,
434 31, 32);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000435 return RTCError::OK();
436}
437
438bool IsValidOfferToReceiveMedia(int value) {
439 typedef PeerConnectionInterface::RTCOfferAnswerOptions Options;
440 return (value >= Options::kUndefined) &&
441 (value <= Options::kMaxOfferToReceiveMedia);
442}
443
444bool ValidateOfferAnswerOptions(
445 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) {
446 return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) &&
447 IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video);
448}
449
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000450// This method will extract any send encodings that were sent by the remote
451// connection. This is currently only relevant for Simulcast scenario (where
452// the number of layers may be communicated by the server).
Harald Alvestrand85466662021-04-19 21:21:36 +0000453std::vector<RtpEncodingParameters> GetSendEncodingsFromRemoteDescription(
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000454 const MediaContentDescription& desc) {
455 if (!desc.HasSimulcast()) {
456 return {};
457 }
458 std::vector<RtpEncodingParameters> result;
459 const SimulcastDescription& simulcast = desc.simulcast_description();
460
461 // This is a remote description, the parameters we are after should appear
462 // as receive streams.
463 for (const auto& alternatives : simulcast.receive_layers()) {
464 RTC_DCHECK(!alternatives.empty());
465 // There is currently no way to specify or choose from alternatives.
466 // We will always use the first alternative, which is the most preferred.
467 const SimulcastLayer& layer = alternatives[0];
468 RtpEncodingParameters parameters;
469 parameters.rid = layer.rid;
470 parameters.active = !layer.is_paused;
471 result.push_back(parameters);
472 }
473
474 return result;
475}
476
Harald Alvestrand85466662021-04-19 21:21:36 +0000477RTCError UpdateSimulcastLayerStatusInSender(
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000478 const std::vector<SimulcastLayer>& layers,
479 rtc::scoped_refptr<RtpSenderInternal> sender) {
480 RTC_DCHECK(sender);
Harald Alvestrand0166be82022-08-25 11:31:01 +0000481 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000482 std::vector<std::string> disabled_layers;
483
484 // The simulcast envelope cannot be changed, only the status of the streams.
485 // So we will iterate over the send encodings rather than the layers.
486 for (RtpEncodingParameters& encoding : parameters.encodings) {
487 auto iter = std::find_if(layers.begin(), layers.end(),
488 [&encoding](const SimulcastLayer& layer) {
489 return layer.rid == encoding.rid;
490 });
491 // A layer that cannot be found may have been removed by the remote party.
492 if (iter == layers.end()) {
493 disabled_layers.push_back(encoding.rid);
494 continue;
495 }
496
497 encoding.active = !iter->is_paused;
498 }
499
Harald Alvestrand0166be82022-08-25 11:31:01 +0000500 RTCError result = sender->SetParametersInternalWithAllLayers(parameters);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000501 if (result.ok()) {
502 result = sender->DisableEncodingLayers(disabled_layers);
503 }
504
505 return result;
506}
507
Harald Alvestrand85466662021-04-19 21:21:36 +0000508bool SimulcastIsRejected(const ContentInfo* local_content,
Lennart Grahl0d0ed762021-05-17 16:06:37 +0200509 const MediaContentDescription& answer_media_desc,
510 bool enable_encrypted_rtp_header_extensions) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000511 bool simulcast_offered = local_content &&
512 local_content->media_description() &&
513 local_content->media_description()->HasSimulcast();
514 bool simulcast_answered = answer_media_desc.HasSimulcast();
515 bool rids_supported = RtpExtension::FindHeaderExtensionByUri(
Lennart Grahl0d0ed762021-05-17 16:06:37 +0200516 answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri,
517 enable_encrypted_rtp_header_extensions
518 ? RtpExtension::Filter::kPreferEncryptedExtension
519 : RtpExtension::Filter::kDiscardEncryptedExtension);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000520 return simulcast_offered && (!simulcast_answered || !rids_supported);
521}
522
Harald Alvestrand85466662021-04-19 21:21:36 +0000523RTCError DisableSimulcastInSender(
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000524 rtc::scoped_refptr<RtpSenderInternal> sender) {
525 RTC_DCHECK(sender);
Harald Alvestrand0166be82022-08-25 11:31:01 +0000526 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000527 if (parameters.encodings.size() <= 1) {
528 return RTCError::OK();
529 }
530
531 std::vector<std::string> disabled_layers;
532 std::transform(
533 parameters.encodings.begin() + 1, parameters.encodings.end(),
534 std::back_inserter(disabled_layers),
535 [](const RtpEncodingParameters& encoding) { return encoding.rid; });
536 return sender->DisableEncodingLayers(disabled_layers);
537}
538
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000539// The SDP parser used to populate these values by default for the 'content
540// name' if an a=mid line was absent.
Harald Alvestrand85466662021-04-19 21:21:36 +0000541absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000542 switch (media_type) {
543 case cricket::MEDIA_TYPE_AUDIO:
544 return cricket::CN_AUDIO;
545 case cricket::MEDIA_TYPE_VIDEO:
546 return cricket::CN_VIDEO;
547 case cricket::MEDIA_TYPE_DATA:
548 return cricket::CN_DATA;
Philipp Hancke4e8c1152020-10-13 12:43:15 +0200549 case cricket::MEDIA_TYPE_UNSUPPORTED:
550 return "not supported";
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000551 }
Artem Titovd3251962021-11-15 16:57:07 +0100552 RTC_DCHECK_NOTREACHED();
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000553 return "";
554}
555
Artem Titov880fa812021-07-30 22:30:23 +0200556// Add options to |[audio/video]_media_description_options| from `senders`.
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000557void AddPlanBRtpSenderOptions(
558 const std::vector<rtc::scoped_refptr<
559 RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
560 cricket::MediaDescriptionOptions* audio_media_description_options,
561 cricket::MediaDescriptionOptions* video_media_description_options,
562 int num_sim_layers) {
563 for (const auto& sender : senders) {
564 if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
565 if (audio_media_description_options) {
566 audio_media_description_options->AddAudioSender(
567 sender->id(), sender->internal()->stream_ids());
568 }
569 } else {
570 RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO);
571 if (video_media_description_options) {
572 video_media_description_options->AddVideoSender(
573 sender->id(), sender->internal()->stream_ids(), {},
574 SimulcastLayerList(), num_sim_layers);
575 }
576 }
577 }
578}
579
Harald Alvestrand85466662021-04-19 21:21:36 +0000580cricket::MediaDescriptionOptions GetMediaDescriptionOptionsForTransceiver(
581 RtpTransceiver* transceiver,
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000582 const std::string& mid,
583 bool is_create_offer) {
584 // NOTE: a stopping transceiver should be treated as a stopped one in
585 // createOffer as specified in
586 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
587 bool stopped =
588 is_create_offer ? transceiver->stopping() : transceiver->stopped();
589 cricket::MediaDescriptionOptions media_description_options(
590 transceiver->media_type(), mid, transceiver->direction(), stopped);
591 media_description_options.codec_preferences =
592 transceiver->codec_preferences();
593 media_description_options.header_extensions =
594 transceiver->HeaderExtensionsToOffer();
595 // This behavior is specified in JSEP. The gist is that:
596 // 1. The MSID is included if the RtpTransceiver's direction is sendonly or
597 // sendrecv.
598 // 2. If the MSID is included, then it must be included in any subsequent
599 // offer/answer exactly the same until the RtpTransceiver is stopped.
600 if (stopped || (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
Harald Alvestrand85466662021-04-19 21:21:36 +0000601 !transceiver->has_ever_been_used_to_send())) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000602 return media_description_options;
603 }
604
605 cricket::SenderOptions sender_options;
606 sender_options.track_id = transceiver->sender()->id();
607 sender_options.stream_ids = transceiver->sender()->stream_ids();
608
609 // The following sets up RIDs and Simulcast.
610 // RIDs are included if Simulcast is requested or if any RID was specified.
611 RtpParameters send_parameters =
Harald Alvestrand0166be82022-08-25 11:31:01 +0000612 transceiver->sender_internal()->GetParametersInternalWithAllLayers();
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000613 bool has_rids = std::any_of(send_parameters.encodings.begin(),
614 send_parameters.encodings.end(),
615 [](const RtpEncodingParameters& encoding) {
616 return !encoding.rid.empty();
617 });
618
619 std::vector<RidDescription> send_rids;
620 SimulcastLayerList send_layers;
621 for (const RtpEncodingParameters& encoding : send_parameters.encodings) {
622 if (encoding.rid.empty()) {
623 continue;
624 }
625 send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend));
626 send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
627 }
628
629 if (has_rids) {
630 sender_options.rids = send_rids;
631 }
632
633 sender_options.simulcast_layers = send_layers;
634 // When RIDs are configured, we must set num_sim_layers to 0 to.
635 // Otherwise, num_sim_layers must be 1 because either there is no
636 // simulcast, or simulcast is acheived by munging the SDP.
637 sender_options.num_sim_layers = has_rids ? 0 : 1;
638 media_description_options.sender_options.push_back(sender_options);
639
640 return media_description_options;
641}
642
Artem Titov880fa812021-07-30 22:30:23 +0200643// Returns the ContentInfo at mline index `i`, or null if none exists.
Harald Alvestrand85466662021-04-19 21:21:36 +0000644const ContentInfo* GetContentByIndex(const SessionDescriptionInterface* sdesc,
645 size_t i) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000646 if (!sdesc) {
647 return nullptr;
648 }
649 const ContentInfos& contents = sdesc->description()->contents();
650 return (i < contents.size() ? &contents[i] : nullptr);
651}
652
Artem Titov880fa812021-07-30 22:30:23 +0200653// From `rtc_options`, fill parts of `session_options` shared by all generated
Harald Alvestrandc06e3742020-10-01 10:23:33 +0000654// m= sectionss (in other words, nothing that involves a map/array).
655void ExtractSharedMediaSessionOptions(
656 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
657 cricket::MediaSessionOptions* session_options) {
658 session_options->vad_enabled = rtc_options.voice_activity_detection;
659 session_options->bundle_enabled = rtc_options.use_rtp_mux;
660 session_options->raw_packetization_for_video =
661 rtc_options.raw_packetization_for_video;
662}
663
Harald Alvestrandbc9ca252020-10-05 13:08:41 +0000664// Generate a RTCP CNAME when a PeerConnection is created.
665std::string GenerateRtcpCname() {
666 std::string cname;
667 if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) {
668 RTC_LOG(LS_ERROR) << "Failed to generate CNAME.";
Artem Titovd3251962021-11-15 16:57:07 +0100669 RTC_DCHECK_NOTREACHED();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +0000670 }
671 return cname;
672}
673
Artem Titov880fa812021-07-30 22:30:23 +0200674// Check if we can send `new_stream` on a PeerConnection.
Harald Alvestrand6f04b652020-10-09 11:42:17 +0000675bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams,
676 webrtc::MediaStreamInterface* new_stream) {
677 if (!new_stream || !current_streams) {
678 return false;
679 }
680 if (current_streams->find(new_stream->id()) != nullptr) {
681 RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id()
682 << " is already added.";
683 return false;
684 }
685 return true;
686}
687
Tomas Gunnarsson92eebef2021-02-10 13:05:44 +0100688rtc::scoped_refptr<webrtc::DtlsTransport> LookupDtlsTransportByMid(
689 rtc::Thread* network_thread,
690 JsepTransportController* controller,
691 const std::string& mid) {
692 // TODO(tommi): Can we post this (and associated operations where this
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +0200693 // function is called) to the network thread and avoid this BlockingCall?
Tomas Gunnarsson92eebef2021-02-10 13:05:44 +0100694 // We might be able to simplify a few things if we set the transport on
695 // the network thread and then update the implementation to check that
696 // the set_ and relevant get methods are always called on the network
697 // thread (we'll need to update proxy maps).
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +0200698 return network_thread->BlockingCall(
Tomas Gunnarsson92eebef2021-02-10 13:05:44 +0100699 [controller, &mid] { return controller->LookupDtlsTransportByMid(mid); });
700}
701
Henrik Boström4ea80f32021-06-09 10:29:50 +0200702bool ContentHasHeaderExtension(const cricket::ContentInfo& content_info,
703 absl::string_view header_extension_uri) {
704 for (const RtpExtension& rtp_header_extension :
705 content_info.media_description()->rtp_header_extensions()) {
706 if (rtp_header_extension.uri == header_extension_uri) {
707 return true;
708 }
709 }
710 return false;
711}
712
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000713} // namespace
714
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100715// This class stores state related to a SetRemoteDescription operation, captures
716// and reports potential errors that migth occur and makes sure to notify the
717// observer of the operation and the operations chain of completion.
718class SdpOfferAnswerHandler::RemoteDescriptionOperation {
719 public:
720 RemoteDescriptionOperation(
721 SdpOfferAnswerHandler* handler,
722 std::unique_ptr<SessionDescriptionInterface> desc,
723 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer,
724 std::function<void()> operations_chain_callback)
725 : handler_(handler),
726 desc_(std::move(desc)),
727 observer_(std::move(observer)),
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100728 operations_chain_callback_(std::move(operations_chain_callback)),
729 unified_plan_(handler_->IsUnifiedPlan()) {
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100730 if (!desc_) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100731 type_ = static_cast<SdpType>(-1);
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100732 InvalidParam("SessionDescription is NULL.");
733 } else {
734 type_ = desc_->GetType();
735 }
736 }
737
738 ~RemoteDescriptionOperation() {
739 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
740 SignalCompletion();
741 operations_chain_callback_();
742 }
743
744 bool ok() const { return error_.ok(); }
745
746 // Notifies the observer that the operation is complete and releases the
747 // reference to the observer.
748 void SignalCompletion() {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100749 if (!observer_)
750 return;
751
752 if (!error_.ok() && type_ != static_cast<SdpType>(-1)) {
753 std::string error_message =
754 GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type_, error_);
755 RTC_LOG(LS_ERROR) << error_message;
756 error_.set_message(std::move(error_message));
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100757 }
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100758
759 observer_->OnSetRemoteDescriptionComplete(error_);
760 observer_ = nullptr; // Only fire the notification once.
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100761 }
762
763 // If a session error has occurred the PeerConnection is in a possibly
764 // inconsistent state so fail right away.
765 bool HaveSessionError() {
766 RTC_DCHECK(ok());
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100767 if (handler_->session_error() != SessionError::kNone)
768 InternalError(handler_->GetSessionErrorMsg());
769 return !ok();
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100770 }
771
772 // Returns true if the operation was a rollback operation. If this function
773 // returns true, the caller should consider the operation complete. Otherwise
774 // proceed to the next step.
775 bool MaybeRollback() {
776 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
777 RTC_DCHECK(ok());
778 if (type_ != SdpType::kRollback) {
779 // Check if we can do an implicit rollback.
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100780 if (type_ == SdpType::kOffer && unified_plan_ &&
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100781 handler_->pc_->configuration()->enable_implicit_rollback &&
782 handler_->signaling_state() ==
783 PeerConnectionInterface::kHaveLocalOffer) {
784 handler_->Rollback(type_);
785 }
786 return false;
787 }
788
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100789 if (unified_plan_) {
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100790 error_ = handler_->Rollback(type_);
791 } else if (type_ == SdpType::kRollback) {
792 Unsupported("Rollback not supported in Plan B");
793 }
794
795 return true;
796 }
797
798 // Report to UMA the format of the received offer or answer.
799 void ReportOfferAnswerUma() {
800 RTC_DCHECK(ok());
801 if (type_ == SdpType::kOffer || type_ == SdpType::kAnswer) {
802 handler_->pc_->ReportSdpFormatReceived(*desc_.get());
803 handler_->pc_->ReportSdpBundleUsage(*desc_.get());
804 }
805 }
806
807 // Checks if the session description for the operation is valid. If not, the
808 // function captures error information and returns false. Note that if the
809 // return value is false, the operation should be considered done.
810 bool IsDescriptionValid() {
811 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
812 RTC_DCHECK(ok());
813 RTC_DCHECK(bundle_groups_by_mid_.empty()) << "Already called?";
814 bundle_groups_by_mid_ = GetBundleGroupsByMid(description());
815 error_ = handler_->ValidateSessionDescription(
816 desc_.get(), cricket::CS_REMOTE, bundle_groups_by_mid_);
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100817 return ok();
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100818 }
819
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100820 // Transfers ownership of the session description object over to `handler_`.
821 bool ReplaceRemoteDescriptionAndCheckEror() {
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100822 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
823 RTC_DCHECK(ok());
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100824 RTC_DCHECK(desc_);
825 RTC_DCHECK(!replaced_remote_description_);
826#if RTC_DCHECK_IS_ON
827 const auto* existing_remote_description = handler_->remote_description();
828#endif
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100829
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100830 error_ = handler_->ReplaceRemoteDescription(std::move(desc_), type_,
831 &replaced_remote_description_);
832
833 if (ok()) {
834#if RTC_DCHECK_IS_ON
835 // Sanity check that our `old_remote_description()` method always returns
836 // the same value as `remote_description()` did before the call to
837 // ReplaceRemoteDescription.
838 RTC_DCHECK_EQ(existing_remote_description, old_remote_description());
839#endif
840 } else {
841 SetAsSessionError();
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100842 }
843
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100844 return ok();
845 }
846
847 bool UpdateChannels() {
848 RTC_DCHECK(ok());
849 RTC_DCHECK(!desc_) << "ReplaceRemoteDescription hasn't been called";
850
851 const auto* remote_description = handler_->remote_description();
852
853 const cricket::SessionDescription* session_desc =
854 remote_description->description();
855
856 // Transport and Media channels will be created only when offer is set.
857 if (unified_plan_) {
858 error_ = handler_->UpdateTransceiversAndDataChannels(
859 cricket::CS_REMOTE, *remote_description,
860 handler_->local_description(), old_remote_description(),
861 bundle_groups_by_mid_);
862 } else {
863 // Media channels will be created only when offer is set. These may use
864 // new transports just created by PushdownTransportDescription.
865 if (type_ == SdpType::kOffer) {
866 // TODO(mallinath) - Handle CreateChannel failure, as new local
867 // description is applied. Restore back to old description.
868 error_ = handler_->CreateChannels(*session_desc);
869 }
870 // Remove unused channels if MediaContentDescription is rejected.
871 handler_->RemoveUnusedChannels(session_desc);
872 }
873
874 return ok();
875 }
876
877 bool UpdateSessionState() {
878 RTC_DCHECK(ok());
879 error_ = handler_->UpdateSessionState(
880 type_, cricket::CS_REMOTE,
881 handler_->remote_description()->description(), bundle_groups_by_mid_);
882 if (!ok())
883 SetAsSessionError();
884 return ok();
885 }
886
887 bool UseCandidatesInRemoteDescription() {
888 RTC_DCHECK(ok());
889 if (handler_->local_description() &&
890 !handler_->UseCandidatesInRemoteDescription()) {
891 InvalidParam(kInvalidCandidates);
892 }
893 return ok();
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100894 }
895
896 // Convenience getter for desc_->GetType().
897 SdpType type() const { return type_; }
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100898 bool unified_plan() const { return unified_plan_; }
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100899 cricket::SessionDescription* description() { return desc_->description(); }
900
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100901 const SessionDescriptionInterface* old_remote_description() const {
902 RTC_DCHECK(!desc_) << "Called before replacing the remote description";
903 if (type_ == SdpType::kAnswer)
904 return replaced_remote_description_.get();
905 return replaced_remote_description_
906 ? replaced_remote_description_.get()
907 : handler_->current_remote_description();
908 }
909
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100910 // Returns a reference to a cached map of bundle groups ordered by mid.
911 // Note that this will only be valid after a successful call to
912 // `IsDescriptionValid`.
913 const std::map<std::string, const cricket::ContentGroup*>&
914 bundle_groups_by_mid() const {
915 RTC_DCHECK(ok());
916 return bundle_groups_by_mid_;
917 }
918
919 private:
920 // Convenience methods for populating the embedded `error_` object.
921 void Unsupported(std::string message) {
922 SetError(RTCErrorType::UNSUPPORTED_OPERATION, std::move(message));
923 }
924
925 void InvalidParam(std::string message) {
926 SetError(RTCErrorType::INVALID_PARAMETER, std::move(message));
927 }
928
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100929 void InternalError(std::string message) {
930 SetError(RTCErrorType::INTERNAL_ERROR, std::move(message));
931 }
932
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100933 void SetError(RTCErrorType type, std::string message) {
934 RTC_DCHECK(ok()) << "Overwriting an existing error?";
935 error_ = RTCError(type, std::move(message));
936 }
937
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100938 // Called when the PeerConnection could be in an inconsistent state and we set
939 // the session error so that future calls to
940 // SetLocalDescription/SetRemoteDescription fail.
941 void SetAsSessionError() {
942 RTC_DCHECK(!ok());
943 handler_->SetSessionError(SessionError::kContent, error_.message());
944 }
945
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100946 SdpOfferAnswerHandler* const handler_;
947 std::unique_ptr<SessionDescriptionInterface> desc_;
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100948 // Keeps the replaced session description object alive while the operation
949 // is taking place since methods that depend on `old_remote_description()`
950 // for updating the state, need it.
951 std::unique_ptr<SessionDescriptionInterface> replaced_remote_description_;
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100952 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer_;
953 std::function<void()> operations_chain_callback_;
954 RTCError error_ = RTCError::OK();
955 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid_;
956 SdpType type_;
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +0100957 const bool unified_plan_;
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +0100958};
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000959// Used by parameterless SetLocalDescription() to create an offer or answer.
960// Upon completion of creating the session description, SetLocalDescription() is
961// invoked with the result.
962class SdpOfferAnswerHandler::ImplicitCreateSessionDescriptionObserver
963 : public CreateSessionDescriptionObserver {
964 public:
965 ImplicitCreateSessionDescriptionObserver(
966 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler,
967 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
968 set_local_description_observer)
969 : sdp_handler_(std::move(sdp_handler)),
970 set_local_description_observer_(
971 std::move(set_local_description_observer)) {}
972 ~ImplicitCreateSessionDescriptionObserver() override {
973 RTC_DCHECK(was_called_);
974 }
975
976 void SetOperationCompleteCallback(
977 std::function<void()> operation_complete_callback) {
978 operation_complete_callback_ = std::move(operation_complete_callback);
979 }
980
981 bool was_called() const { return was_called_; }
982
983 void OnSuccess(SessionDescriptionInterface* desc_ptr) override {
984 RTC_DCHECK(!was_called_);
985 std::unique_ptr<SessionDescriptionInterface> desc(desc_ptr);
986 was_called_ = true;
987
Artem Titov880fa812021-07-30 22:30:23 +0200988 // Abort early if `pc_` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000989 if (!sdp_handler_) {
990 operation_complete_callback_();
991 return;
992 }
993 // DoSetLocalDescription() is a synchronous operation that invokes
Artem Titov880fa812021-07-30 22:30:23 +0200994 // `set_local_description_observer_` with the result.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +0000995 sdp_handler_->DoSetLocalDescription(
996 std::move(desc), std::move(set_local_description_observer_));
997 operation_complete_callback_();
998 }
999
1000 void OnFailure(RTCError error) override {
1001 RTC_DCHECK(!was_called_);
1002 was_called_ = true;
1003 set_local_description_observer_->OnSetLocalDescriptionComplete(RTCError(
1004 error.type(), std::string("SetLocalDescription failed to create "
1005 "session description - ") +
1006 error.message()));
1007 operation_complete_callback_();
1008 }
1009
1010 private:
1011 bool was_called_ = false;
1012 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler_;
1013 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
1014 set_local_description_observer_;
1015 std::function<void()> operation_complete_callback_;
1016};
1017
1018// Wraps a CreateSessionDescriptionObserver and an OperationsChain operation
1019// complete callback. When the observer is invoked, the wrapped observer is
1020// invoked followed by invoking the completion callback.
1021class CreateSessionDescriptionObserverOperationWrapper
1022 : public CreateSessionDescriptionObserver {
1023 public:
1024 CreateSessionDescriptionObserverOperationWrapper(
1025 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer,
1026 std::function<void()> operation_complete_callback)
1027 : observer_(std::move(observer)),
1028 operation_complete_callback_(std::move(operation_complete_callback)) {
1029 RTC_DCHECK(observer_);
1030 }
1031 ~CreateSessionDescriptionObserverOperationWrapper() override {
Tomas Gunnarsson36992362020-10-05 21:41:36 +02001032#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001033 RTC_DCHECK(was_called_);
Tomas Gunnarsson36992362020-10-05 21:41:36 +02001034#endif
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001035 }
1036
1037 void OnSuccess(SessionDescriptionInterface* desc) override {
Tomas Gunnarsson36992362020-10-05 21:41:36 +02001038#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001039 RTC_DCHECK(!was_called_);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001040 was_called_ = true;
1041#endif // RTC_DCHECK_IS_ON
1042 // Completing the operation before invoking the observer allows the observer
1043 // to execute SetLocalDescription() without delay.
1044 operation_complete_callback_();
1045 observer_->OnSuccess(desc);
1046 }
1047
1048 void OnFailure(RTCError error) override {
Tomas Gunnarsson36992362020-10-05 21:41:36 +02001049#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001050 RTC_DCHECK(!was_called_);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001051 was_called_ = true;
1052#endif // RTC_DCHECK_IS_ON
1053 operation_complete_callback_();
1054 observer_->OnFailure(std::move(error));
1055 }
1056
1057 private:
Tomas Gunnarsson36992362020-10-05 21:41:36 +02001058#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001059 bool was_called_ = false;
1060#endif // RTC_DCHECK_IS_ON
1061 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer_;
1062 std::function<void()> operation_complete_callback_;
1063};
1064
1065// Wrapper for SetSessionDescriptionObserver that invokes the success or failure
1066// callback in a posted message handled by the peer connection. This introduces
1067// a delay that prevents recursive API calls by the observer, but this also
1068// means that the PeerConnection can be modified before the observer sees the
1069// result of the operation. This is ill-advised for synchronizing states.
1070//
1071// Implements both the SetLocalDescriptionObserverInterface and the
1072// SetRemoteDescriptionObserverInterface.
1073class SdpOfferAnswerHandler::SetSessionDescriptionObserverAdapter
1074 : public SetLocalDescriptionObserverInterface,
1075 public SetRemoteDescriptionObserverInterface {
1076 public:
1077 SetSessionDescriptionObserverAdapter(
1078 rtc::WeakPtr<SdpOfferAnswerHandler> handler,
1079 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer)
1080 : handler_(std::move(handler)),
1081 inner_observer_(std::move(inner_observer)) {}
1082
1083 // SetLocalDescriptionObserverInterface implementation.
1084 void OnSetLocalDescriptionComplete(RTCError error) override {
1085 OnSetDescriptionComplete(std::move(error));
1086 }
1087 // SetRemoteDescriptionObserverInterface implementation.
1088 void OnSetRemoteDescriptionComplete(RTCError error) override {
1089 OnSetDescriptionComplete(std::move(error));
1090 }
1091
1092 private:
1093 void OnSetDescriptionComplete(RTCError error) {
1094 if (!handler_)
1095 return;
1096 if (error.ok()) {
Harald Alvestrand1090e442020-10-05 07:01:09 +00001097 handler_->pc_->message_handler()->PostSetSessionDescriptionSuccess(
Niels Möllerafb246b2022-04-20 14:26:50 +02001098 inner_observer_.get());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001099 } else {
Harald Alvestrand1090e442020-10-05 07:01:09 +00001100 handler_->pc_->message_handler()->PostSetSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 14:26:50 +02001101 inner_observer_.get(), std::move(error));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001102 }
1103 }
1104
1105 rtc::WeakPtr<SdpOfferAnswerHandler> handler_;
1106 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer_;
1107};
1108
1109class SdpOfferAnswerHandler::LocalIceCredentialsToReplace {
1110 public:
1111 // Sets the ICE credentials that need restarting to the ICE credentials of
1112 // the current and pending descriptions.
1113 void SetIceCredentialsFromLocalDescriptions(
1114 const SessionDescriptionInterface* current_local_description,
1115 const SessionDescriptionInterface* pending_local_description) {
1116 ice_credentials_.clear();
1117 if (current_local_description) {
1118 AppendIceCredentialsFromSessionDescription(*current_local_description);
1119 }
1120 if (pending_local_description) {
1121 AppendIceCredentialsFromSessionDescription(*pending_local_description);
1122 }
1123 }
1124
1125 void ClearIceCredentials() { ice_credentials_.clear(); }
1126
1127 // Returns true if we have ICE credentials that need restarting.
1128 bool HasIceCredentials() const { return !ice_credentials_.empty(); }
1129
Artem Titov880fa812021-07-30 22:30:23 +02001130 // Returns true if `local_description` shares no ICE credentials with the
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001131 // ICE credentials that need restarting.
1132 bool SatisfiesIceRestart(
1133 const SessionDescriptionInterface& local_description) const {
1134 for (const auto& transport_info :
1135 local_description.description()->transport_infos()) {
1136 if (ice_credentials_.find(std::make_pair(
1137 transport_info.description.ice_ufrag,
1138 transport_info.description.ice_pwd)) != ice_credentials_.end()) {
1139 return false;
1140 }
1141 }
1142 return true;
1143 }
1144
1145 private:
1146 void AppendIceCredentialsFromSessionDescription(
1147 const SessionDescriptionInterface& desc) {
1148 for (const auto& transport_info : desc.description()->transport_infos()) {
1149 ice_credentials_.insert(
1150 std::make_pair(transport_info.description.ice_ufrag,
1151 transport_info.description.ice_pwd));
1152 }
1153 }
1154
1155 std::set<std::pair<std::string, std::string>> ice_credentials_;
1156};
1157
Harald Alvestrand66c40362022-01-28 17:41:30 +00001158SdpOfferAnswerHandler::SdpOfferAnswerHandler(PeerConnectionSdpMethods* pc,
1159 ConnectionContext* context)
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001160 : pc_(pc),
Harald Alvestrand66c40362022-01-28 17:41:30 +00001161 context_(context),
Harald Alvestrand6f04b652020-10-09 11:42:17 +00001162 local_streams_(StreamCollection::Create()),
1163 remote_streams_(StreamCollection::Create()),
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001164 operations_chain_(rtc::OperationsChain::Create()),
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001165 rtcp_cname_(GenerateRtcpCname()),
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001166 local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()),
1167 weak_ptr_factory_(this) {
1168 operations_chain_->SetOnChainEmptyCallback(
1169 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr()]() {
1170 if (!this_weak_ptr)
1171 return;
1172 this_weak_ptr->OnOperationsChainEmpty();
1173 });
1174}
1175
1176SdpOfferAnswerHandler::~SdpOfferAnswerHandler() {}
1177
Harald Alvestrand9cd199d2020-10-27 07:10:43 +00001178// Static
1179std::unique_ptr<SdpOfferAnswerHandler> SdpOfferAnswerHandler::Create(
Harald Alvestrand5b661302022-01-28 13:08:34 +00001180 PeerConnectionSdpMethods* pc,
Harald Alvestrand9cd199d2020-10-27 07:10:43 +00001181 const PeerConnectionInterface::RTCConfiguration& configuration,
Harald Alvestrand66c40362022-01-28 17:41:30 +00001182 PeerConnectionDependencies& dependencies,
1183 ConnectionContext* context) {
1184 auto handler = absl::WrapUnique(new SdpOfferAnswerHandler(pc, context));
Jonas Orelanded99dae2022-03-09 09:28:10 +01001185 handler->Initialize(configuration, dependencies, context);
Harald Alvestrand9cd199d2020-10-27 07:10:43 +00001186 return handler;
1187}
1188
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001189void SdpOfferAnswerHandler::Initialize(
1190 const PeerConnectionInterface::RTCConfiguration& configuration,
Jonas Orelanded99dae2022-03-09 09:28:10 +01001191 PeerConnectionDependencies& dependencies,
1192 ConnectionContext* context) {
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001193 RTC_DCHECK_RUN_ON(signaling_thread());
Henrik Boström6344bf12022-05-10 09:34:27 +02001194 // 100 kbps is used by default, but can be overriden by a non-standard
1195 // RTCConfiguration value (not available on Web).
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001196 video_options_.screencast_min_bitrate_kbps =
Henrik Boström6344bf12022-05-10 09:34:27 +02001197 configuration.screencast_min_bitrate.value_or(100);
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001198 audio_options_.combined_audio_video_bwe =
1199 configuration.combined_audio_video_bwe;
1200
1201 audio_options_.audio_jitter_buffer_max_packets =
1202 configuration.audio_jitter_buffer_max_packets;
1203
1204 audio_options_.audio_jitter_buffer_fast_accelerate =
1205 configuration.audio_jitter_buffer_fast_accelerate;
1206
1207 audio_options_.audio_jitter_buffer_min_delay_ms =
1208 configuration.audio_jitter_buffer_min_delay_ms;
1209
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001210 // Obtain a certificate from RTCConfiguration if any were provided (optional).
1211 rtc::scoped_refptr<rtc::RTCCertificate> certificate;
1212 if (!configuration.certificates.empty()) {
1213 // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of
1214 // just picking the first one. The decision should be made based on the DTLS
1215 // handshake. The DTLS negotiations need to know about all certificates.
1216 certificate = configuration.certificates[0];
1217 }
1218
1219 webrtc_session_desc_factory_ =
1220 std::make_unique<WebRtcSessionDescriptionFactory>(
Jonas Orelanded99dae2022-03-09 09:28:10 +01001221 context, this, pc_->session_id(), pc_->dtls_enabled(),
Danil Chapovalovb7da8162022-08-22 16:39:34 +02001222 std::move(dependencies.cert_generator), std::move(certificate),
Harald Alvestranda0947872020-11-09 14:15:00 +00001223 [this](const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
Harald Alvestrandbc32c562022-02-09 12:08:47 +00001224 RTC_DCHECK_RUN_ON(signaling_thread());
1225 transport_controller_s()->SetLocalCertificate(certificate);
Jonas Oreland6c7f9842022-04-19 17:24:10 +02001226 },
1227 pc_->trials());
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001228
Harald Alvestrand0d018412021-11-04 13:52:31 +00001229 if (pc_->options()->disable_encryption) {
1230 webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
1231 }
1232
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001233 webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions(
1234 pc_->GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions);
1235 webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan());
1236
Harald Alvestrand9cd199d2020-10-27 07:10:43 +00001237 if (dependencies.video_bitrate_allocator_factory) {
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001238 video_bitrate_allocator_factory_ =
Harald Alvestrand9cd199d2020-10-27 07:10:43 +00001239 std::move(dependencies.video_bitrate_allocator_factory);
Harald Alvestrand763f5a92020-10-22 10:39:40 +00001240 } else {
1241 video_bitrate_allocator_factory_ =
1242 CreateBuiltinVideoBitrateAllocatorFactory();
1243 }
1244}
1245
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001246// ==================================================================
Artem Titovc6c02ef2022-05-09 08:30:09 +00001247// Access to pc_ variables
Harald Alvestrand35f4b4c2022-05-16 10:36:43 +00001248cricket::MediaEngineInterface* SdpOfferAnswerHandler::media_engine() const {
1249 RTC_DCHECK(context_);
Harald Alvestrandc3fa7c32022-05-22 10:57:01 +00001250 return context_->media_engine();
Harald Alvestrand35f4b4c2022-05-16 10:36:43 +00001251}
1252
Harald Alvestrande15fb152020-10-19 13:28:05 +00001253TransceiverList* SdpOfferAnswerHandler::transceivers() {
1254 if (!pc_->rtp_manager()) {
1255 return nullptr;
1256 }
1257 return pc_->rtp_manager()->transceivers();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001258}
Harald Alvestrand35f4b4c2022-05-16 10:36:43 +00001259
Harald Alvestrande15fb152020-10-19 13:28:05 +00001260const TransceiverList* SdpOfferAnswerHandler::transceivers() const {
1261 if (!pc_->rtp_manager()) {
1262 return nullptr;
1263 }
1264 return pc_->rtp_manager()->transceivers();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001265}
Harald Alvestrandbc32c562022-02-09 12:08:47 +00001266JsepTransportController* SdpOfferAnswerHandler::transport_controller_s() {
1267 return pc_->transport_controller_s();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001268}
Harald Alvestrandbc32c562022-02-09 12:08:47 +00001269JsepTransportController* SdpOfferAnswerHandler::transport_controller_n() {
1270 return pc_->transport_controller_n();
1271}
1272const JsepTransportController* SdpOfferAnswerHandler::transport_controller_s()
Harald Alvestrandf01bd6c2020-10-23 13:30:46 +00001273 const {
Harald Alvestrandbc32c562022-02-09 12:08:47 +00001274 return pc_->transport_controller_s();
1275}
1276const JsepTransportController* SdpOfferAnswerHandler::transport_controller_n()
1277 const {
1278 return pc_->transport_controller_n();
Harald Alvestrandf01bd6c2020-10-23 13:30:46 +00001279}
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001280DataChannelController* SdpOfferAnswerHandler::data_channel_controller() {
Harald Alvestrand653429c2020-10-19 16:05:20 +00001281 return pc_->data_channel_controller();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001282}
1283const DataChannelController* SdpOfferAnswerHandler::data_channel_controller()
1284 const {
Harald Alvestrand653429c2020-10-19 16:05:20 +00001285 return pc_->data_channel_controller();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001286}
1287cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() {
Harald Alvestrand653429c2020-10-19 16:05:20 +00001288 return pc_->port_allocator();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001289}
1290const cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() const {
Harald Alvestrand653429c2020-10-19 16:05:20 +00001291 return pc_->port_allocator();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001292}
Harald Alvestrande15fb152020-10-19 13:28:05 +00001293RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() {
1294 return pc_->rtp_manager();
1295}
1296const RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() const {
1297 return pc_->rtp_manager();
1298}
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001299
1300// ===================================================================
1301
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001302void SdpOfferAnswerHandler::PrepareForShutdown() {
1303 RTC_DCHECK_RUN_ON(signaling_thread());
1304 weak_ptr_factory_.InvalidateWeakPtrs();
1305}
1306
1307void SdpOfferAnswerHandler::Close() {
1308 ChangeSignalingState(PeerConnectionInterface::kClosed);
1309}
1310
1311void SdpOfferAnswerHandler::RestartIce() {
1312 RTC_DCHECK_RUN_ON(signaling_thread());
1313 local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions(
1314 current_local_description(), pending_local_description());
1315 UpdateNegotiationNeeded();
1316}
1317
1318rtc::Thread* SdpOfferAnswerHandler::signaling_thread() const {
Harald Alvestrand66c40362022-01-28 17:41:30 +00001319 return context_->signaling_thread();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001320}
1321
Harald Alvestrandbc32c562022-02-09 12:08:47 +00001322rtc::Thread* SdpOfferAnswerHandler::network_thread() const {
1323 return context_->network_thread();
1324}
1325
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001326void SdpOfferAnswerHandler::CreateOffer(
1327 CreateSessionDescriptionObserver* observer,
1328 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
1329 RTC_DCHECK_RUN_ON(signaling_thread());
1330 // Chain this operation. If asynchronous operations are pending on the chain,
1331 // this operation will be queued to be invoked, otherwise the contents of the
1332 // lambda will execute immediately.
1333 operations_chain_->ChainOperation(
1334 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1335 observer_refptr =
1336 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
1337 options](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 22:30:23 +02001338 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001339 if (!this_weak_ptr) {
1340 observer_refptr->OnFailure(
1341 RTCError(RTCErrorType::INTERNAL_ERROR,
1342 "CreateOffer failed because the session was shut down"));
1343 operations_chain_callback();
1344 return;
1345 }
1346 // The operation completes asynchronously when the wrapper is invoked.
Niels Möllerb7aac6f2021-08-23 15:48:06 +02001347 auto observer_wrapper = rtc::make_ref_counted<
1348 CreateSessionDescriptionObserverOperationWrapper>(
1349 std::move(observer_refptr), std::move(operations_chain_callback));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001350 this_weak_ptr->DoCreateOffer(options, observer_wrapper);
1351 });
1352}
1353
1354void SdpOfferAnswerHandler::SetLocalDescription(
1355 SetSessionDescriptionObserver* observer,
1356 SessionDescriptionInterface* desc_ptr) {
1357 RTC_DCHECK_RUN_ON(signaling_thread());
1358 // Chain this operation. If asynchronous operations are pending on the chain,
1359 // this operation will be queued to be invoked, otherwise the contents of the
1360 // lambda will execute immediately.
1361 operations_chain_->ChainOperation(
1362 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1363 observer_refptr =
1364 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1365 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1366 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 22:30:23 +02001367 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001368 if (!this_weak_ptr) {
1369 // For consistency with SetSessionDescriptionObserverAdapter whose
1370 // posted messages doesn't get processed when the PC is destroyed, we
Artem Titov880fa812021-07-30 22:30:23 +02001371 // do not inform `observer_refptr` that the operation failed.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001372 operations_chain_callback();
1373 return;
1374 }
1375 // SetSessionDescriptionObserverAdapter takes care of making sure the
Artem Titov880fa812021-07-30 22:30:23 +02001376 // `observer_refptr` is invoked in a posted message.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001377 this_weak_ptr->DoSetLocalDescription(
1378 std::move(desc),
Niels Möllerb7aac6f2021-08-23 15:48:06 +02001379 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1380 this_weak_ptr, observer_refptr));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001381 // For backwards-compatability reasons, we declare the operation as
1382 // completed here (rather than in a post), so that the operation chain
1383 // is not blocked by this operation when the observer is invoked. This
1384 // allows the observer to trigger subsequent offer/answer operations
1385 // synchronously if the operation chain is now empty.
1386 operations_chain_callback();
1387 });
1388}
1389
1390void SdpOfferAnswerHandler::SetLocalDescription(
1391 std::unique_ptr<SessionDescriptionInterface> desc,
1392 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1393 RTC_DCHECK_RUN_ON(signaling_thread());
1394 // Chain this operation. If asynchronous operations are pending on the chain,
1395 // this operation will be queued to be invoked, otherwise the contents of the
1396 // lambda will execute immediately.
1397 operations_chain_->ChainOperation(
1398 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1399 desc = std::move(desc)](
1400 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 22:30:23 +02001401 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001402 if (!this_weak_ptr) {
1403 observer->OnSetLocalDescriptionComplete(RTCError(
1404 RTCErrorType::INTERNAL_ERROR,
1405 "SetLocalDescription failed because the session was shut down"));
1406 operations_chain_callback();
1407 return;
1408 }
1409 this_weak_ptr->DoSetLocalDescription(std::move(desc), observer);
1410 // DoSetLocalDescription() is implemented as a synchronous operation.
Artem Titov880fa812021-07-30 22:30:23 +02001411 // The `observer` will already have been informed that it completed, and
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001412 // we can mark this operation as complete without any loose ends.
1413 operations_chain_callback();
1414 });
1415}
1416
1417void SdpOfferAnswerHandler::SetLocalDescription(
1418 SetSessionDescriptionObserver* observer) {
1419 RTC_DCHECK_RUN_ON(signaling_thread());
1420 SetLocalDescription(
Niels Möllerb7aac6f2021-08-23 15:48:06 +02001421 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
Niels Möllere7cc8832022-01-04 15:20:03 +01001422 weak_ptr_factory_.GetWeakPtr(),
1423 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer)));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001424}
1425
1426void SdpOfferAnswerHandler::SetLocalDescription(
1427 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1428 RTC_DCHECK_RUN_ON(signaling_thread());
Artem Titov880fa812021-07-30 22:30:23 +02001429 // The `create_sdp_observer` handles performing DoSetLocalDescription() with
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001430 // the resulting description as well as completing the operation.
Niels Möllerb7aac6f2021-08-23 15:48:06 +02001431 auto create_sdp_observer =
1432 rtc::make_ref_counted<ImplicitCreateSessionDescriptionObserver>(
1433 weak_ptr_factory_.GetWeakPtr(), observer);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001434 // Chain this operation. If asynchronous operations are pending on the chain,
1435 // this operation will be queued to be invoked, otherwise the contents of the
1436 // lambda will execute immediately.
1437 operations_chain_->ChainOperation(
1438 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1439 create_sdp_observer](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 22:30:23 +02001440 // The `create_sdp_observer` is responsible for completing the
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001441 // operation.
1442 create_sdp_observer->SetOperationCompleteCallback(
1443 std::move(operations_chain_callback));
Artem Titov880fa812021-07-30 22:30:23 +02001444 // Abort early if `this_weak_ptr` is no longer valid. This triggers the
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001445 // same code path as if DoCreateOffer() or DoCreateAnswer() failed.
1446 if (!this_weak_ptr) {
1447 create_sdp_observer->OnFailure(RTCError(
1448 RTCErrorType::INTERNAL_ERROR,
1449 "SetLocalDescription failed because the session was shut down"));
1450 return;
1451 }
1452 switch (this_weak_ptr->signaling_state()) {
1453 case PeerConnectionInterface::kStable:
1454 case PeerConnectionInterface::kHaveLocalOffer:
1455 case PeerConnectionInterface::kHaveRemotePrAnswer:
1456 // TODO(hbos): If [LastCreatedOffer] exists and still represents the
1457 // current state of the system, use that instead of creating another
1458 // offer.
1459 this_weak_ptr->DoCreateOffer(
1460 PeerConnectionInterface::RTCOfferAnswerOptions(),
1461 create_sdp_observer);
1462 break;
1463 case PeerConnectionInterface::kHaveLocalPrAnswer:
1464 case PeerConnectionInterface::kHaveRemoteOffer:
1465 // TODO(hbos): If [LastCreatedAnswer] exists and still represents
1466 // the current state of the system, use that instead of creating
1467 // another answer.
1468 this_weak_ptr->DoCreateAnswer(
1469 PeerConnectionInterface::RTCOfferAnswerOptions(),
1470 create_sdp_observer);
1471 break;
1472 case PeerConnectionInterface::kClosed:
1473 create_sdp_observer->OnFailure(RTCError(
1474 RTCErrorType::INVALID_STATE,
1475 "SetLocalDescription called when PeerConnection is closed."));
1476 break;
1477 }
1478 });
1479}
1480
1481RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
Henrik Boströmf8187e02021-04-26 21:04:26 +02001482 std::unique_ptr<SessionDescriptionInterface> desc,
1483 const std::map<std::string, const cricket::ContentGroup*>&
1484 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 13:30:46 +02001485 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyLocalDescription");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001486 RTC_DCHECK_RUN_ON(signaling_thread());
1487 RTC_DCHECK(desc);
1488
Philipp Hancke7baa63f2022-09-01 15:39:50 +02001489 // Invalidate the stats caches to make sure that they get
1490 // updated the next time getStats() gets called, as updating the session
1491 // description affects the stats.
1492 pc_->ClearStatsCache();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001493
1494 // Take a reference to the old local description since it's used below to
1495 // compare against the new local description. When setting the new local
1496 // description, grab ownership of the replaced session description in case it
Artem Titov880fa812021-07-30 22:30:23 +02001497 // is the same as `old_local_description`, to keep it alive for the duration
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001498 // of the method.
1499 const SessionDescriptionInterface* old_local_description =
1500 local_description();
1501 std::unique_ptr<SessionDescriptionInterface> replaced_local_description;
1502 SdpType type = desc->GetType();
1503 if (type == SdpType::kAnswer) {
1504 replaced_local_description = pending_local_description_
1505 ? std::move(pending_local_description_)
1506 : std::move(current_local_description_);
1507 current_local_description_ = std::move(desc);
1508 pending_local_description_ = nullptr;
1509 current_remote_description_ = std::move(pending_remote_description_);
1510 } else {
1511 replaced_local_description = std::move(pending_local_description_);
1512 pending_local_description_ = std::move(desc);
1513 }
Harald Alvestrandbc32c562022-02-09 12:08:47 +00001514 if (!initial_offerer_) {
1515 initial_offerer_.emplace(type == SdpType::kOffer);
1516 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001517 // The session description to apply now must be accessed by
Artem Titov880fa812021-07-30 22:30:23 +02001518 // `local_description()`.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001519 RTC_DCHECK(local_description());
1520
1521 // Report statistics about any use of simulcast.
1522 ReportSimulcastApiVersion(kSimulcastVersionApplyLocalDescription,
1523 *local_description()->description());
1524
1525 if (!is_caller_) {
1526 if (remote_description()) {
1527 // Remote description was applied first, so this PC is the callee.
1528 is_caller_ = false;
1529 } else {
1530 // Local description is applied first, so this PC is the caller.
1531 is_caller_ = true;
1532 }
1533 }
1534
Harald Alvestranda474fbf2020-10-01 16:47:23 +00001535 RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001536 if (!error.ok()) {
1537 return error;
1538 }
1539
1540 if (IsUnifiedPlan()) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001541 error = UpdateTransceiversAndDataChannels(
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001542 cricket::CS_LOCAL, *local_description(), old_local_description,
Henrik Boströmf8187e02021-04-26 21:04:26 +02001543 remote_description(), bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001544 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001545 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1546 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001547 return error;
1548 }
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00001549 if (ConfiguredForMedia()) {
1550 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
1551 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
1552 for (const auto& transceiver_ext : transceivers()->List()) {
1553 auto transceiver = transceiver_ext->internal();
1554 if (transceiver->stopped()) {
1555 continue;
Harald Alvestrandc48ad732022-05-06 15:15:34 +00001556 }
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00001557
1558 // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots.
1559 // Note that code paths that don't set MID won't be able to use
1560 // information about DTLS transports.
1561 if (transceiver->mid()) {
1562 auto dtls_transport = LookupDtlsTransportByMid(
1563 context_->network_thread(), transport_controller_s(),
1564 *transceiver->mid());
1565 transceiver->sender_internal()->set_transport(dtls_transport);
1566 transceiver->receiver_internal()->set_transport(dtls_transport);
1567 }
1568
1569 const ContentInfo* content =
1570 FindMediaSectionForTransceiver(transceiver, local_description());
1571 if (!content) {
1572 continue;
1573 }
1574 const MediaContentDescription* media_desc =
1575 content->media_description();
1576 // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run
1577 // the following steps:
1578 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
1579 // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and
1580 // transceiver's [[FiredDirection]] slot is either "sendrecv" or
1581 // "recvonly", process the removal of a remote track for the media
1582 // description, given transceiver, removeList, and muteTracks.
1583 if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
1584 (transceiver->fired_direction() &&
1585 RtpTransceiverDirectionHasRecv(
1586 *transceiver->fired_direction()))) {
1587 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
1588 &removed_streams);
1589 }
1590 // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and
1591 // [[FiredDirection]] slots to direction.
1592 transceiver->set_current_direction(media_desc->direction());
1593 transceiver->set_fired_direction(media_desc->direction());
1594 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001595 }
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00001596 auto observer = pc_->Observer();
1597 for (const auto& transceiver : remove_list) {
1598 observer->OnRemoveTrack(transceiver->receiver());
1599 }
1600 for (const auto& stream : removed_streams) {
1601 observer->OnRemoveStream(stream);
1602 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001603 }
1604 } else {
1605 // Media channels will be created only when offer is set. These may use new
1606 // transports just created by PushdownTransportDescription.
1607 if (type == SdpType::kOffer) {
1608 // TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local
1609 // description is applied. Restore back to old description.
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001610 RTCError error = CreateChannels(*local_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001611 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001612 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1613 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001614 return error;
1615 }
1616 }
1617 // Remove unused channels if MediaContentDescription is rejected.
Harald Alvestranda474fbf2020-10-01 16:47:23 +00001618 RemoveUnusedChannels(local_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001619 }
1620
1621 error = UpdateSessionState(type, cricket::CS_LOCAL,
Henrik Boströmf8187e02021-04-26 21:04:26 +02001622 local_description()->description(),
1623 bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001624 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001625 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1626 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001627 return error;
1628 }
1629
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001630 // Now that we have a local description, we can push down remote candidates.
1631 UseCandidatesInRemoteDescription();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001632
1633 pending_ice_restarts_.clear();
Harald Alvestranda474fbf2020-10-01 16:47:23 +00001634 if (session_error() != SessionError::kNone) {
1635 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001636 }
1637
1638 // If setting the description decided our SSL role, allocate any necessary
1639 // SCTP sids.
1640 rtc::SSLRole role;
Florent Castelli516e2842021-04-19 15:29:50 +02001641 if (pc_->GetSctpSslRole(&role)) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001642 data_channel_controller()->AllocateSctpSids(role);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001643 }
1644
1645 if (IsUnifiedPlan()) {
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00001646 if (ConfiguredForMedia()) {
1647 // We must use List and not ListInternal here because
1648 // transceivers()->StableState() is indexed by the non-internal refptr.
1649 for (const auto& transceiver_ext : transceivers()->List()) {
1650 auto transceiver = transceiver_ext->internal();
1651 if (transceiver->stopped()) {
1652 continue;
1653 }
1654 const ContentInfo* content =
1655 FindMediaSectionForTransceiver(transceiver, local_description());
1656 if (!content) {
1657 continue;
1658 }
1659 cricket::ChannelInterface* channel = transceiver->channel();
1660 if (content->rejected || !channel || channel->local_streams().empty()) {
1661 // 0 is a special value meaning "this sender has no associated send
1662 // stream". Need to call this so the sender won't attempt to configure
1663 // a no longer existing stream and run into DCHECKs in the lower
1664 // layers.
1665 transceiver->sender_internal()->SetSsrc(0);
1666 } else {
1667 // Get the StreamParams from the channel which could generate SSRCs.
1668 const std::vector<StreamParams>& streams = channel->local_streams();
1669 transceiver->sender_internal()->set_stream_ids(
1670 streams[0].stream_ids());
1671 auto encodings =
1672 transceiver->sender_internal()->init_send_encodings();
1673 transceiver->sender_internal()->SetSsrc(streams[0].first_ssrc());
1674 if (!encodings.empty()) {
1675 transceivers()
1676 ->StableState(transceiver_ext)
1677 ->SetInitSendEncodings(encodings);
1678 }
Eldar Rello950d6b92021-04-06 22:38:00 +03001679 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001680 }
1681 }
1682 } else {
1683 // Plan B semantics.
1684
1685 // Update state and SSRC of local MediaStreams and DataChannels based on the
1686 // local session description.
1687 const cricket::ContentInfo* audio_content =
1688 GetFirstAudioContent(local_description()->description());
1689 if (audio_content) {
1690 if (audio_content->rejected) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00001691 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001692 } else {
1693 const cricket::AudioContentDescription* audio_desc =
1694 audio_content->media_description()->as_audio();
Harald Alvestranda474fbf2020-10-01 16:47:23 +00001695 UpdateLocalSenders(audio_desc->streams(), audio_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001696 }
1697 }
1698
1699 const cricket::ContentInfo* video_content =
1700 GetFirstVideoContent(local_description()->description());
1701 if (video_content) {
1702 if (video_content->rejected) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00001703 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001704 } else {
1705 const cricket::VideoContentDescription* video_desc =
1706 video_content->media_description()->as_video();
Harald Alvestranda474fbf2020-10-01 16:47:23 +00001707 UpdateLocalSenders(video_desc->streams(), video_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001708 }
1709 }
1710 }
1711
Harald Alvestrand7af57c62021-04-16 11:12:14 +00001712 // This function does nothing with data content.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001713
1714 if (type == SdpType::kAnswer &&
1715 local_ice_credentials_to_replace_->SatisfiesIceRestart(
1716 *current_local_description_)) {
1717 local_ice_credentials_to_replace_->ClearIceCredentials();
1718 }
1719
1720 return RTCError::OK();
1721}
1722
1723void SdpOfferAnswerHandler::SetRemoteDescription(
1724 SetSessionDescriptionObserver* observer,
1725 SessionDescriptionInterface* desc_ptr) {
1726 RTC_DCHECK_RUN_ON(signaling_thread());
1727 // Chain this operation. If asynchronous operations are pending on the chain,
1728 // this operation will be queued to be invoked, otherwise the contents of the
1729 // lambda will execute immediately.
1730 operations_chain_->ChainOperation(
1731 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1732 observer_refptr =
1733 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1734 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1735 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 22:30:23 +02001736 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001737 if (!this_weak_ptr) {
1738 // For consistency with SetSessionDescriptionObserverAdapter whose
1739 // posted messages doesn't get processed when the PC is destroyed, we
Artem Titov880fa812021-07-30 22:30:23 +02001740 // do not inform `observer_refptr` that the operation failed.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001741 operations_chain_callback();
1742 return;
1743 }
1744 // SetSessionDescriptionObserverAdapter takes care of making sure the
Artem Titov880fa812021-07-30 22:30:23 +02001745 // `observer_refptr` is invoked in a posted message.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001746 this_weak_ptr->DoSetRemoteDescription(
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01001747 std::make_unique<RemoteDescriptionOperation>(
1748 this_weak_ptr.get(), std::move(desc),
1749 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1750 this_weak_ptr, observer_refptr),
1751 std::move(operations_chain_callback)));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001752 });
1753}
1754
1755void SdpOfferAnswerHandler::SetRemoteDescription(
1756 std::unique_ptr<SessionDescriptionInterface> desc,
1757 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer) {
1758 RTC_DCHECK_RUN_ON(signaling_thread());
1759 // Chain this operation. If asynchronous operations are pending on the chain,
1760 // this operation will be queued to be invoked, otherwise the contents of the
1761 // lambda will execute immediately.
1762 operations_chain_->ChainOperation(
1763 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1764 desc = std::move(desc)](
1765 std::function<void()> operations_chain_callback) mutable {
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01001766 if (!observer) {
1767 RTC_DLOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
1768 operations_chain_callback();
1769 return;
1770 }
1771
Artem Titov880fa812021-07-30 22:30:23 +02001772 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001773 if (!this_weak_ptr) {
1774 observer->OnSetRemoteDescriptionComplete(RTCError(
1775 RTCErrorType::INTERNAL_ERROR,
1776 "SetRemoteDescription failed because the session was shut down"));
1777 operations_chain_callback();
1778 return;
1779 }
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01001780
1781 this_weak_ptr->DoSetRemoteDescription(
1782 std::make_unique<RemoteDescriptionOperation>(
1783 this_weak_ptr.get(), std::move(desc), std::move(observer),
1784 std::move(operations_chain_callback)));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001785 });
1786}
1787
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001788RTCError SdpOfferAnswerHandler::ReplaceRemoteDescription(
Henrik Boströmf8187e02021-04-26 21:04:26 +02001789 std::unique_ptr<SessionDescriptionInterface> desc,
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001790 SdpType sdp_type,
1791 std::unique_ptr<SessionDescriptionInterface>* replaced_description) {
1792 RTC_DCHECK(replaced_description);
1793 if (sdp_type == SdpType::kAnswer) {
1794 *replaced_description = pending_remote_description_
1795 ? std::move(pending_remote_description_)
1796 : std::move(current_remote_description_);
1797 current_remote_description_ = std::move(desc);
1798 pending_remote_description_ = nullptr;
1799 current_local_description_ = std::move(pending_local_description_);
1800 } else {
1801 *replaced_description = std::move(pending_remote_description_);
1802 pending_remote_description_ = std::move(desc);
1803 }
1804
1805 // The session description to apply now must be accessed by
1806 // `remote_description()`.
1807 const cricket::SessionDescription* session_desc =
1808 remote_description()->description();
1809
1810 // Report statistics about any use of simulcast.
1811 ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription,
1812 *session_desc);
1813
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02001814 // NOTE: This will perform a BlockingCall() to the network thread.
Harald Alvestrandbc32c562022-02-09 12:08:47 +00001815 return transport_controller_s()->SetRemoteDescription(sdp_type, session_desc);
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001816}
1817
1818void SdpOfferAnswerHandler::ApplyRemoteDescription(
1819 std::unique_ptr<RemoteDescriptionOperation> operation) {
Markus Handell518669d2021-06-07 13:30:46 +02001820 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyRemoteDescription");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001821 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001822 RTC_DCHECK(operation->description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001823
Philipp Hancke7baa63f2022-09-01 15:39:50 +02001824 // Invalidate the stats caches to make sure that they get
1825 // updated next time getStats() gets called, as updating the session
1826 // description affects the stats.
1827 pc_->ClearStatsCache();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001828
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001829 if (!operation->ReplaceRemoteDescriptionAndCheckEror())
1830 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001831
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001832 if (!operation->UpdateChannels())
1833 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001834
1835 // NOTE: Candidates allocation will be initiated only when
1836 // SetLocalDescription is called.
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001837 if (!operation->UpdateSessionState())
1838 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001839
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001840 if (!operation->UseCandidatesInRemoteDescription())
1841 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001842
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001843 if (operation->old_remote_description()) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001844 for (const cricket::ContentInfo& content :
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001845 operation->old_remote_description()->description()->contents()) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001846 // Check if this new SessionDescription contains new ICE ufrag and
1847 // password that indicates the remote peer requests an ICE restart.
1848 // TODO(deadbeef): When we start storing both the current and pending
1849 // remote description, this should reset pending_ice_restarts and compare
1850 // against the current description.
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001851 if (CheckForRemoteIceRestart(operation->old_remote_description(),
1852 remote_description(), content.name)) {
1853 if (operation->type() == SdpType::kOffer) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001854 pending_ice_restarts_.insert(content.name);
1855 }
1856 } else {
1857 // We retain all received candidates only if ICE is not restarted.
1858 // When ICE is restarted, all previous candidates belong to an old
1859 // generation and should not be kept.
1860 // TODO(deadbeef): This goes against the W3C spec which says the remote
1861 // description should only contain candidates from the last set remote
1862 // description plus any candidates added since then. We should remove
1863 // this once we're sure it won't break anything.
1864 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001865 operation->old_remote_description(), content.name,
1866 mutable_remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001867 }
1868 }
1869 }
1870
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001871 if (operation->HaveSessionError())
1872 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001873
1874 // Set the the ICE connection state to connecting since the connection may
1875 // become writable with peer reflexive candidates before any remote candidate
1876 // is signaled.
1877 // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix
1878 // is to have a new signal the indicates a change in checking state from the
1879 // transport and expose a new checking() member from transport that can be
1880 // read to determine the current checking state. The existing SignalConnecting
1881 // actually means "gathering candidates", so cannot be be used here.
1882 if (remote_description()->GetType() != SdpType::kOffer &&
1883 remote_description()->number_of_mediasections() > 0u &&
Harald Alvestrand5b661302022-01-28 13:08:34 +00001884 pc_->ice_connection_state_internal() ==
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001885 PeerConnectionInterface::kIceConnectionNew) {
1886 pc_->SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1887 }
1888
1889 // If setting the description decided our SSL role, allocate any necessary
1890 // SCTP sids.
1891 rtc::SSLRole role;
Florent Castelli516e2842021-04-19 15:29:50 +02001892 if (pc_->GetSctpSslRole(&role)) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00001893 data_channel_controller()->AllocateSctpSids(role);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001894 }
1895
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001896 if (operation->unified_plan()) {
1897 ApplyRemoteDescriptionUpdateTransceiverState(operation->type());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001898 }
1899
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001900 const cricket::AudioContentDescription* audio_desc =
1901 GetFirstAudioContentDescription(remote_description()->description());
1902 const cricket::VideoContentDescription* video_desc =
1903 GetFirstVideoContentDescription(remote_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001904
1905 // Check if the descriptions include streams, just in case the peer supports
1906 // MSID, but doesn't indicate so with "a=msid-semantic".
1907 if (remote_description()->description()->msid_supported() ||
1908 (audio_desc && !audio_desc->streams().empty()) ||
1909 (video_desc && !video_desc->streams().empty())) {
1910 remote_peer_supports_msid_ = true;
1911 }
1912
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001913 if (!operation->unified_plan()) {
Tomas Gunnarssonb625edf2022-01-07 16:22:14 +00001914 PlanBUpdateSendersAndReceivers(
1915 GetFirstAudioContent(remote_description()->description()), audio_desc,
1916 GetFirstVideoContent(remote_description()->description()), video_desc);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001917 }
1918
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001919 if (operation->type() == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01001920 if (local_ice_credentials_to_replace_->SatisfiesIceRestart(
1921 *current_local_description_)) {
1922 local_ice_credentials_to_replace_->ClearIceCredentials();
1923 }
1924
1925 RemoveStoppedTransceivers();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001926 }
1927
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01001928 // Consider the operation complete at this point.
1929 operation->SignalCompletion();
1930
1931 SetRemoteDescriptionPostProcess(operation->type() == SdpType::kAnswer);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00001932}
1933
Tomas Gunnarsson651586c2022-01-07 18:33:12 +00001934void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState(
1935 SdpType sdp_type) {
1936 RTC_DCHECK_RUN_ON(signaling_thread());
1937 RTC_DCHECK(IsUnifiedPlan());
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00001938 if (!ConfiguredForMedia()) {
1939 return;
1940 }
Tomas Gunnarsson651586c2022-01-07 18:33:12 +00001941 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
1942 now_receiving_transceivers;
1943 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
1944 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
1945 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
1946 for (const auto& transceiver_ext : transceivers()->List()) {
1947 const auto transceiver = transceiver_ext->internal();
1948 const ContentInfo* content =
1949 FindMediaSectionForTransceiver(transceiver, remote_description());
1950 if (!content) {
1951 continue;
1952 }
1953 const MediaContentDescription* media_desc = content->media_description();
1954 RtpTransceiverDirection local_direction =
1955 RtpTransceiverDirectionReversed(media_desc->direction());
Henrik Boström0a162762022-05-02 15:47:52 +02001956 // Remember the previous remote streams if this is a remote offer. This
1957 // makes it possible to rollback modifications to the streams.
1958 if (sdp_type == SdpType::kOffer) {
1959 transceivers()
1960 ->StableState(transceiver_ext)
1961 ->SetRemoteStreamIds(transceiver->receiver()->stream_ids());
1962 }
Tomas Gunnarsson651586c2022-01-07 18:33:12 +00001963 // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the
1964 // RTCSessionDescription: Set the associated remote streams given
1965 // transceiver.[[Receiver]], msids, addList, and removeList".
1966 // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription
1967 if (RtpTransceiverDirectionHasRecv(local_direction)) {
1968 std::vector<std::string> stream_ids;
1969 if (!media_desc->streams().empty()) {
1970 // The remote description has signaled the stream IDs.
1971 stream_ids = media_desc->streams()[0].stream_ids();
1972 }
Tomas Gunnarsson651586c2022-01-07 18:33:12 +00001973
1974 RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name
1975 << " (" << GetStreamIdsString(stream_ids) << ").";
1976 SetAssociatedRemoteStreams(transceiver->receiver_internal(), stream_ids,
1977 &added_streams, &removed_streams);
1978 // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6
1979 // "Set the RTCSessionDescription: If direction is sendrecv or recvonly,
1980 // and transceiver's current direction is neither sendrecv nor recvonly,
1981 // process the addition of a remote track for the media description.
1982 if (!transceiver->fired_direction() ||
1983 !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) {
1984 RTC_LOG(LS_INFO) << "Processing the addition of a remote track for MID="
1985 << content->name << ".";
1986 // Since the transceiver is passed to the user in an
1987 // OnTrack event, we must use the proxied transceiver.
1988 now_receiving_transceivers.push_back(transceiver_ext);
1989 }
1990 }
1991 // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's
1992 // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the
1993 // removal of a remote track for the media description, given transceiver,
1994 // removeList, and muteTracks.
1995 if (!RtpTransceiverDirectionHasRecv(local_direction) &&
1996 (transceiver->fired_direction() &&
1997 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) {
1998 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
1999 &removed_streams);
2000 }
2001 // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction.
Henrik Boström0a162762022-05-02 15:47:52 +02002002 if (sdp_type == SdpType::kOffer) {
2003 // Remember the previous fired direction if this is a remote offer. This
2004 // makes it possible to rollback modifications to [[FiredDirection]],
2005 // which is necessary for "ontrack" to fire in or after rollback.
2006 transceivers()
2007 ->StableState(transceiver_ext)
2008 ->SetFiredDirection(transceiver->fired_direction());
2009 }
Tomas Gunnarsson651586c2022-01-07 18:33:12 +00002010 transceiver->set_fired_direction(local_direction);
2011 // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run
2012 // the following steps:
2013 if (sdp_type == SdpType::kPrAnswer || sdp_type == SdpType::kAnswer) {
2014 // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to
2015 // direction.
2016 transceiver->set_current_direction(local_direction);
2017 // 2.2.8.1.11.[3-6]: Set the transport internal slots.
2018 if (transceiver->mid()) {
2019 auto dtls_transport = LookupDtlsTransportByMid(
Harald Alvestrandbc32c562022-02-09 12:08:47 +00002020 context_->network_thread(), transport_controller_s(),
Harald Alvestrand66c40362022-01-28 17:41:30 +00002021 *transceiver->mid());
Tomas Gunnarsson651586c2022-01-07 18:33:12 +00002022 transceiver->sender_internal()->set_transport(dtls_transport);
2023 transceiver->receiver_internal()->set_transport(dtls_transport);
2024 }
2025 }
2026 // 2.2.8.1.12: If the media description is rejected, and transceiver is
2027 // not already stopped, stop the RTCRtpTransceiver transceiver.
2028 if (content->rejected && !transceiver->stopped()) {
2029 RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name
2030 << " since the media section was rejected.";
2031 transceiver->StopTransceiverProcedure();
2032 }
2033 if (!content->rejected && RtpTransceiverDirectionHasRecv(local_direction)) {
2034 if (!media_desc->streams().empty() &&
2035 media_desc->streams()[0].has_ssrcs()) {
2036 uint32_t ssrc = media_desc->streams()[0].first_ssrc();
2037 transceiver->receiver_internal()->SetupMediaChannel(ssrc);
2038 } else {
2039 transceiver->receiver_internal()->SetupUnsignaledMediaChannel();
2040 }
2041 }
2042 }
2043 // Once all processing has finished, fire off callbacks.
2044 auto observer = pc_->Observer();
2045 for (const auto& transceiver : now_receiving_transceivers) {
Henrik Boströmf7859892022-07-04 14:36:37 +02002046 pc_->legacy_stats()->AddTrack(transceiver->receiver()->track().get());
Tomas Gunnarsson651586c2022-01-07 18:33:12 +00002047 observer->OnTrack(transceiver);
2048 observer->OnAddTrack(transceiver->receiver(),
2049 transceiver->receiver()->streams());
2050 }
2051 for (const auto& stream : added_streams) {
2052 observer->OnAddStream(stream);
2053 }
2054 for (const auto& transceiver : remove_list) {
2055 observer->OnRemoveTrack(transceiver->receiver());
2056 }
2057 for (const auto& stream : removed_streams) {
2058 observer->OnRemoveStream(stream);
2059 }
2060}
2061
Tomas Gunnarssonb625edf2022-01-07 16:22:14 +00002062void SdpOfferAnswerHandler::PlanBUpdateSendersAndReceivers(
2063 const cricket::ContentInfo* audio_content,
2064 const cricket::AudioContentDescription* audio_desc,
2065 const cricket::ContentInfo* video_content,
2066 const cricket::VideoContentDescription* video_desc) {
2067 RTC_DCHECK_RUN_ON(signaling_thread());
2068 RTC_DCHECK(!IsUnifiedPlan());
2069
2070 // We wait to signal new streams until we finish processing the description,
2071 // since only at that point will new streams have all their tracks.
2072 rtc::scoped_refptr<StreamCollection> new_streams(StreamCollection::Create());
2073
2074 // TODO(steveanton): When removing RTP senders/receivers in response to a
2075 // rejected media section, there is some cleanup logic that expects the
2076 // voice/ video channel to still be set. But in this method the voice/video
2077 // channel would have been destroyed by the SetRemoteDescription caller
2078 // above so the cleanup that relies on them fails to run. The RemoveSenders
2079 // calls should be moved to right before the DestroyChannel calls to fix
2080 // this.
2081
2082 // Find all audio rtp streams and create corresponding remote AudioTracks
2083 // and MediaStreams.
2084 if (audio_content) {
2085 if (audio_content->rejected) {
2086 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
2087 } else {
2088 bool default_audio_track_needed =
2089 !remote_peer_supports_msid_ &&
2090 RtpTransceiverDirectionHasSend(audio_desc->direction());
2091 UpdateRemoteSendersList(GetActiveStreams(audio_desc),
2092 default_audio_track_needed, audio_desc->type(),
Niels Möllerafb246b2022-04-20 14:26:50 +02002093 new_streams.get());
Tomas Gunnarssonb625edf2022-01-07 16:22:14 +00002094 }
2095 }
2096
2097 // Find all video rtp streams and create corresponding remote VideoTracks
2098 // and MediaStreams.
2099 if (video_content) {
2100 if (video_content->rejected) {
2101 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
2102 } else {
2103 bool default_video_track_needed =
2104 !remote_peer_supports_msid_ &&
2105 RtpTransceiverDirectionHasSend(video_desc->direction());
2106 UpdateRemoteSendersList(GetActiveStreams(video_desc),
2107 default_video_track_needed, video_desc->type(),
Niels Möllerafb246b2022-04-20 14:26:50 +02002108 new_streams.get());
Tomas Gunnarssonb625edf2022-01-07 16:22:14 +00002109 }
2110 }
2111
2112 // Iterate new_streams and notify the observer about new MediaStreams.
2113 auto observer = pc_->Observer();
2114 for (size_t i = 0; i < new_streams->count(); ++i) {
2115 MediaStreamInterface* new_stream = new_streams->at(i);
Henrik Boströmf7859892022-07-04 14:36:37 +02002116 pc_->legacy_stats()->AddStream(new_stream);
Tomas Gunnarssonb625edf2022-01-07 16:22:14 +00002117 observer->OnAddStream(rtc::scoped_refptr<MediaStreamInterface>(new_stream));
2118 }
2119
2120 UpdateEndedRemoteMediaStreams();
2121}
2122
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002123void SdpOfferAnswerHandler::DoSetLocalDescription(
2124 std::unique_ptr<SessionDescriptionInterface> desc,
2125 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
2126 RTC_DCHECK_RUN_ON(signaling_thread());
2127 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetLocalDescription");
2128
2129 if (!observer) {
2130 RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL.";
2131 return;
2132 }
2133
2134 if (!desc) {
2135 observer->OnSetLocalDescriptionComplete(
2136 RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL."));
2137 return;
2138 }
2139
2140 // If a session error has occurred the PeerConnection is in a possibly
2141 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002142 if (session_error() != SessionError::kNone) {
2143 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002144 RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message;
2145 observer->OnSetLocalDescriptionComplete(
2146 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2147 return;
2148 }
2149
2150 // For SLD we support only explicit rollback.
2151 if (desc->GetType() == SdpType::kRollback) {
2152 if (IsUnifiedPlan()) {
2153 observer->OnSetLocalDescriptionComplete(Rollback(desc->GetType()));
2154 } else {
2155 observer->OnSetLocalDescriptionComplete(
2156 RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2157 "Rollback not supported in Plan B"));
2158 }
2159 return;
2160 }
2161
Henrik Boströmf8187e02021-04-26 21:04:26 +02002162 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid =
2163 GetBundleGroupsByMid(desc->description());
2164 RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL,
2165 bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002166 if (!error.ok()) {
2167 std::string error_message = GetSetDescriptionErrorMessage(
2168 cricket::CS_LOCAL, desc->GetType(), error);
2169 RTC_LOG(LS_ERROR) << error_message;
2170 observer->OnSetLocalDescriptionComplete(
2171 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2172 return;
2173 }
2174
2175 // Grab the description type before moving ownership to ApplyLocalDescription,
2176 // which may destroy it before returning.
2177 const SdpType type = desc->GetType();
2178
Henrik Boströmf8187e02021-04-26 21:04:26 +02002179 error = ApplyLocalDescription(std::move(desc), bundle_groups_by_mid);
Artem Titov880fa812021-07-30 22:30:23 +02002180 // `desc` may be destroyed at this point.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002181
2182 if (!error.ok()) {
2183 // If ApplyLocalDescription fails, the PeerConnection could be in an
2184 // inconsistent state, so act conservatively here and set the session error
2185 // so that future calls to SetLocalDescription/SetRemoteDescription fail.
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002186 SetSessionError(SessionError::kContent, error.message());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002187 std::string error_message =
2188 GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error);
2189 RTC_LOG(LS_ERROR) << error_message;
2190 observer->OnSetLocalDescriptionComplete(
2191 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2192 return;
2193 }
2194 RTC_DCHECK(local_description());
2195
2196 if (local_description()->GetType() == SdpType::kAnswer) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002197 RemoveStoppedTransceivers();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002198
2199 // TODO(deadbeef): We already had to hop to the network thread for
2200 // MaybeStartGathering...
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02002201 context_->network_thread()->BlockingCall(
2202 [this] { port_allocator()->DiscardCandidatePool(); });
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002203 // Make UMA notes about what was agreed to.
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002204 ReportNegotiatedSdpSemantics(*local_description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002205 }
2206
2207 observer->OnSetLocalDescriptionComplete(RTCError::OK());
Harald Alvestrand44d0dff2020-10-09 05:43:53 +00002208 pc_->NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002209
2210 // Check if negotiation is needed. We must do this after informing the
2211 // observer that SetLocalDescription() has completed to ensure negotiation is
2212 // not needed prior to the promise resolving.
2213 if (IsUnifiedPlan()) {
2214 bool was_negotiation_needed = is_negotiation_needed_;
2215 UpdateNegotiationNeeded();
2216 if (signaling_state() == PeerConnectionInterface::kStable &&
2217 was_negotiation_needed && is_negotiation_needed_) {
2218 // Legacy version.
2219 pc_->Observer()->OnRenegotiationNeeded();
2220 // Spec-compliant version; the event may get invalidated before firing.
2221 GenerateNegotiationNeededEvent();
2222 }
2223 }
2224
2225 // MaybeStartGathering needs to be called after informing the observer so that
2226 // we don't signal any candidates before signaling that SetLocalDescription
2227 // completed.
Harald Alvestrandbc32c562022-02-09 12:08:47 +00002228 transport_controller_s()->MaybeStartGathering();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002229}
2230
2231void SdpOfferAnswerHandler::DoCreateOffer(
2232 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2233 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2234 RTC_DCHECK_RUN_ON(signaling_thread());
2235 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateOffer");
2236
2237 if (!observer) {
2238 RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
2239 return;
2240 }
2241
2242 if (pc_->IsClosed()) {
2243 std::string error = "CreateOffer called when PeerConnection is closed.";
2244 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:09 +00002245 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 14:26:50 +02002246 observer.get(),
2247 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002248 return;
2249 }
2250
2251 // If a session error has occurred the PeerConnection is in a possibly
2252 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002253 if (session_error() != SessionError::kNone) {
2254 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002255 RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message;
Harald Alvestrand1090e442020-10-05 07:01:09 +00002256 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 14:26:50 +02002257 observer.get(),
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002258 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2259 return;
2260 }
2261
2262 if (!ValidateOfferAnswerOptions(options)) {
2263 std::string error = "CreateOffer called with invalid options.";
2264 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:09 +00002265 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 14:26:50 +02002266 observer.get(),
2267 RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002268 return;
2269 }
2270
2271 // Legacy handling for offer_to_receive_audio and offer_to_receive_video.
2272 // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions".
2273 if (IsUnifiedPlan()) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002274 RTCError error = HandleLegacyOfferOptions(options);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002275 if (!error.ok()) {
Harald Alvestrand1090e442020-10-05 07:01:09 +00002276 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 14:26:50 +02002277 observer.get(), std::move(error));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002278 return;
2279 }
2280 }
2281
2282 cricket::MediaSessionOptions session_options;
Harald Alvestrandc06e3742020-10-01 10:23:33 +00002283 GetOptionsForOffer(options, &session_options);
Niels Möllerafb246b2022-04-20 14:26:50 +02002284 webrtc_session_desc_factory_->CreateOffer(observer.get(), options,
2285 session_options);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002286}
2287
2288void SdpOfferAnswerHandler::CreateAnswer(
2289 CreateSessionDescriptionObserver* observer,
2290 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
Markus Handell518669d2021-06-07 13:30:46 +02002291 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateAnswer");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002292 RTC_DCHECK_RUN_ON(signaling_thread());
2293 // Chain this operation. If asynchronous operations are pending on the chain,
2294 // this operation will be queued to be invoked, otherwise the contents of the
2295 // lambda will execute immediately.
2296 operations_chain_->ChainOperation(
2297 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2298 observer_refptr =
2299 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
2300 options](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 22:30:23 +02002301 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002302 if (!this_weak_ptr) {
2303 observer_refptr->OnFailure(RTCError(
2304 RTCErrorType::INTERNAL_ERROR,
2305 "CreateAnswer failed because the session was shut down"));
2306 operations_chain_callback();
2307 return;
2308 }
2309 // The operation completes asynchronously when the wrapper is invoked.
Niels Möllerb7aac6f2021-08-23 15:48:06 +02002310 auto observer_wrapper = rtc::make_ref_counted<
2311 CreateSessionDescriptionObserverOperationWrapper>(
2312 std::move(observer_refptr), std::move(operations_chain_callback));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002313 this_weak_ptr->DoCreateAnswer(options, observer_wrapper);
2314 });
2315}
2316
2317void SdpOfferAnswerHandler::DoCreateAnswer(
2318 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2319 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2320 RTC_DCHECK_RUN_ON(signaling_thread());
2321 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateAnswer");
2322 if (!observer) {
2323 RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
2324 return;
2325 }
2326
2327 // If a session error has occurred the PeerConnection is in a possibly
2328 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002329 if (session_error() != SessionError::kNone) {
2330 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002331 RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message;
Harald Alvestrand1090e442020-10-05 07:01:09 +00002332 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 14:26:50 +02002333 observer.get(),
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002334 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2335 return;
2336 }
2337
2338 if (!(signaling_state_ == PeerConnectionInterface::kHaveRemoteOffer ||
2339 signaling_state_ == PeerConnectionInterface::kHaveLocalPrAnswer)) {
2340 std::string error =
2341 "PeerConnection cannot create an answer in a state other than "
2342 "have-remote-offer or have-local-pranswer.";
2343 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:09 +00002344 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 14:26:50 +02002345 observer.get(),
2346 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002347 return;
2348 }
2349
2350 // The remote description should be set if we're in the right state.
2351 RTC_DCHECK(remote_description());
2352
2353 if (IsUnifiedPlan()) {
2354 if (options.offer_to_receive_audio !=
2355 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2356 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not "
2357 "supported with Unified Plan semantics. Use the "
2358 "RtpTransceiver API instead.";
2359 }
2360 if (options.offer_to_receive_video !=
2361 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2362 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not "
2363 "supported with Unified Plan semantics. Use the "
2364 "RtpTransceiver API instead.";
2365 }
2366 }
2367
2368 cricket::MediaSessionOptions session_options;
Harald Alvestrandc06e3742020-10-01 10:23:33 +00002369 GetOptionsForAnswer(options, &session_options);
Niels Möllerafb246b2022-04-20 14:26:50 +02002370 webrtc_session_desc_factory_->CreateAnswer(observer.get(), session_options);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002371}
2372
2373void SdpOfferAnswerHandler::DoSetRemoteDescription(
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002374 std::unique_ptr<RemoteDescriptionOperation> operation) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002375 RTC_DCHECK_RUN_ON(signaling_thread());
2376 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetRemoteDescription");
2377
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002378 if (!operation->ok())
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002379 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002380
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002381 if (operation->HaveSessionError())
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002382 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002383
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002384 if (operation->MaybeRollback())
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002385 return;
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002386
2387 operation->ReportOfferAnswerUma();
2388
2389 // Handle remote descriptions missing a=mid lines for interop with legacy
2390 // end points.
2391 FillInMissingRemoteMids(operation->description());
2392 if (!operation->IsDescriptionValid())
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002393 return;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002394
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01002395 ApplyRemoteDescription(std::move(operation));
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002396}
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002397
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002398// Called after a DoSetRemoteDescription operation completes.
2399void SdpOfferAnswerHandler::SetRemoteDescriptionPostProcess(bool was_answer) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002400 RTC_DCHECK(remote_description());
2401
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002402 if (was_answer) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002403 // TODO(deadbeef): We already had to hop to the network thread for
2404 // MaybeStartGathering...
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02002405 context_->network_thread()->BlockingCall(
2406 [this] { port_allocator()->DiscardCandidatePool(); });
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002407 // Make UMA notes about what was agreed to.
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002408 ReportNegotiatedSdpSemantics(*remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002409 }
2410
Harald Alvestrand44d0dff2020-10-09 05:43:53 +00002411 pc_->NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002412
2413 // Check if negotiation is needed. We must do this after informing the
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002414 // observer that SetRemoteDescription() has completed to ensure negotiation
2415 // is not needed prior to the promise resolving.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002416 if (IsUnifiedPlan()) {
2417 bool was_negotiation_needed = is_negotiation_needed_;
2418 UpdateNegotiationNeeded();
2419 if (signaling_state() == PeerConnectionInterface::kStable &&
2420 was_negotiation_needed && is_negotiation_needed_) {
2421 // Legacy version.
2422 pc_->Observer()->OnRenegotiationNeeded();
2423 // Spec-compliant version; the event may get invalidated before firing.
2424 GenerateNegotiationNeededEvent();
2425 }
2426 }
2427}
2428
2429void SdpOfferAnswerHandler::SetAssociatedRemoteStreams(
2430 rtc::scoped_refptr<RtpReceiverInternal> receiver,
2431 const std::vector<std::string>& stream_ids,
2432 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* added_streams,
2433 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
2434 RTC_DCHECK_RUN_ON(signaling_thread());
2435 std::vector<rtc::scoped_refptr<MediaStreamInterface>> media_streams;
2436 for (const std::string& stream_id : stream_ids) {
Niels Möllere7cc8832022-01-04 15:20:03 +01002437 rtc::scoped_refptr<MediaStreamInterface> stream(
2438 remote_streams_->find(stream_id));
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002439 if (!stream) {
2440 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
2441 MediaStream::Create(stream_id));
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002442 remote_streams_->AddStream(stream);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002443 added_streams->push_back(stream);
2444 }
2445 media_streams.push_back(stream);
2446 }
2447 // Special case: "a=msid" missing, use random stream ID.
2448 if (media_streams.empty() &&
2449 !(remote_description()->description()->msid_signaling() &
2450 cricket::kMsidSignalingMediaSection)) {
2451 if (!missing_msid_default_stream_) {
2452 missing_msid_default_stream_ = MediaStreamProxy::Create(
2453 rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid()));
2454 added_streams->push_back(missing_msid_default_stream_);
2455 }
2456 media_streams.push_back(missing_msid_default_stream_);
2457 }
2458 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
2459 receiver->streams();
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002460 // SetStreams() will add/remove the receiver's track to/from the streams.
2461 // This differs from the spec - the spec uses an "addList" and "removeList"
2462 // to update the stream-track relationships in a later step. We do this
2463 // earlier, changing the order of things, but the end-result is the same.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002464 // TODO(hbos): When we remove remote_streams(), use set_stream_ids()
2465 // instead. https://crbug.com/webrtc/9480
2466 receiver->SetStreams(media_streams);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002467 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002468}
2469
2470bool SdpOfferAnswerHandler::AddIceCandidate(
2471 const IceCandidateInterface* ice_candidate) {
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002472 const AddIceCandidateResult result = AddIceCandidateInternal(ice_candidate);
2473 NoteAddIceCandidateResult(result);
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002474 // If the return value is kAddIceCandidateFailNotReady, the candidate has
2475 // been added, although not 'ready', but that's a success.
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002476 return result == kAddIceCandidateSuccess ||
2477 result == kAddIceCandidateFailNotReady;
2478}
2479
2480AddIceCandidateResult SdpOfferAnswerHandler::AddIceCandidateInternal(
2481 const IceCandidateInterface* ice_candidate) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002482 RTC_DCHECK_RUN_ON(signaling_thread());
2483 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
2484 if (pc_->IsClosed()) {
2485 RTC_LOG(LS_ERROR) << "AddIceCandidate: PeerConnection is closed.";
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002486 return kAddIceCandidateFailClosed;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002487 }
2488
2489 if (!remote_description()) {
2490 RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added "
2491 "without any remote session description.";
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002492 return kAddIceCandidateFailNoRemoteDescription;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002493 }
2494
2495 if (!ice_candidate) {
2496 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null.";
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002497 return kAddIceCandidateFailNullCandidate;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002498 }
2499
2500 bool valid = false;
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00002501 bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002502 if (!valid) {
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002503 return kAddIceCandidateFailNotValid;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002504 }
2505
2506 // Add this candidate to the remote session description.
2507 if (!mutable_remote_description()->AddCandidate(ice_candidate)) {
2508 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used.";
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002509 return kAddIceCandidateFailInAddition;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002510 }
2511
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002512 if (!ready) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002513 RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate.";
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002514 return kAddIceCandidateFailNotReady;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002515 }
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002516
2517 if (!UseCandidate(ice_candidate)) {
2518 return kAddIceCandidateFailNotUsable;
2519 }
2520
2521 pc_->NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED);
2522
2523 return kAddIceCandidateSuccess;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002524}
2525
2526void SdpOfferAnswerHandler::AddIceCandidate(
2527 std::unique_ptr<IceCandidateInterface> candidate,
2528 std::function<void(RTCError)> callback) {
Markus Handell518669d2021-06-07 13:30:46 +02002529 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002530 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002531 // Chain this operation. If asynchronous operations are pending on the
2532 // chain, this operation will be queued to be invoked, otherwise the
2533 // contents of the lambda will execute immediately.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002534 operations_chain_->ChainOperation(
2535 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2536 candidate = std::move(candidate), callback = std::move(callback)](
2537 std::function<void()> operations_chain_callback) {
Tomas Gunnarsson2efb8a52021-04-01 16:26:57 +02002538 auto result =
2539 this_weak_ptr
2540 ? this_weak_ptr->AddIceCandidateInternal(candidate.get())
2541 : kAddIceCandidateFailClosed;
2542 NoteAddIceCandidateResult(result);
2543 operations_chain_callback();
Henrik Boström347488e2022-01-21 15:18:08 +01002544 switch (result) {
2545 case AddIceCandidateResult::kAddIceCandidateSuccess:
2546 case AddIceCandidateResult::kAddIceCandidateFailNotReady:
2547 // Success!
2548 callback(RTCError::OK());
2549 break;
2550 case AddIceCandidateResult::kAddIceCandidateFailClosed:
2551 // Note that the spec says to just abort without resolving the
2552 // promise in this case, but this layer must return an RTCError.
2553 callback(RTCError(
2554 RTCErrorType::INVALID_STATE,
2555 "AddIceCandidate failed because the session was shut down"));
2556 break;
2557 case AddIceCandidateResult::kAddIceCandidateFailNoRemoteDescription:
2558 // Spec: "If remoteDescription is null return a promise rejected
2559 // with a newly created InvalidStateError."
2560 callback(RTCError(RTCErrorType::INVALID_STATE,
2561 "The remote description was null"));
2562 break;
2563 case AddIceCandidateResult::kAddIceCandidateFailNullCandidate:
2564 // TODO(https://crbug.com/935898): Handle end-of-candidates instead
2565 // of treating null candidate as an error.
2566 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2567 "Error processing ICE candidate"));
2568 break;
2569 case AddIceCandidateResult::kAddIceCandidateFailNotValid:
2570 case AddIceCandidateResult::kAddIceCandidateFailInAddition:
2571 case AddIceCandidateResult::kAddIceCandidateFailNotUsable:
2572 // Spec: "If candidate could not be successfully added [...] Reject
2573 // p with a newly created OperationError and abort these steps."
2574 // UNSUPPORTED_OPERATION maps to OperationError.
2575 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2576 "Error processing ICE candidate"));
2577 break;
2578 default:
2579 RTC_DCHECK_NOTREACHED();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002580 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002581 });
2582}
2583
2584bool SdpOfferAnswerHandler::RemoveIceCandidates(
2585 const std::vector<cricket::Candidate>& candidates) {
2586 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveIceCandidates");
2587 RTC_DCHECK_RUN_ON(signaling_thread());
2588 if (pc_->IsClosed()) {
2589 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: PeerConnection is closed.";
2590 return false;
2591 }
2592
2593 if (!remote_description()) {
2594 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed "
2595 "without any remote session description.";
2596 return false;
2597 }
2598
2599 if (candidates.empty()) {
2600 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty.";
2601 return false;
2602 }
2603
2604 size_t number_removed =
2605 mutable_remote_description()->RemoveCandidates(candidates);
2606 if (number_removed != candidates.size()) {
2607 RTC_LOG(LS_ERROR)
2608 << "RemoveIceCandidates: Failed to remove candidates. Requested "
2609 << candidates.size() << " but only " << number_removed
2610 << " are removed.";
2611 }
2612
2613 // Remove the candidates from the transport controller.
Harald Alvestrandbc32c562022-02-09 12:08:47 +00002614 RTCError error = transport_controller_s()->RemoveRemoteCandidates(candidates);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002615 if (!error.ok()) {
2616 RTC_LOG(LS_ERROR)
2617 << "RemoveIceCandidates: Error when removing remote candidates: "
2618 << error.message();
2619 }
2620 return true;
2621}
2622
2623void SdpOfferAnswerHandler::AddLocalIceCandidate(
2624 const JsepIceCandidate* candidate) {
2625 RTC_DCHECK_RUN_ON(signaling_thread());
2626 if (local_description()) {
2627 mutable_local_description()->AddCandidate(candidate);
2628 }
2629}
2630
2631void SdpOfferAnswerHandler::RemoveLocalIceCandidates(
2632 const std::vector<cricket::Candidate>& candidates) {
2633 RTC_DCHECK_RUN_ON(signaling_thread());
2634 if (local_description()) {
2635 mutable_local_description()->RemoveCandidates(candidates);
2636 }
2637}
2638
2639const SessionDescriptionInterface* SdpOfferAnswerHandler::local_description()
2640 const {
2641 RTC_DCHECK_RUN_ON(signaling_thread());
2642 return pending_local_description_ ? pending_local_description_.get()
2643 : current_local_description_.get();
2644}
2645
2646const SessionDescriptionInterface* SdpOfferAnswerHandler::remote_description()
2647 const {
2648 RTC_DCHECK_RUN_ON(signaling_thread());
2649 return pending_remote_description_ ? pending_remote_description_.get()
2650 : current_remote_description_.get();
2651}
2652
2653const SessionDescriptionInterface*
2654SdpOfferAnswerHandler::current_local_description() const {
2655 RTC_DCHECK_RUN_ON(signaling_thread());
2656 return current_local_description_.get();
2657}
2658
2659const SessionDescriptionInterface*
2660SdpOfferAnswerHandler::current_remote_description() const {
2661 RTC_DCHECK_RUN_ON(signaling_thread());
2662 return current_remote_description_.get();
2663}
2664
2665const SessionDescriptionInterface*
2666SdpOfferAnswerHandler::pending_local_description() const {
2667 RTC_DCHECK_RUN_ON(signaling_thread());
2668 return pending_local_description_.get();
2669}
2670
2671const SessionDescriptionInterface*
2672SdpOfferAnswerHandler::pending_remote_description() const {
2673 RTC_DCHECK_RUN_ON(signaling_thread());
2674 return pending_remote_description_.get();
2675}
2676
2677PeerConnectionInterface::SignalingState SdpOfferAnswerHandler::signaling_state()
2678 const {
2679 RTC_DCHECK_RUN_ON(signaling_thread());
2680 return signaling_state_;
2681}
2682
2683void SdpOfferAnswerHandler::ChangeSignalingState(
2684 PeerConnectionInterface::SignalingState signaling_state) {
Markus Handell518669d2021-06-07 13:30:46 +02002685 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ChangeSignalingState");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002686 RTC_DCHECK_RUN_ON(signaling_thread());
2687 if (signaling_state_ == signaling_state) {
2688 return;
2689 }
2690 RTC_LOG(LS_INFO) << "Session: " << pc_->session_id() << " Old state: "
Harald Alvestrand31b03e92021-11-02 10:54:38 +00002691 << PeerConnectionInterface::AsString(signaling_state_)
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002692 << " New state: "
Harald Alvestrand31b03e92021-11-02 10:54:38 +00002693 << PeerConnectionInterface::AsString(signaling_state);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002694 signaling_state_ = signaling_state;
2695 pc_->Observer()->OnSignalingChange(signaling_state_);
2696}
2697
2698RTCError SdpOfferAnswerHandler::UpdateSessionState(
2699 SdpType type,
2700 cricket::ContentSource source,
Henrik Boströmf8187e02021-04-26 21:04:26 +02002701 const cricket::SessionDescription* description,
2702 const std::map<std::string, const cricket::ContentGroup*>&
2703 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002704 RTC_DCHECK_RUN_ON(signaling_thread());
2705
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002706 // If there's already a pending error then no state transition should
2707 // happen. But all call-sites should be verifying this before calling us!
Harald Alvestranda474fbf2020-10-01 16:47:23 +00002708 RTC_DCHECK(session_error() == SessionError::kNone);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002709
Taylor Brandstetterd0acbd82021-01-25 13:44:55 -08002710 // If this is answer-ish we're ready to let media flow.
2711 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
2712 EnableSending();
2713 }
2714
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002715 // Update the signaling state according to the specified state machine (see
2716 // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum).
2717 if (type == SdpType::kOffer) {
2718 ChangeSignalingState(source == cricket::CS_LOCAL
2719 ? PeerConnectionInterface::kHaveLocalOffer
2720 : PeerConnectionInterface::kHaveRemoteOffer);
2721 } else if (type == SdpType::kPrAnswer) {
2722 ChangeSignalingState(source == cricket::CS_LOCAL
2723 ? PeerConnectionInterface::kHaveLocalPrAnswer
2724 : PeerConnectionInterface::kHaveRemotePrAnswer);
2725 } else {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002726 RTC_DCHECK(type == SdpType::kAnswer);
2727 ChangeSignalingState(PeerConnectionInterface::kStable);
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00002728 if (ConfiguredForMedia()) {
2729 transceivers()->DiscardStableStates();
2730 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002731 }
2732
2733 // Update internal objects according to the session description's media
2734 // descriptions.
Tommicc7a3682021-05-04 14:59:38 +02002735 return PushdownMediaDescription(type, source, bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002736}
2737
2738bool SdpOfferAnswerHandler::ShouldFireNegotiationNeededEvent(
2739 uint32_t event_id) {
2740 RTC_DCHECK_RUN_ON(signaling_thread());
2741 // Plan B? Always fire to conform with useless legacy behavior.
2742 if (!IsUnifiedPlan()) {
2743 return true;
2744 }
2745 // The event ID has been invalidated. Either negotiation is no longer needed
2746 // or a newer negotiation needed event has been generated.
2747 if (event_id != negotiation_needed_event_id_) {
2748 return false;
2749 }
2750 // The chain is no longer empty, update negotiation needed when it becomes
2751 // empty. This should generate a newer negotiation needed event, making this
2752 // one obsolete.
2753 if (!operations_chain_->IsEmpty()) {
2754 // Since we just suppressed an event that would have been fired, if
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01002755 // negotiation is still needed by the time the chain becomes empty again,
2756 // we must make sure to generate another event if negotiation is needed
2757 // then. This happens when `is_negotiation_needed_` goes from false to
2758 // true, so we set it to false until UpdateNegotiationNeeded() is called.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002759 is_negotiation_needed_ = false;
2760 update_negotiation_needed_on_empty_chain_ = true;
2761 return false;
2762 }
2763 // We must not fire if the signaling state is no longer "stable". If
2764 // negotiation is still needed when we return to "stable", a new negotiation
2765 // needed event will be generated, so this one can safely be suppressed.
2766 if (signaling_state_ != PeerConnectionInterface::kStable) {
2767 return false;
2768 }
2769 // All checks have passed - please fire "negotiationneeded" now!
2770 return true;
2771}
2772
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002773rtc::scoped_refptr<StreamCollectionInterface>
2774SdpOfferAnswerHandler::local_streams() {
2775 RTC_DCHECK_RUN_ON(signaling_thread());
2776 RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified "
2777 "Plan SdpSemantics. Please use GetSenders "
2778 "instead.";
2779 return local_streams_;
2780}
2781
2782rtc::scoped_refptr<StreamCollectionInterface>
2783SdpOfferAnswerHandler::remote_streams() {
2784 RTC_DCHECK_RUN_ON(signaling_thread());
2785 RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified "
2786 "Plan SdpSemantics. Please use GetReceivers "
2787 "instead.";
2788 return remote_streams_;
2789}
2790
2791bool SdpOfferAnswerHandler::AddStream(MediaStreamInterface* local_stream) {
2792 RTC_DCHECK_RUN_ON(signaling_thread());
2793 RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan "
2794 "SdpSemantics. Please use AddTrack instead.";
2795 if (pc_->IsClosed()) {
2796 return false;
2797 }
Niels Möllerafb246b2022-04-20 14:26:50 +02002798 if (!CanAddLocalMediaStream(local_streams_.get(), local_stream)) {
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002799 return false;
2800 }
2801
Niels Möllere7cc8832022-01-04 15:20:03 +01002802 local_streams_->AddStream(
2803 rtc::scoped_refptr<MediaStreamInterface>(local_stream));
Mirko Bonadei9ff450d2021-08-02 10:56:33 +02002804 auto observer = std::make_unique<MediaStreamObserver>(
2805 local_stream,
2806 [this](AudioTrackInterface* audio_track,
2807 MediaStreamInterface* media_stream) {
2808 RTC_DCHECK_RUN_ON(signaling_thread());
2809 OnAudioTrackAdded(audio_track, media_stream);
2810 },
2811 [this](AudioTrackInterface* audio_track,
2812 MediaStreamInterface* media_stream) {
2813 RTC_DCHECK_RUN_ON(signaling_thread());
2814 OnAudioTrackRemoved(audio_track, media_stream);
2815 },
2816 [this](VideoTrackInterface* video_track,
2817 MediaStreamInterface* media_stream) {
2818 RTC_DCHECK_RUN_ON(signaling_thread());
2819 OnVideoTrackAdded(video_track, media_stream);
2820 },
2821 [this](VideoTrackInterface* video_track,
2822 MediaStreamInterface* media_stream) {
2823 RTC_DCHECK_RUN_ON(signaling_thread());
2824 OnVideoTrackRemoved(video_track, media_stream);
2825 });
2826 stream_observers_.push_back(std::move(observer));
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002827
2828 for (const auto& track : local_stream->GetAudioTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00002829 rtp_manager()->AddAudioTrack(track.get(), local_stream);
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002830 }
2831 for (const auto& track : local_stream->GetVideoTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00002832 rtp_manager()->AddVideoTrack(track.get(), local_stream);
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002833 }
2834
Henrik Boströmf7859892022-07-04 14:36:37 +02002835 pc_->legacy_stats()->AddStream(local_stream);
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002836 UpdateNegotiationNeeded();
2837 return true;
2838}
2839
2840void SdpOfferAnswerHandler::RemoveStream(MediaStreamInterface* local_stream) {
2841 RTC_DCHECK_RUN_ON(signaling_thread());
2842 RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified "
2843 "Plan SdpSemantics. Please use RemoveTrack "
2844 "instead.";
2845 TRACE_EVENT0("webrtc", "PeerConnection::RemoveStream");
2846 if (!pc_->IsClosed()) {
2847 for (const auto& track : local_stream->GetAudioTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00002848 rtp_manager()->RemoveAudioTrack(track.get(), local_stream);
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002849 }
2850 for (const auto& track : local_stream->GetVideoTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00002851 rtp_manager()->RemoveVideoTrack(track.get(), local_stream);
Harald Alvestrand6f04b652020-10-09 11:42:17 +00002852 }
2853 }
2854 local_streams_->RemoveStream(local_stream);
2855 stream_observers_.erase(
2856 std::remove_if(
2857 stream_observers_.begin(), stream_observers_.end(),
2858 [local_stream](const std::unique_ptr<MediaStreamObserver>& observer) {
2859 return observer->stream()->id().compare(local_stream->id()) == 0;
2860 }),
2861 stream_observers_.end());
2862
2863 if (pc_->IsClosed()) {
2864 return;
2865 }
2866 UpdateNegotiationNeeded();
2867}
2868
Harald Alvestrand763f5a92020-10-22 10:39:40 +00002869void SdpOfferAnswerHandler::OnAudioTrackAdded(AudioTrackInterface* track,
2870 MediaStreamInterface* stream) {
2871 if (pc_->IsClosed()) {
2872 return;
2873 }
2874 rtp_manager()->AddAudioTrack(track, stream);
2875 UpdateNegotiationNeeded();
2876}
2877
2878void SdpOfferAnswerHandler::OnAudioTrackRemoved(AudioTrackInterface* track,
2879 MediaStreamInterface* stream) {
2880 if (pc_->IsClosed()) {
2881 return;
2882 }
2883 rtp_manager()->RemoveAudioTrack(track, stream);
2884 UpdateNegotiationNeeded();
2885}
2886
2887void SdpOfferAnswerHandler::OnVideoTrackAdded(VideoTrackInterface* track,
2888 MediaStreamInterface* stream) {
2889 if (pc_->IsClosed()) {
2890 return;
2891 }
2892 rtp_manager()->AddVideoTrack(track, stream);
2893 UpdateNegotiationNeeded();
2894}
2895
2896void SdpOfferAnswerHandler::OnVideoTrackRemoved(VideoTrackInterface* track,
2897 MediaStreamInterface* stream) {
2898 if (pc_->IsClosed()) {
2899 return;
2900 }
2901 rtp_manager()->RemoveVideoTrack(track, stream);
2902 UpdateNegotiationNeeded();
2903}
2904
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002905RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) {
Markus Handell518669d2021-06-07 13:30:46 +02002906 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::Rollback");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002907 auto state = signaling_state();
2908 if (state != PeerConnectionInterface::kHaveLocalOffer &&
2909 state != PeerConnectionInterface::kHaveRemoteOffer) {
2910 return RTCError(RTCErrorType::INVALID_STATE,
Harald Alvestrand31b03e92021-11-02 10:54:38 +00002911 (rtc::StringBuilder("Called in wrong signalingState: ")
2912 << (PeerConnectionInterface::AsString(signaling_state())))
2913 .Release());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002914 }
2915 RTC_DCHECK_RUN_ON(signaling_thread());
2916 RTC_DCHECK(IsUnifiedPlan());
Henrik Boström0a162762022-05-02 15:47:52 +02002917 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
2918 now_receiving_transceivers;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002919 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_added_streams;
2920 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_removed_streams;
2921 std::vector<rtc::scoped_refptr<RtpReceiverInterface>> removed_receivers;
2922
Harald Alvestrande15fb152020-10-19 13:28:05 +00002923 for (auto&& transceivers_stable_state_pair : transceivers()->StableStates()) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002924 auto transceiver = transceivers_stable_state_pair.first;
2925 auto state = transceivers_stable_state_pair.second;
2926
Henrik Boström0a162762022-05-02 15:47:52 +02002927 if (state.did_set_fired_direction()) {
2928 // If this rollback triggers going from not receiving to receving again,
2929 // we need to fire "ontrack".
2930 bool previously_fired_direction_is_recv =
2931 transceiver->fired_direction().has_value() &&
2932 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction());
2933 bool currently_fired_direction_is_recv =
2934 state.fired_direction().has_value() &&
2935 RtpTransceiverDirectionHasRecv(state.fired_direction().value());
2936 if (!previously_fired_direction_is_recv &&
2937 currently_fired_direction_is_recv) {
2938 now_receiving_transceivers.push_back(transceiver);
2939 }
2940 transceiver->internal()->set_fired_direction(state.fired_direction());
2941 }
2942
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002943 if (state.remote_stream_ids()) {
2944 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
2945 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
2946 SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(),
2947 state.remote_stream_ids().value(),
2948 &added_streams, &removed_streams);
2949 all_added_streams.insert(all_added_streams.end(), added_streams.begin(),
2950 added_streams.end());
2951 all_removed_streams.insert(all_removed_streams.end(),
2952 removed_streams.begin(),
2953 removed_streams.end());
2954 if (!state.has_m_section() && !state.newly_created()) {
2955 continue;
2956 }
2957 }
2958
Henrik Boström0a162762022-05-02 15:47:52 +02002959 // Due to the above `continue` statement, the below code only runs if there
2960 // is a change in mid association (has_m_section), if the transceiver was
2961 // newly created (newly_created) or if remote streams were not set.
2962
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002963 RTC_DCHECK(transceiver->internal()->mid().has_value());
Harald Alvestrand19ebabc2022-04-28 13:31:17 +00002964 transceiver->internal()->ClearChannel();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002965
2966 if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer &&
2967 transceiver->receiver()) {
2968 removed_receivers.push_back(transceiver->receiver());
2969 }
2970 if (state.newly_created()) {
2971 if (transceiver->internal()->reused_for_addtrack()) {
2972 transceiver->internal()->set_created_by_addtrack(true);
2973 } else {
Henrik Boströma8ad11d2022-04-27 11:54:01 +02002974 transceiver->internal()->StopTransceiverProcedure();
Harald Alvestrande15fb152020-10-19 13:28:05 +00002975 transceivers()->Remove(transceiver);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002976 }
2977 }
Eldar Rello950d6b92021-04-06 22:38:00 +03002978 if (state.init_send_encodings()) {
2979 transceiver->internal()->sender_internal()->set_init_send_encodings(
2980 state.init_send_encodings().value());
2981 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002982 transceiver->internal()->sender_internal()->set_transport(nullptr);
2983 transceiver->internal()->receiver_internal()->set_transport(nullptr);
2984 transceiver->internal()->set_mid(state.mid());
2985 transceiver->internal()->set_mline_index(state.mline_index());
2986 }
Harald Alvestrandbc32c562022-02-09 12:08:47 +00002987 RTCError e = transport_controller_s()->RollbackTransports();
Taylor Brandstetter8591eff2021-08-11 14:56:38 -07002988 if (!e.ok()) {
2989 return e;
2990 }
Harald Alvestrande15fb152020-10-19 13:28:05 +00002991 transceivers()->DiscardStableStates();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00002992 pending_local_description_.reset();
2993 pending_remote_description_.reset();
2994 ChangeSignalingState(PeerConnectionInterface::kStable);
2995
2996 // Once all processing has finished, fire off callbacks.
Henrik Boström0a162762022-05-02 15:47:52 +02002997 for (const auto& transceiver : now_receiving_transceivers) {
2998 pc_->Observer()->OnTrack(transceiver);
2999 pc_->Observer()->OnAddTrack(transceiver->receiver(),
3000 transceiver->receiver()->streams());
3001 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003002 for (const auto& receiver : removed_receivers) {
3003 pc_->Observer()->OnRemoveTrack(receiver);
3004 }
3005 for (const auto& stream : all_added_streams) {
3006 pc_->Observer()->OnAddStream(stream);
3007 }
3008 for (const auto& stream : all_removed_streams) {
3009 pc_->Observer()->OnRemoveStream(stream);
3010 }
3011
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003012 // The assumption is that in case of implicit rollback
3013 // UpdateNegotiationNeeded gets called in SetRemoteDescription.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003014 if (desc_type == SdpType::kRollback) {
3015 UpdateNegotiationNeeded();
3016 if (is_negotiation_needed_) {
3017 // Legacy version.
3018 pc_->Observer()->OnRenegotiationNeeded();
3019 // Spec-compliant version; the event may get invalidated before firing.
3020 GenerateNegotiationNeededEvent();
3021 }
3022 }
3023 return RTCError::OK();
3024}
3025
3026bool SdpOfferAnswerHandler::IsUnifiedPlan() const {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003027 return pc_->IsUnifiedPlan();
3028}
3029
3030void SdpOfferAnswerHandler::OnOperationsChainEmpty() {
3031 RTC_DCHECK_RUN_ON(signaling_thread());
3032 if (pc_->IsClosed() || !update_negotiation_needed_on_empty_chain_)
3033 return;
3034 update_negotiation_needed_on_empty_chain_ = false;
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003035 // Firing when chain is empty is only supported in Unified Plan to avoid
3036 // Plan B regressions. (In Plan B, onnegotiationneeded is already broken
3037 // anyway, so firing it even more might just be confusing.)
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003038 if (IsUnifiedPlan()) {
3039 UpdateNegotiationNeeded();
3040 }
3041}
3042
3043absl::optional<bool> SdpOfferAnswerHandler::is_caller() {
3044 RTC_DCHECK_RUN_ON(signaling_thread());
3045 return is_caller_;
3046}
3047
3048bool SdpOfferAnswerHandler::HasNewIceCredentials() {
3049 RTC_DCHECK_RUN_ON(signaling_thread());
3050 return local_ice_credentials_to_replace_->HasIceCredentials();
3051}
3052
3053bool SdpOfferAnswerHandler::IceRestartPending(
3054 const std::string& content_name) const {
3055 RTC_DCHECK_RUN_ON(signaling_thread());
3056 return pending_ice_restarts_.find(content_name) !=
3057 pending_ice_restarts_.end();
3058}
3059
Harald Alvestrandf01bd6c2020-10-23 13:30:46 +00003060bool SdpOfferAnswerHandler::NeedsIceRestart(
3061 const std::string& content_name) const {
Tommic3257d02021-02-10 17:40:08 +00003062 return pc_->NeedsIceRestart(content_name);
Harald Alvestrandf01bd6c2020-10-23 13:30:46 +00003063}
3064
3065absl::optional<rtc::SSLRole> SdpOfferAnswerHandler::GetDtlsRole(
3066 const std::string& mid) const {
Harald Alvestrandbc32c562022-02-09 12:08:47 +00003067 RTC_DCHECK_RUN_ON(signaling_thread());
3068 return transport_controller_s()->GetDtlsRole(mid);
Harald Alvestrandf01bd6c2020-10-23 13:30:46 +00003069}
3070
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003071void SdpOfferAnswerHandler::UpdateNegotiationNeeded() {
3072 RTC_DCHECK_RUN_ON(signaling_thread());
3073 if (!IsUnifiedPlan()) {
3074 pc_->Observer()->OnRenegotiationNeeded();
3075 GenerateNegotiationNeededEvent();
3076 return;
3077 }
3078
3079 // In the spec, a task is queued here to run the following steps - this is
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003080 // meant to ensure we do not fire onnegotiationneeded prematurely if
3081 // multiple changes are being made at once. In order to support Chromium's
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003082 // implementation where the JavaScript representation of the PeerConnection
3083 // lives on a separate thread though, the queuing of a task is instead
3084 // performed by the PeerConnectionObserver posting from the signaling thread
3085 // to the JavaScript main thread that negotiation is needed. And because the
3086 // Operations Chain lives on the WebRTC signaling thread,
3087 // ShouldFireNegotiationNeededEvent() must be called before firing the event
3088 // to ensure the Operations Chain is still empty and the event has not been
3089 // invalidated.
3090
3091 // If connection's [[IsClosed]] slot is true, abort these steps.
3092 if (pc_->IsClosed())
3093 return;
3094
3095 // If connection's signaling state is not "stable", abort these steps.
3096 if (signaling_state() != PeerConnectionInterface::kStable)
3097 return;
3098
3099 // NOTE
3100 // The negotiation-needed flag will be updated once the state transitions to
3101 // "stable", as part of the steps for setting an RTCSessionDescription.
3102
3103 // If the result of checking if negotiation is needed is false, clear the
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003104 // negotiation-needed flag by setting connection's [[NegotiationNeeded]]
3105 // slot to false, and abort these steps.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003106 bool is_negotiation_needed = CheckIfNegotiationIsNeeded();
3107 if (!is_negotiation_needed) {
3108 is_negotiation_needed_ = false;
3109 // Invalidate any negotiation needed event that may previosuly have been
3110 // generated.
3111 ++negotiation_needed_event_id_;
3112 return;
3113 }
3114
3115 // If connection's [[NegotiationNeeded]] slot is already true, abort these
3116 // steps.
3117 if (is_negotiation_needed_)
3118 return;
3119
3120 // Set connection's [[NegotiationNeeded]] slot to true.
3121 is_negotiation_needed_ = true;
3122
3123 // Queue a task that runs the following steps:
3124 // If connection's [[IsClosed]] slot is true, abort these steps.
3125 // If connection's [[NegotiationNeeded]] slot is false, abort these steps.
3126 // Fire an event named negotiationneeded at connection.
3127 pc_->Observer()->OnRenegotiationNeeded();
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003128 // Fire the spec-compliant version; when ShouldFireNegotiationNeededEvent()
3129 // is used in the task queued by the observer, this event will only fire
3130 // when the chain is empty.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003131 GenerateNegotiationNeededEvent();
3132}
3133
3134bool SdpOfferAnswerHandler::CheckIfNegotiationIsNeeded() {
3135 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003136 // 1. If any implementation-specific negotiation is required, as described
3137 // at the start of this section, return true.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003138
3139 // 2. If connection.[[LocalIceCredentialsToReplace]] is not empty, return
3140 // true.
3141 if (local_ice_credentials_to_replace_->HasIceCredentials()) {
3142 return true;
3143 }
3144
3145 // 3. Let description be connection.[[CurrentLocalDescription]].
3146 const SessionDescriptionInterface* description = current_local_description();
3147 if (!description)
3148 return true;
3149
3150 // 4. If connection has created any RTCDataChannels, and no m= section in
3151 // description has been negotiated yet for data, return true.
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003152 if (data_channel_controller()->HasSctpDataChannels()) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003153 if (!cricket::GetFirstDataContent(description->description()->contents()))
3154 return true;
3155 }
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00003156 if (!ConfiguredForMedia()) {
3157 return false;
3158 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003159
3160 // 5. For each transceiver in connection's set of transceivers, perform the
3161 // following checks:
Harald Alvestrand85466662021-04-19 21:21:36 +00003162 for (const auto& transceiver : transceivers()->ListInternal()) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003163 const ContentInfo* current_local_msection =
Harald Alvestrand85466662021-04-19 21:21:36 +00003164 FindTransceiverMSection(transceiver, description);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003165
Harald Alvestrand85466662021-04-19 21:21:36 +00003166 const ContentInfo* current_remote_msection =
3167 FindTransceiverMSection(transceiver, current_remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003168
3169 // 5.4 If transceiver is stopped and is associated with an m= section,
3170 // but the associated m= section is not yet rejected in
3171 // connection.[[CurrentLocalDescription]] or
3172 // connection.[[CurrentRemoteDescription]], return true.
3173 if (transceiver->stopped()) {
3174 RTC_DCHECK(transceiver->stopping());
3175 if (current_local_msection && !current_local_msection->rejected &&
3176 ((current_remote_msection && !current_remote_msection->rejected) ||
3177 !current_remote_msection)) {
3178 return true;
3179 }
3180 continue;
3181 }
3182
3183 // 5.1 If transceiver.[[Stopping]] is true and transceiver.[[Stopped]] is
3184 // false, return true.
3185 if (transceiver->stopping() && !transceiver->stopped())
3186 return true;
3187
3188 // 5.2 If transceiver isn't stopped and isn't yet associated with an m=
3189 // section in description, return true.
3190 if (!current_local_msection)
3191 return true;
3192
3193 const MediaContentDescription* current_local_media_description =
3194 current_local_msection->media_description();
3195 // 5.3 If transceiver isn't stopped and is associated with an m= section
3196 // in description then perform the following checks:
3197
3198 // 5.3.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the
3199 // associated m= section in description either doesn't contain a single
3200 // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this
3201 // m= section, or the MSID values themselves, differ from what is in
3202 // transceiver.sender.[[AssociatedMediaStreamIds]], return true.
3203 if (RtpTransceiverDirectionHasSend(transceiver->direction())) {
3204 if (current_local_media_description->streams().size() == 0)
3205 return true;
3206
3207 std::vector<std::string> msection_msids;
3208 for (const auto& stream : current_local_media_description->streams()) {
3209 for (const std::string& msid : stream.stream_ids())
3210 msection_msids.push_back(msid);
3211 }
3212
3213 std::vector<std::string> transceiver_msids =
3214 transceiver->sender()->stream_ids();
3215 if (msection_msids.size() != transceiver_msids.size())
3216 return true;
3217
3218 absl::c_sort(transceiver_msids);
3219 absl::c_sort(msection_msids);
3220 if (transceiver_msids != msection_msids)
3221 return true;
3222 }
3223
3224 // 5.3.2 If description is of type "offer", and the direction of the
3225 // associated m= section in neither connection.[[CurrentLocalDescription]]
3226 // nor connection.[[CurrentRemoteDescription]] matches
3227 // transceiver.[[Direction]], return true.
3228 if (description->GetType() == SdpType::kOffer) {
3229 if (!current_remote_description())
3230 return true;
3231
3232 if (!current_remote_msection)
3233 return true;
3234
3235 RtpTransceiverDirection current_local_direction =
3236 current_local_media_description->direction();
3237 RtpTransceiverDirection current_remote_direction =
3238 current_remote_msection->media_description()->direction();
3239 if (transceiver->direction() != current_local_direction &&
3240 transceiver->direction() !=
3241 RtpTransceiverDirectionReversed(current_remote_direction)) {
3242 return true;
3243 }
3244 }
3245
3246 // 5.3.3 If description is of type "answer", and the direction of the
3247 // associated m= section in the description does not match
3248 // transceiver.[[Direction]] intersected with the offered direction (as
3249 // described in [JSEP] (section 5.3.1.)), return true.
3250 if (description->GetType() == SdpType::kAnswer) {
3251 if (!remote_description())
3252 return true;
3253
3254 const ContentInfo* offered_remote_msection =
Harald Alvestrand85466662021-04-19 21:21:36 +00003255 FindTransceiverMSection(transceiver, remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003256
3257 RtpTransceiverDirection offered_direction =
3258 offered_remote_msection
3259 ? offered_remote_msection->media_description()->direction()
3260 : RtpTransceiverDirection::kInactive;
3261
3262 if (current_local_media_description->direction() !=
3263 (RtpTransceiverDirectionIntersection(
3264 transceiver->direction(),
3265 RtpTransceiverDirectionReversed(offered_direction)))) {
3266 return true;
3267 }
3268 }
3269 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003270 // If all the preceding checks were performed and true was not returned,
3271 // nothing remains to be negotiated; return false.
3272 return false;
3273}
3274
3275void SdpOfferAnswerHandler::GenerateNegotiationNeededEvent() {
3276 RTC_DCHECK_RUN_ON(signaling_thread());
3277 ++negotiation_needed_event_id_;
3278 pc_->Observer()->OnNegotiationNeededEvent(negotiation_needed_event_id_);
3279}
3280
3281RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
3282 const SessionDescriptionInterface* sdesc,
Henrik Boströmf8187e02021-04-26 21:04:26 +02003283 cricket::ContentSource source,
3284 const std::map<std::string, const cricket::ContentGroup*>&
3285 bundle_groups_by_mid) {
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003286 // An assumption is that a check for session error is done at a higher level.
3287 RTC_DCHECK_EQ(SessionError::kNone, session_error());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003288
3289 if (!sdesc || !sdesc->description()) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003290 return RTCError(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003291 }
3292
3293 SdpType type = sdesc->GetType();
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003294 if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) ||
3295 (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003296 return RTCError(RTCErrorType::INVALID_STATE,
3297 (rtc::StringBuilder("Called in wrong state: ")
3298 << PeerConnectionInterface::AsString(signaling_state()))
3299 .Release());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003300 }
3301
3302 RTCError error = ValidateMids(*sdesc->description());
3303 if (!error.ok()) {
3304 return error;
3305 }
3306
3307 // Verify crypto settings.
3308 std::string crypto_error;
Harald Alvestrand0d018412021-11-04 13:52:31 +00003309 if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
3310 pc_->dtls_enabled()) {
3311 RTCError crypto_error = VerifyCrypto(
3312 sdesc->description(), pc_->dtls_enabled(), bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003313 if (!crypto_error.ok()) {
3314 return crypto_error;
3315 }
3316 }
3317
3318 // Verify ice-ufrag and ice-pwd.
Henrik Boströmf8187e02021-04-26 21:04:26 +02003319 if (!VerifyIceUfragPwdPresent(sdesc->description(), bundle_groups_by_mid)) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003320 return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutIceUfragPwd);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003321 }
3322
Henrik Boströmf8187e02021-04-26 21:04:26 +02003323 if (!pc_->ValidateBundleSettings(sdesc->description(),
3324 bundle_groups_by_mid)) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003325 return RTCError(RTCErrorType::INVALID_PARAMETER, kBundleWithoutRtcpMux);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003326 }
3327
3328 // TODO(skvlad): When the local rtcp-mux policy is Require, reject any
3329 // m-lines that do not rtcp-mux enabled.
3330
3331 // Verify m-lines in Answer when compared against Offer.
3332 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003333 // With an answer we want to compare the new answer session description
3334 // with the offer's session description from the current negotiation.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003335 const cricket::SessionDescription* offer_desc =
3336 (source == cricket::CS_LOCAL) ? remote_description()->description()
3337 : local_description()->description();
3338 if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) ||
3339 !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(),
3340 type)) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003341 return RTCError(RTCErrorType::INVALID_PARAMETER, kMlineMismatchInAnswer);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003342 }
3343 } else {
3344 // The re-offers should respect the order of m= sections in current
3345 // description. See RFC3264 Section 8 paragraph 4 for more details.
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003346 // With a re-offer, either the current local or current remote
3347 // descriptions could be the most up to date, so we would like to check
3348 // against both of them if they exist. It could be the case that one of
3349 // them has a 0 port for a media section, but the other does not. This is
3350 // important to check against in the case that we are recycling an m=
3351 // section.
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003352 const cricket::SessionDescription* current_desc = nullptr;
3353 const cricket::SessionDescription* secondary_current_desc = nullptr;
3354 if (local_description()) {
3355 current_desc = local_description()->description();
3356 if (remote_description()) {
3357 secondary_current_desc = remote_description()->description();
3358 }
3359 } else if (remote_description()) {
3360 current_desc = remote_description()->description();
3361 }
3362 if (current_desc &&
3363 !MediaSectionsInSameOrder(*current_desc, secondary_current_desc,
3364 *sdesc->description(), type)) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003365 return RTCError(RTCErrorType::INVALID_PARAMETER,
3366 kMlineMismatchInSubsequentOffer);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003367 }
3368 }
3369
3370 if (IsUnifiedPlan()) {
3371 // Ensure that each audio and video media section has at most one
3372 // "StreamParams". This will return an error if receiving a session
3373 // description from a "Plan B" endpoint which adds multiple tracks of the
3374 // same type. With Unified Plan, there can only be at most one track per
3375 // media section.
3376 for (const ContentInfo& content : sdesc->description()->contents()) {
3377 const MediaContentDescription& desc = *content.media_description();
3378 if ((desc.type() == cricket::MEDIA_TYPE_AUDIO ||
3379 desc.type() == cricket::MEDIA_TYPE_VIDEO) &&
3380 desc.streams().size() > 1u) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003381 return RTCError(
3382 RTCErrorType::INVALID_PARAMETER,
3383 "Media section has more than one track specified with a=ssrc lines "
3384 "which is not supported with Unified Plan.");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003385 }
3386 }
3387 }
3388
3389 return RTCError::OK();
3390}
3391
3392RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels(
3393 cricket::ContentSource source,
3394 const SessionDescriptionInterface& new_session,
3395 const SessionDescriptionInterface* old_local_description,
Henrik Boströmf8187e02021-04-26 21:04:26 +02003396 const SessionDescriptionInterface* old_remote_description,
3397 const std::map<std::string, const cricket::ContentGroup*>&
3398 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 13:30:46 +02003399 TRACE_EVENT0("webrtc",
3400 "SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003401 RTC_DCHECK_RUN_ON(signaling_thread());
3402 RTC_DCHECK(IsUnifiedPlan());
3403
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003404 if (new_session.GetType() == SdpType::kOffer) {
Henrik Boströmf8187e02021-04-26 21:04:26 +02003405 // If the BUNDLE policy is max-bundle, then we know for sure that all
Tomas Gunnarsson1c7c09b2022-01-12 13:11:04 +01003406 // transports will be bundled from the start. Return an error if
3407 // max-bundle is specified but the session description does not have a
3408 // BUNDLE group.
Henrik Boströmf8187e02021-04-26 21:04:26 +02003409 if (pc_->configuration()->bundle_policy ==
3410 PeerConnectionInterface::kBundlePolicyMaxBundle &&
3411 bundle_groups_by_mid.empty()) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003412 return RTCError(
3413 RTCErrorType::INVALID_PARAMETER,
3414 "max-bundle configured but session description has no BUNDLE group");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003415 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003416 }
3417
3418 const ContentInfos& new_contents = new_session.description()->contents();
3419 for (size_t i = 0; i < new_contents.size(); ++i) {
3420 const cricket::ContentInfo& new_content = new_contents[i];
3421 cricket::MediaType media_type = new_content.media_description()->type();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003422 mid_generator_.AddKnownId(new_content.name);
Henrik Boströmf8187e02021-04-26 21:04:26 +02003423 auto it = bundle_groups_by_mid.find(new_content.name);
3424 const cricket::ContentGroup* bundle_group =
3425 it != bundle_groups_by_mid.end() ? it->second : nullptr;
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003426 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
3427 media_type == cricket::MEDIA_TYPE_VIDEO) {
3428 const cricket::ContentInfo* old_local_content = nullptr;
3429 if (old_local_description &&
3430 i < old_local_description->description()->contents().size()) {
3431 old_local_content =
3432 &old_local_description->description()->contents()[i];
3433 }
3434 const cricket::ContentInfo* old_remote_content = nullptr;
3435 if (old_remote_description &&
3436 i < old_remote_description->description()->contents().size()) {
3437 old_remote_content =
3438 &old_remote_description->description()->contents()[i];
3439 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003440 auto transceiver_or_error =
3441 AssociateTransceiver(source, new_session.GetType(), i, new_content,
3442 old_local_content, old_remote_content);
3443 if (!transceiver_or_error.ok()) {
Henrik Boströma8ad11d2022-04-27 11:54:01 +02003444 // In the case where a transceiver is rejected locally prior to being
3445 // associated, we don't expect to find a transceiver, but might find it
3446 // in the case where state is still "stopping", not "stopped".
Harald Alvestrand09bd9ba2020-10-09 08:13:30 +00003447 if (new_content.rejected) {
3448 continue;
3449 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003450 return transceiver_or_error.MoveError();
3451 }
3452 auto transceiver = transceiver_or_error.MoveValue();
3453 RTCError error =
3454 UpdateTransceiverChannel(transceiver, new_content, bundle_group);
Henrik Boströma8ad11d2022-04-27 11:54:01 +02003455 // Handle locally rejected content. This code path is only needed for apps
3456 // that SDP munge. Remote rejected content is handled in
3457 // ApplyRemoteDescriptionUpdateTransceiverState().
3458 if (source == cricket::ContentSource::CS_LOCAL && new_content.rejected) {
3459 // Local offer.
3460 if (new_session.GetType() == SdpType::kOffer) {
3461 // If the RtpTransceiver API was used, it would already have made the
3462 // transceiver stopping. But if the rejection was caused by SDP
3463 // munging then we need to ensure the transceiver is stopping here.
3464 if (!transceiver->internal()->stopping()) {
3465 transceiver->internal()->StopStandard();
3466 }
3467 RTC_DCHECK(transceiver->internal()->stopping());
3468 } else {
3469 // Local answer.
3470 RTC_DCHECK(new_session.GetType() == SdpType::kAnswer ||
3471 new_session.GetType() == SdpType::kPrAnswer);
3472 // When RtpTransceiver API is used, rejection happens in the offer and
3473 // the transceiver will already be stopped at local answer time
3474 // (calling stop between SRD(offer) and SLD(answer) would not reject
3475 // the content in the answer - instead this would trigger a follow-up
3476 // O/A exchange). So if the content was rejected but the transceiver
3477 // is not already stopped, SDP munging has happened and we need to
3478 // ensure the transceiver is stopped.
3479 if (!transceiver->internal()->stopped()) {
3480 transceiver->internal()->StopTransceiverProcedure();
3481 }
3482 RTC_DCHECK(transceiver->internal()->stopped());
3483 }
3484 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003485 if (!error.ok()) {
3486 return error;
3487 }
3488 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3489 if (pc_->GetDataMid() && new_content.name != *(pc_->GetDataMid())) {
3490 // Ignore all but the first data section.
3491 RTC_LOG(LS_INFO) << "Ignoring data media section with MID="
3492 << new_content.name;
3493 continue;
3494 }
3495 RTCError error = UpdateDataChannel(source, new_content, bundle_group);
3496 if (!error.ok()) {
3497 return error;
3498 }
Philipp Hancke4e8c1152020-10-13 12:43:15 +02003499 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
3500 RTC_LOG(LS_INFO) << "Ignoring unsupported media type";
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003501 } else {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003502 return RTCError(RTCErrorType::INTERNAL_ERROR, "Unknown section type.");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003503 }
3504 }
3505
3506 return RTCError::OK();
3507}
3508
3509RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
3510SdpOfferAnswerHandler::AssociateTransceiver(
3511 cricket::ContentSource source,
3512 SdpType type,
3513 size_t mline_index,
3514 const ContentInfo& content,
3515 const ContentInfo* old_local_content,
3516 const ContentInfo* old_remote_content) {
Markus Handell518669d2021-06-07 13:30:46 +02003517 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AssociateTransceiver");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003518 RTC_DCHECK(IsUnifiedPlan());
Tomas Gunnarsson36992362020-10-05 21:41:36 +02003519#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003520 // If this is an offer then the m= section might be recycled. If the m=
3521 // section is being recycled (defined as: rejected in the current local or
3522 // remote description and not rejected in new description), the transceiver
Harald Alvestrande15fb152020-10-19 13:28:05 +00003523 // should have been removed by RemoveStoppedtransceivers()->
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003524 if (IsMediaSectionBeingRecycled(type, content, old_local_content,
3525 old_remote_content)) {
3526 const std::string& old_mid =
3527 (old_local_content && old_local_content->rejected)
3528 ? old_local_content->name
3529 : old_remote_content->name;
Harald Alvestrande15fb152020-10-19 13:28:05 +00003530 auto old_transceiver = transceivers()->FindByMid(old_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003531 // The transceiver should be disassociated in RemoveStoppedTransceivers()
3532 RTC_DCHECK(!old_transceiver);
3533 }
Tomas Gunnarsson36992362020-10-05 21:41:36 +02003534#endif
3535
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003536 const MediaContentDescription* media_desc = content.media_description();
Harald Alvestrande15fb152020-10-19 13:28:05 +00003537 auto transceiver = transceivers()->FindByMid(content.name);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003538 if (source == cricket::CS_LOCAL) {
3539 // Find the RtpTransceiver that corresponds to this m= section, using the
3540 // mapping between transceivers and m= section indices established when
3541 // creating the offer.
3542 if (!transceiver) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00003543 transceiver = transceivers()->FindByMLineIndex(mline_index);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003544 }
3545 if (!transceiver) {
Harald Alvestrand09bd9ba2020-10-09 08:13:30 +00003546 // This may happen normally when media sections are rejected.
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003547 return RTCError(RTCErrorType::INVALID_PARAMETER,
3548 "Transceiver not found based on m-line index");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003549 }
3550 } else {
3551 RTC_DCHECK_EQ(source, cricket::CS_REMOTE);
3552 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers
3553 // of the same type...
3554 // When simulcast is requested, a transceiver cannot be associated because
3555 // AddTrack cannot be called to initialize it.
3556 if (!transceiver &&
3557 RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
3558 !media_desc->HasSimulcast()) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003559 transceiver = FindAvailableTransceiverToReceive(media_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003560 }
3561 // If no RtpTransceiver was found in the previous step, create one with a
3562 // recvonly direction.
3563 if (!transceiver) {
3564 RTC_LOG(LS_INFO) << "Adding "
3565 << cricket::MediaTypeToString(media_desc->type())
3566 << " transceiver for MID=" << content.name
3567 << " at i=" << mline_index
3568 << " in response to the remote description.";
3569 std::string sender_id = rtc::CreateRandomUuid();
3570 std::vector<RtpEncodingParameters> send_encodings =
3571 GetSendEncodingsFromRemoteDescription(*media_desc);
Harald Alvestrande15fb152020-10-19 13:28:05 +00003572 auto sender = rtp_manager()->CreateSender(media_desc->type(), sender_id,
3573 nullptr, {}, send_encodings);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003574 std::string receiver_id;
3575 if (!media_desc->streams().empty()) {
3576 receiver_id = media_desc->streams()[0].id;
3577 } else {
3578 receiver_id = rtc::CreateRandomUuid();
3579 }
Harald Alvestrande15fb152020-10-19 13:28:05 +00003580 auto receiver =
3581 rtp_manager()->CreateReceiver(media_desc->type(), receiver_id);
3582 transceiver = rtp_manager()->CreateAndAddTransceiver(sender, receiver);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003583 transceiver->internal()->set_direction(
3584 RtpTransceiverDirection::kRecvOnly);
3585 if (type == SdpType::kOffer) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00003586 transceivers()->StableState(transceiver)->set_newly_created();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003587 }
3588 }
Tomas Gunnarsson36992362020-10-05 21:41:36 +02003589
3590 RTC_DCHECK(transceiver);
3591
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003592 // Check if the offer indicated simulcast but the answer rejected it.
3593 // This can happen when simulcast is not supported on the remote party.
Lennart Grahl0d0ed762021-05-17 16:06:37 +02003594 if (SimulcastIsRejected(old_local_content, *media_desc,
3595 pc_->GetCryptoOptions()
3596 .srtp.enable_encrypted_rtp_header_extensions)) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003597 RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true);
3598 RTCError error =
3599 DisableSimulcastInSender(transceiver->internal()->sender_internal());
3600 if (!error.ok()) {
3601 RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast.";
3602 return std::move(error);
3603 }
3604 }
3605 }
Tomas Gunnarsson36992362020-10-05 21:41:36 +02003606
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003607 if (transceiver->media_type() != media_desc->type()) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003608 return RTCError(RTCErrorType::INVALID_PARAMETER,
3609 "Transceiver type does not match media description type.");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003610 }
Tomas Gunnarsson36992362020-10-05 21:41:36 +02003611
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003612 if (media_desc->HasSimulcast()) {
3613 std::vector<SimulcastLayer> layers =
3614 source == cricket::CS_LOCAL
3615 ? media_desc->simulcast_description().send_layers().GetAllLayers()
3616 : media_desc->simulcast_description()
3617 .receive_layers()
3618 .GetAllLayers();
3619 RTCError error = UpdateSimulcastLayerStatusInSender(
3620 layers, transceiver->internal()->sender_internal());
3621 if (!error.ok()) {
3622 RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers.";
3623 return std::move(error);
3624 }
3625 }
3626 if (type == SdpType::kOffer) {
3627 bool state_changes = transceiver->internal()->mid() != content.name ||
3628 transceiver->internal()->mline_index() != mline_index;
3629 if (state_changes) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003630 transceivers()
Harald Alvestrande15fb152020-10-19 13:28:05 +00003631 ->StableState(transceiver)
Harald Alvestrand38b768c2020-09-29 11:54:05 +00003632 ->SetMSectionIfUnset(transceiver->internal()->mid(),
3633 transceiver->internal()->mline_index());
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003634 }
3635 }
3636 // Associate the found or created RtpTransceiver with the m= section by
3637 // setting the value of the RtpTransceiver's mid property to the MID of the m=
3638 // section, and establish a mapping between the transceiver and the index of
3639 // the m= section.
3640 transceiver->internal()->set_mid(content.name);
3641 transceiver->internal()->set_mline_index(mline_index);
3642 return std::move(transceiver);
3643}
3644
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003645RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel(
3646 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
3647 transceiver,
3648 const cricket::ContentInfo& content,
3649 const cricket::ContentGroup* bundle_group) {
Markus Handell518669d2021-06-07 13:30:46 +02003650 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateTransceiverChannel");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003651 RTC_DCHECK(IsUnifiedPlan());
3652 RTC_DCHECK(transceiver);
3653 cricket::ChannelInterface* channel = transceiver->internal()->channel();
3654 if (content.rejected) {
3655 if (channel) {
Harald Alvestrand19ebabc2022-04-28 13:31:17 +00003656 transceiver->internal()->ClearChannel();
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003657 }
3658 } else {
3659 if (!channel) {
Harald Alvestrand8f429922022-05-04 10:32:30 +00003660 auto error = transceiver->internal()->CreateChannel(
3661 content.name, pc_->call_ptr(), pc_->configuration()->media_config,
3662 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
3663 video_options(), video_bitrate_allocator_factory_.get(),
3664 [&](absl::string_view mid) {
Harald Alvestrand3af79d12022-04-29 15:04:58 +00003665 RTC_DCHECK_RUN_ON(network_thread());
3666 return transport_controller_n()->GetRtpTransport(mid);
3667 });
Harald Alvestrand8f429922022-05-04 10:32:30 +00003668 if (!error.ok()) {
3669 return error;
3670 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003671 }
3672 }
3673 return RTCError::OK();
3674}
3675
3676RTCError SdpOfferAnswerHandler::UpdateDataChannel(
3677 cricket::ContentSource source,
3678 const cricket::ContentInfo& content,
3679 const cricket::ContentGroup* bundle_group) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003680 if (content.rejected) {
Florent Castellidcb9ffc2021-06-29 14:58:23 +02003681 RTC_LOG(LS_INFO) << "Rejected data channel transport with mid="
3682 << content.mid();
3683
3684 rtc::StringBuilder sb;
3685 sb << "Rejected data channel transport with mid=" << content.mid();
3686 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
3687 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
3688 DestroyDataChannelTransport(error);
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003689 } else {
Harald Alvestrand7af57c62021-04-16 11:12:14 +00003690 if (!data_channel_controller()->data_channel_transport()) {
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003691 RTC_LOG(LS_INFO) << "Creating data channel, mid=" << content.mid();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003692 if (!CreateDataChannel(content.name)) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01003693 return RTCError(RTCErrorType::INTERNAL_ERROR,
3694 "Failed to create data channel.");
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003695 }
3696 }
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00003697 }
3698 return RTCError::OK();
3699}
3700
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003701bool SdpOfferAnswerHandler::ExpectSetLocalDescription(SdpType type) {
3702 PeerConnectionInterface::SignalingState state = signaling_state();
3703 if (type == SdpType::kOffer) {
3704 return (state == PeerConnectionInterface::kStable) ||
3705 (state == PeerConnectionInterface::kHaveLocalOffer);
3706 } else {
3707 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3708 return (state == PeerConnectionInterface::kHaveRemoteOffer) ||
3709 (state == PeerConnectionInterface::kHaveLocalPrAnswer);
3710 }
3711}
3712
3713bool SdpOfferAnswerHandler::ExpectSetRemoteDescription(SdpType type) {
3714 PeerConnectionInterface::SignalingState state = signaling_state();
3715 if (type == SdpType::kOffer) {
3716 return (state == PeerConnectionInterface::kStable) ||
3717 (state == PeerConnectionInterface::kHaveRemoteOffer);
3718 } else {
3719 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3720 return (state == PeerConnectionInterface::kHaveLocalOffer) ||
3721 (state == PeerConnectionInterface::kHaveRemotePrAnswer);
3722 }
3723}
3724
3725void SdpOfferAnswerHandler::FillInMissingRemoteMids(
3726 cricket::SessionDescription* new_remote_description) {
3727 RTC_DCHECK_RUN_ON(signaling_thread());
3728 RTC_DCHECK(new_remote_description);
3729 const cricket::ContentInfos no_infos;
3730 const cricket::ContentInfos& local_contents =
3731 (local_description() ? local_description()->description()->contents()
3732 : no_infos);
3733 const cricket::ContentInfos& remote_contents =
3734 (remote_description() ? remote_description()->description()->contents()
3735 : no_infos);
3736 for (size_t i = 0; i < new_remote_description->contents().size(); ++i) {
3737 cricket::ContentInfo& content = new_remote_description->contents()[i];
3738 if (!content.name.empty()) {
3739 continue;
3740 }
3741 std::string new_mid;
3742 absl::string_view source_explanation;
3743 if (IsUnifiedPlan()) {
3744 if (i < local_contents.size()) {
3745 new_mid = local_contents[i].name;
3746 source_explanation = "from the matching local media section";
3747 } else if (i < remote_contents.size()) {
3748 new_mid = remote_contents[i].name;
3749 source_explanation = "from the matching previous remote media section";
3750 } else {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003751 new_mid = mid_generator_.GenerateString();
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003752 source_explanation = "generated just now";
3753 }
3754 } else {
3755 new_mid = std::string(
3756 GetDefaultMidForPlanB(content.media_description()->type()));
3757 source_explanation = "to match pre-existing behavior";
3758 }
3759 RTC_DCHECK(!new_mid.empty());
3760 content.name = new_mid;
3761 new_remote_description->transport_infos()[i].content_name = new_mid;
3762 RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i
3763 << " is missing an a=mid line. Filling in the value '"
3764 << new_mid << "' " << source_explanation << ".";
3765 }
3766}
3767
3768rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
3769SdpOfferAnswerHandler::FindAvailableTransceiverToReceive(
3770 cricket::MediaType media_type) const {
3771 RTC_DCHECK_RUN_ON(signaling_thread());
3772 RTC_DCHECK(IsUnifiedPlan());
3773 // From JSEP section 5.10 (Applying a Remote Description):
3774 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of
3775 // the same type that were added to the PeerConnection by addTrack and are not
3776 // associated with any m= section and are not stopped, find the first such
3777 // RtpTransceiver.
Harald Alvestrande15fb152020-10-19 13:28:05 +00003778 for (auto transceiver : transceivers()->List()) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003779 if (transceiver->media_type() == media_type &&
3780 transceiver->internal()->created_by_addtrack() && !transceiver->mid() &&
3781 !transceiver->stopped()) {
3782 return transceiver;
3783 }
3784 }
3785 return nullptr;
3786}
3787
3788const cricket::ContentInfo*
3789SdpOfferAnswerHandler::FindMediaSectionForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:36 +00003790 const RtpTransceiver* transceiver,
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003791 const SessionDescriptionInterface* sdesc) const {
3792 RTC_DCHECK_RUN_ON(signaling_thread());
3793 RTC_DCHECK(transceiver);
3794 RTC_DCHECK(sdesc);
3795 if (IsUnifiedPlan()) {
Harald Alvestrand85466662021-04-19 21:21:36 +00003796 if (!transceiver->mid()) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003797 // This transceiver is not associated with a media section yet.
3798 return nullptr;
3799 }
Harald Alvestrand85466662021-04-19 21:21:36 +00003800 return sdesc->description()->GetContentByName(*transceiver->mid());
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003801 } else {
3802 // Plan B only allows at most one audio and one video section, so use the
3803 // first media section of that type.
3804 return cricket::GetFirstMediaContent(sdesc->description()->contents(),
3805 transceiver->media_type());
3806 }
3807}
3808
3809void SdpOfferAnswerHandler::GetOptionsForOffer(
3810 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
3811 cricket::MediaSessionOptions* session_options) {
3812 RTC_DCHECK_RUN_ON(signaling_thread());
3813 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
3814
3815 if (IsUnifiedPlan()) {
3816 GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options);
3817 } else {
3818 GetOptionsForPlanBOffer(offer_answer_options, session_options);
3819 }
3820
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003821 // Apply ICE restart flag and renomination flag.
3822 bool ice_restart = offer_answer_options.ice_restart || HasNewIceCredentials();
3823 for (auto& options : session_options->media_description_options) {
3824 options.transport_options.ice_restart = ice_restart;
3825 options.transport_options.enable_ice_renomination =
3826 pc_->configuration()->enable_ice_renomination;
3827 }
3828
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003829 session_options->rtcp_cname = rtcp_cname_;
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003830 session_options->crypto_options = pc_->GetCryptoOptions();
3831 session_options->pooled_ice_credentials =
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02003832 context_->network_thread()->BlockingCall(
Niels Möller4bab23f2021-01-18 09:24:33 +01003833 [this] { return port_allocator()->GetPooledIceCredentials(); });
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003834 session_options->offer_extmap_allow_mixed =
3835 pc_->configuration()->offer_extmap_allow_mixed;
3836
3837 // Allow fallback for using obsolete SCTP syntax.
Artem Titov880fa812021-07-30 22:30:23 +02003838 // Note that the default in `session_options` is true, while
3839 // the default in `options` is false.
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003840 session_options->use_obsolete_sctp_sdp =
3841 offer_answer_options.use_obsolete_sctp_sdp;
3842}
3843
3844void SdpOfferAnswerHandler::GetOptionsForPlanBOffer(
3845 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
3846 cricket::MediaSessionOptions* session_options) {
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00003847 bool offer_new_data_description =
3848 data_channel_controller()->HasDataChannels();
3849 bool send_audio = false;
3850 bool send_video = false;
3851 bool recv_audio = false;
3852 bool recv_video = false;
3853 if (ConfiguredForMedia()) {
3854 // Figure out transceiver directional preferences.
3855 send_audio =
3856 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
3857 send_video =
3858 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003859
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00003860 // By default, generate sendrecv/recvonly m= sections.
3861 recv_audio = true;
3862 recv_video = true;
3863 }
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003864 // By default, only offer a new m= section if we have media to send with it.
3865 bool offer_new_audio_description = send_audio;
3866 bool offer_new_video_description = send_video;
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00003867 if (ConfiguredForMedia()) {
3868 // The "offer_to_receive_X" options allow those defaults to be overridden.
3869 if (offer_answer_options.offer_to_receive_audio !=
3870 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
3871 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
3872 offer_new_audio_description =
3873 offer_new_audio_description ||
3874 (offer_answer_options.offer_to_receive_audio > 0);
3875 }
3876 if (offer_answer_options.offer_to_receive_video !=
3877 RTCOfferAnswerOptions::kUndefined) {
3878 recv_video = (offer_answer_options.offer_to_receive_video > 0);
3879 offer_new_video_description =
3880 offer_new_video_description ||
3881 (offer_answer_options.offer_to_receive_video > 0);
3882 }
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003883 }
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003884 absl::optional<size_t> audio_index;
3885 absl::optional<size_t> video_index;
3886 absl::optional<size_t> data_index;
3887 // If a current description exists, generate m= sections in the same order,
3888 // using the first audio/video/data section that appears and rejecting
3889 // extraneous ones.
3890 if (local_description()) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003891 GenerateMediaDescriptionOptions(
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003892 local_description(),
3893 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
3894 RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
3895 &audio_index, &video_index, &data_index, session_options);
3896 }
3897
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00003898 if (ConfiguredForMedia()) {
3899 // Add audio/video/data m= sections to the end if needed.
3900 if (!audio_index && offer_new_audio_description) {
3901 cricket::MediaDescriptionOptions options(
3902 cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
3903 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false);
3904 options.header_extensions =
3905 media_engine()->voice().GetRtpHeaderExtensions();
3906 session_options->media_description_options.push_back(options);
3907 audio_index = session_options->media_description_options.size() - 1;
3908 }
3909 if (!video_index && offer_new_video_description) {
3910 cricket::MediaDescriptionOptions options(
3911 cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
3912 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false);
3913 options.header_extensions =
3914 media_engine()->video().GetRtpHeaderExtensions();
3915 session_options->media_description_options.push_back(options);
3916 video_index = session_options->media_description_options.size() - 1;
3917 }
3918 cricket::MediaDescriptionOptions* audio_media_description_options =
3919 !audio_index
3920 ? nullptr
3921 : &session_options->media_description_options[*audio_index];
3922 cricket::MediaDescriptionOptions* video_media_description_options =
3923 !video_index
3924 ? nullptr
3925 : &session_options->media_description_options[*video_index];
3926
3927 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
3928 audio_media_description_options,
3929 video_media_description_options,
3930 offer_answer_options.num_simulcast_layers);
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003931 }
3932 if (!data_index && offer_new_data_description) {
3933 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00003934 GetMediaDescriptionOptionsForActiveData(cricket::CN_DATA));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003935 }
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003936}
3937
3938void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanOffer(
3939 const RTCOfferAnswerOptions& offer_answer_options,
3940 cricket::MediaSessionOptions* session_options) {
3941 // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial
3942 // Offers) and 5.2.2 (Subsequent Offers).
3943 RTC_DCHECK_EQ(session_options->media_description_options.size(), 0);
3944 const ContentInfos no_infos;
3945 const ContentInfos& local_contents =
3946 (local_description() ? local_description()->description()->contents()
3947 : no_infos);
3948 const ContentInfos& remote_contents =
3949 (remote_description() ? remote_description()->description()->contents()
3950 : no_infos);
3951 // The mline indices that can be recycled. New transceivers should reuse these
3952 // slots first.
3953 std::queue<size_t> recycleable_mline_indices;
3954 // First, go through each media section that exists in either the local or
3955 // remote description and generate a media section in this offer for the
3956 // associated transceiver. If a media section can be recycled, generate a
3957 // default, rejected media section here that can be later overwritten.
3958 for (size_t i = 0;
3959 i < std::max(local_contents.size(), remote_contents.size()); ++i) {
Artem Titov880fa812021-07-30 22:30:23 +02003960 // Either `local_content` or `remote_content` is non-null.
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003961 const ContentInfo* local_content =
3962 (i < local_contents.size() ? &local_contents[i] : nullptr);
3963 const ContentInfo* current_local_content =
3964 GetContentByIndex(current_local_description(), i);
3965 const ContentInfo* remote_content =
3966 (i < remote_contents.size() ? &remote_contents[i] : nullptr);
3967 const ContentInfo* current_remote_content =
3968 GetContentByIndex(current_remote_description(), i);
3969 bool had_been_rejected =
3970 (current_local_content && current_local_content->rejected) ||
3971 (current_remote_content && current_remote_content->rejected);
3972 const std::string& mid =
3973 (local_content ? local_content->name : remote_content->name);
3974 cricket::MediaType media_type =
3975 (local_content ? local_content->media_description()->type()
3976 : remote_content->media_description()->type());
3977 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
3978 media_type == cricket::MEDIA_TYPE_VIDEO) {
3979 // A media section is considered eligible for recycling if it is marked as
3980 // rejected in either the current local or current remote description.
Harald Alvestrande15fb152020-10-19 13:28:05 +00003981 auto transceiver = transceivers()->FindByMid(mid);
Harald Alvestrandc06e3742020-10-01 10:23:33 +00003982 if (!transceiver) {
3983 // No associated transceiver. The media section has been stopped.
3984 recycleable_mline_indices.push(i);
3985 session_options->media_description_options.push_back(
3986 cricket::MediaDescriptionOptions(media_type, mid,
3987 RtpTransceiverDirection::kInactive,
3988 /*stopped=*/true));
3989 } else {
3990 // NOTE: a stopping transceiver should be treated as a stopped one in
3991 // createOffer as specified in
3992 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
3993 if (had_been_rejected && transceiver->stopping()) {
3994 session_options->media_description_options.push_back(
3995 cricket::MediaDescriptionOptions(
3996 transceiver->media_type(), mid,
3997 RtpTransceiverDirection::kInactive,
3998 /*stopped=*/true));
3999 recycleable_mline_indices.push(i);
4000 } else {
4001 session_options->media_description_options.push_back(
4002 GetMediaDescriptionOptionsForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:36 +00004003 transceiver->internal(), mid,
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004004 /*is_create_offer=*/true));
4005 // CreateOffer shouldn't really cause any state changes in
4006 // PeerConnection, but we need a way to match new transceivers to new
4007 // media sections in SetLocalDescription and JSEP specifies this is
4008 // done by recording the index of the media section generated for the
4009 // transceiver in the offer.
4010 transceiver->internal()->set_mline_index(i);
4011 }
4012 }
Philipp Hancke4e8c1152020-10-13 12:43:15 +02004013 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4014 RTC_DCHECK(local_content->rejected);
4015 session_options->media_description_options.push_back(
4016 cricket::MediaDescriptionOptions(media_type, mid,
4017 RtpTransceiverDirection::kInactive,
4018 /*stopped=*/true));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004019 } else {
4020 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4021 if (had_been_rejected) {
4022 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004023 GetMediaDescriptionOptionsForRejectedData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004024 } else {
4025 RTC_CHECK(pc_->GetDataMid());
4026 if (mid == *(pc_->GetDataMid())) {
4027 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004028 GetMediaDescriptionOptionsForActiveData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004029 } else {
4030 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004031 GetMediaDescriptionOptionsForRejectedData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004032 }
4033 }
4034 }
4035 }
4036
4037 // Next, look for transceivers that are newly added (that is, are not stopped
4038 // and not associated). Reuse media sections marked as recyclable first,
4039 // otherwise append to the end of the offer. New media sections should be
4040 // added in the order they were added to the PeerConnection.
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004041 if (ConfiguredForMedia()) {
4042 for (const auto& transceiver : transceivers()->ListInternal()) {
4043 if (transceiver->mid() || transceiver->stopping()) {
4044 continue;
4045 }
4046 size_t mline_index;
4047 if (!recycleable_mline_indices.empty()) {
4048 mline_index = recycleable_mline_indices.front();
4049 recycleable_mline_indices.pop();
4050 session_options->media_description_options[mline_index] =
4051 GetMediaDescriptionOptionsForTransceiver(
4052 transceiver, mid_generator_.GenerateString(),
4053 /*is_create_offer=*/true);
4054 } else {
4055 mline_index = session_options->media_description_options.size();
4056 session_options->media_description_options.push_back(
4057 GetMediaDescriptionOptionsForTransceiver(
4058 transceiver, mid_generator_.GenerateString(),
4059 /*is_create_offer=*/true));
4060 }
4061 // See comment above for why CreateOffer changes the transceiver's state.
4062 transceiver->set_mline_index(mline_index);
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004063 }
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004064 }
4065 // Lastly, add a m-section if we have local data channels and an m section
4066 // does not already exist.
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004067 if (!pc_->GetDataMid() && data_channel_controller()->HasDataChannels()) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004068 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004069 GetMediaDescriptionOptionsForActiveData(
4070 mid_generator_.GenerateString()));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004071 }
4072}
4073
4074void SdpOfferAnswerHandler::GetOptionsForAnswer(
4075 const RTCOfferAnswerOptions& offer_answer_options,
4076 cricket::MediaSessionOptions* session_options) {
4077 RTC_DCHECK_RUN_ON(signaling_thread());
4078 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
4079
4080 if (IsUnifiedPlan()) {
4081 GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options);
4082 } else {
4083 GetOptionsForPlanBAnswer(offer_answer_options, session_options);
4084 }
4085
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004086 // Apply ICE renomination flag.
4087 for (auto& options : session_options->media_description_options) {
4088 options.transport_options.enable_ice_renomination =
4089 pc_->configuration()->enable_ice_renomination;
4090 }
4091
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004092 session_options->rtcp_cname = rtcp_cname_;
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004093 session_options->crypto_options = pc_->GetCryptoOptions();
4094 session_options->pooled_ice_credentials =
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02004095 context_->network_thread()->BlockingCall(
Niels Möller4bab23f2021-01-18 09:24:33 +01004096 [this] { return port_allocator()->GetPooledIceCredentials(); });
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004097}
4098
4099void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer(
4100 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4101 cricket::MediaSessionOptions* session_options) {
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004102 bool send_audio = false;
4103 bool recv_audio = false;
4104 bool send_video = false;
4105 bool recv_video = false;
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004106
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004107 if (ConfiguredForMedia()) {
4108 // Figure out transceiver directional preferences.
4109 send_audio =
4110 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
4111 send_video =
4112 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004113
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004114 // By default, generate sendrecv/recvonly m= sections. The direction is also
4115 // restricted by the direction in the offer.
4116 recv_audio = true;
4117 recv_video = true;
4118
4119 // The "offer_to_receive_X" options allow those defaults to be overridden.
4120 if (offer_answer_options.offer_to_receive_audio !=
4121 RTCOfferAnswerOptions::kUndefined) {
4122 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
4123 }
4124 if (offer_answer_options.offer_to_receive_video !=
4125 RTCOfferAnswerOptions::kUndefined) {
4126 recv_video = (offer_answer_options.offer_to_receive_video > 0);
4127 }
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004128 }
4129
4130 absl::optional<size_t> audio_index;
4131 absl::optional<size_t> video_index;
4132 absl::optional<size_t> data_index;
4133
4134 // Generate m= sections that match those in the offer.
4135 // Note that mediasession.cc will handle intersection our preferred
4136 // direction with the offered direction.
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004137 GenerateMediaDescriptionOptions(
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004138 remote_description(),
4139 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
4140 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index,
4141 &video_index, &data_index, session_options);
4142
4143 cricket::MediaDescriptionOptions* audio_media_description_options =
4144 !audio_index ? nullptr
4145 : &session_options->media_description_options[*audio_index];
4146 cricket::MediaDescriptionOptions* video_media_description_options =
4147 !video_index ? nullptr
4148 : &session_options->media_description_options[*video_index];
4149
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004150 if (ConfiguredForMedia()) {
4151 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
4152 audio_media_description_options,
4153 video_media_description_options,
4154 offer_answer_options.num_simulcast_layers);
4155 }
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004156}
4157
4158void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanAnswer(
4159 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4160 cricket::MediaSessionOptions* session_options) {
4161 // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial
4162 // Answers) and 5.3.2 (Subsequent Answers).
4163 RTC_DCHECK(remote_description());
4164 RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer);
4165 for (const ContentInfo& content :
4166 remote_description()->description()->contents()) {
4167 cricket::MediaType media_type = content.media_description()->type();
4168 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
4169 media_type == cricket::MEDIA_TYPE_VIDEO) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004170 auto transceiver = transceivers()->FindByMid(content.name);
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004171 if (transceiver) {
4172 session_options->media_description_options.push_back(
4173 GetMediaDescriptionOptionsForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:36 +00004174 transceiver->internal(), content.name,
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004175 /*is_create_offer=*/false));
4176 } else {
4177 // This should only happen with rejected transceivers.
4178 RTC_DCHECK(content.rejected);
4179 session_options->media_description_options.push_back(
4180 cricket::MediaDescriptionOptions(media_type, content.name,
4181 RtpTransceiverDirection::kInactive,
4182 /*stopped=*/true));
4183 }
Philipp Hancke4e8c1152020-10-13 12:43:15 +02004184 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4185 RTC_DCHECK(content.rejected);
4186 session_options->media_description_options.push_back(
4187 cricket::MediaDescriptionOptions(media_type, content.name,
4188 RtpTransceiverDirection::kInactive,
4189 /*stopped=*/true));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004190 } else {
4191 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4192 // Reject all data sections if data channels are disabled.
4193 // Reject a data section if it has already been rejected.
4194 // Reject all data sections except for the first one.
Florent Castelli516e2842021-04-19 15:29:50 +02004195 if (content.rejected || content.name != *(pc_->GetDataMid())) {
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004196 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004197 GetMediaDescriptionOptionsForRejectedData(content.name));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004198 } else {
4199 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004200 GetMediaDescriptionOptionsForActiveData(content.name));
Harald Alvestrandc06e3742020-10-01 10:23:33 +00004201 }
4202 }
4203 }
4204}
4205
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004206const char* SdpOfferAnswerHandler::SessionErrorToString(
4207 SessionError error) const {
4208 switch (error) {
4209 case SessionError::kNone:
4210 return "ERROR_NONE";
4211 case SessionError::kContent:
4212 return "ERROR_CONTENT";
4213 case SessionError::kTransport:
4214 return "ERROR_TRANSPORT";
4215 }
Artem Titovd3251962021-11-15 16:57:07 +01004216 RTC_DCHECK_NOTREACHED();
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004217 return "";
4218}
4219
4220std::string SdpOfferAnswerHandler::GetSessionErrorMsg() {
4221 RTC_DCHECK_RUN_ON(signaling_thread());
4222 rtc::StringBuilder desc;
4223 desc << kSessionError << SessionErrorToString(session_error()) << ". ";
4224 desc << kSessionErrorDesc << session_error_desc() << ".";
4225 return desc.Release();
4226}
4227
4228void SdpOfferAnswerHandler::SetSessionError(SessionError error,
4229 const std::string& error_desc) {
4230 RTC_DCHECK_RUN_ON(signaling_thread());
4231 if (error != session_error_) {
4232 session_error_ = error;
4233 session_error_desc_ = error_desc;
4234 }
4235}
4236
4237RTCError SdpOfferAnswerHandler::HandleLegacyOfferOptions(
4238 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
4239 RTC_DCHECK_RUN_ON(signaling_thread());
4240 RTC_DCHECK(IsUnifiedPlan());
4241
4242 if (options.offer_to_receive_audio == 0) {
4243 RemoveRecvDirectionFromReceivingTransceiversOfType(
4244 cricket::MEDIA_TYPE_AUDIO);
4245 } else if (options.offer_to_receive_audio == 1) {
4246 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO);
4247 } else if (options.offer_to_receive_audio > 1) {
4248 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4249 "offer_to_receive_audio > 1 is not supported.");
4250 }
4251
4252 if (options.offer_to_receive_video == 0) {
4253 RemoveRecvDirectionFromReceivingTransceiversOfType(
4254 cricket::MEDIA_TYPE_VIDEO);
4255 } else if (options.offer_to_receive_video == 1) {
4256 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO);
4257 } else if (options.offer_to_receive_video > 1) {
4258 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4259 "offer_to_receive_video > 1 is not supported.");
4260 }
4261
4262 return RTCError::OK();
4263}
4264
4265void SdpOfferAnswerHandler::RemoveRecvDirectionFromReceivingTransceiversOfType(
4266 cricket::MediaType media_type) {
4267 for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) {
4268 RtpTransceiverDirection new_direction =
4269 RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false);
4270 if (new_direction != transceiver->direction()) {
4271 RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type)
4272 << " transceiver (MID="
4273 << transceiver->mid().value_or("<not set>") << ") from "
4274 << RtpTransceiverDirectionToString(
4275 transceiver->direction())
4276 << " to "
4277 << RtpTransceiverDirectionToString(new_direction)
4278 << " since CreateOffer specified offer_to_receive=0";
4279 transceiver->internal()->set_direction(new_direction);
4280 }
4281 }
4282}
4283
4284void SdpOfferAnswerHandler::AddUpToOneReceivingTransceiverOfType(
4285 cricket::MediaType media_type) {
4286 RTC_DCHECK_RUN_ON(signaling_thread());
4287 if (GetReceivingTransceiversOfType(media_type).empty()) {
4288 RTC_LOG(LS_INFO)
4289 << "Adding one recvonly " << cricket::MediaTypeToString(media_type)
4290 << " transceiver since CreateOffer specified offer_to_receive=1";
4291 RtpTransceiverInit init;
4292 init.direction = RtpTransceiverDirection::kRecvOnly;
4293 pc_->AddTransceiver(media_type, nullptr, init,
4294 /*update_negotiation_needed=*/false);
4295 }
4296}
4297
4298std::vector<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
4299SdpOfferAnswerHandler::GetReceivingTransceiversOfType(
4300 cricket::MediaType media_type) {
4301 std::vector<
4302 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
4303 receiving_transceivers;
Harald Alvestrande15fb152020-10-19 13:28:05 +00004304 for (const auto& transceiver : transceivers()->List()) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004305 if (!transceiver->stopped() && transceiver->media_type() == media_type &&
4306 RtpTransceiverDirectionHasRecv(transceiver->direction())) {
4307 receiving_transceivers.push_back(transceiver);
4308 }
4309 }
4310 return receiving_transceivers;
4311}
4312
4313void SdpOfferAnswerHandler::ProcessRemovalOfRemoteTrack(
4314 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
4315 transceiver,
4316 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>* remove_list,
4317 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4318 RTC_DCHECK(transceiver->mid());
4319 RTC_LOG(LS_INFO) << "Processing the removal of a track for MID="
4320 << *transceiver->mid();
4321 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
4322 transceiver->internal()->receiver_internal()->streams();
4323 // This will remove the remote track from the streams.
4324 transceiver->internal()->receiver_internal()->set_stream_ids({});
4325 remove_list->push_back(transceiver);
4326 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
4327}
4328
4329void SdpOfferAnswerHandler::RemoveRemoteStreamsIfEmpty(
4330 const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& remote_streams,
4331 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4332 RTC_DCHECK_RUN_ON(signaling_thread());
4333 // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of
4334 // streams, see if the stream was removed by checking if this was the last
4335 // receiver with that stream ID.
4336 for (const auto& remote_stream : remote_streams) {
4337 if (remote_stream->GetAudioTracks().empty() &&
4338 remote_stream->GetVideoTracks().empty()) {
Niels Möllerafb246b2022-04-20 14:26:50 +02004339 remote_streams_->RemoveStream(remote_stream.get());
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004340 removed_streams->push_back(remote_stream);
4341 }
4342 }
4343}
4344
4345void SdpOfferAnswerHandler::RemoveSenders(cricket::MediaType media_type) {
4346 RTC_DCHECK_RUN_ON(signaling_thread());
4347 UpdateLocalSenders(std::vector<cricket::StreamParams>(), media_type);
4348 UpdateRemoteSendersList(std::vector<cricket::StreamParams>(), false,
4349 media_type, nullptr);
4350}
4351
4352void SdpOfferAnswerHandler::UpdateLocalSenders(
4353 const std::vector<cricket::StreamParams>& streams,
4354 cricket::MediaType media_type) {
Markus Handell518669d2021-06-07 13:30:46 +02004355 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateLocalSenders");
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004356 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrande15fb152020-10-19 13:28:05 +00004357 std::vector<RtpSenderInfo>* current_senders =
4358 rtp_manager()->GetLocalSenderInfos(media_type);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004359
4360 // Find removed tracks. I.e., tracks where the track id, stream id or ssrc
4361 // don't match the new StreamParam.
4362 for (auto sender_it = current_senders->begin();
4363 sender_it != current_senders->end();
4364 /* incremented manually */) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004365 const RtpSenderInfo& info = *sender_it;
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004366 const cricket::StreamParams* params =
4367 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4368 if (!params || params->id != info.sender_id ||
4369 params->first_stream_id() != info.stream_id) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004370 rtp_manager()->OnLocalSenderRemoved(info, media_type);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004371 sender_it = current_senders->erase(sender_it);
4372 } else {
4373 ++sender_it;
4374 }
4375 }
4376
4377 // Find new and active senders.
4378 for (const cricket::StreamParams& params : streams) {
Artem Titovcfea2182021-08-10 01:22:31 +02004379 // The sync_label is the MediaStream label and the `stream.id` is the
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004380 // sender id.
4381 const std::string& stream_id = params.first_stream_id();
4382 const std::string& sender_id = params.id;
4383 uint32_t ssrc = params.first_ssrc();
Harald Alvestrande15fb152020-10-19 13:28:05 +00004384 const RtpSenderInfo* sender_info =
4385 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004386 if (!sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004387 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
4388 rtp_manager()->OnLocalSenderAdded(current_senders->back(), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004389 }
4390 }
4391}
4392
4393void SdpOfferAnswerHandler::UpdateRemoteSendersList(
4394 const cricket::StreamParamsVec& streams,
4395 bool default_sender_needed,
4396 cricket::MediaType media_type,
4397 StreamCollection* new_streams) {
Markus Handell518669d2021-06-07 13:30:46 +02004398 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateRemoteSendersList");
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004399 RTC_DCHECK_RUN_ON(signaling_thread());
4400 RTC_DCHECK(!IsUnifiedPlan());
4401
Harald Alvestrande15fb152020-10-19 13:28:05 +00004402 std::vector<RtpSenderInfo>* current_senders =
4403 rtp_manager()->GetRemoteSenderInfos(media_type);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004404
4405 // Find removed senders. I.e., senders where the sender id or ssrc don't match
4406 // the new StreamParam.
4407 for (auto sender_it = current_senders->begin();
4408 sender_it != current_senders->end();
4409 /* incremented manually */) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004410 const RtpSenderInfo& info = *sender_it;
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004411 const cricket::StreamParams* params =
4412 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4413 std::string params_stream_id;
4414 if (params) {
4415 params_stream_id =
4416 (!params->first_stream_id().empty() ? params->first_stream_id()
4417 : kDefaultStreamId);
4418 }
4419 bool sender_exists = params && params->id == info.sender_id &&
4420 params_stream_id == info.stream_id;
4421 // If this is a default track, and we still need it, don't remove it.
4422 if ((info.stream_id == kDefaultStreamId && default_sender_needed) ||
4423 sender_exists) {
4424 ++sender_it;
4425 } else {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004426 rtp_manager()->OnRemoteSenderRemoved(
4427 info, remote_streams_->find(info.stream_id), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004428 sender_it = current_senders->erase(sender_it);
4429 }
4430 }
4431
4432 // Find new and active senders.
4433 for (const cricket::StreamParams& params : streams) {
4434 if (!params.has_ssrcs()) {
4435 // The remote endpoint has streams, but didn't signal ssrcs. For an active
4436 // sender, this means it is coming from a Unified Plan endpoint,so we just
4437 // create a default.
4438 default_sender_needed = true;
4439 break;
4440 }
4441
Artem Titovcfea2182021-08-10 01:22:31 +02004442 // `params.id` is the sender id and the stream id uses the first of
4443 // `params.stream_ids`. The remote description could come from a Unified
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004444 // Plan endpoint, with multiple or no stream_ids() signaled. Since this is
4445 // not supported in Plan B, we just take the first here and create the
4446 // default stream ID if none is specified.
4447 const std::string& stream_id =
4448 (!params.first_stream_id().empty() ? params.first_stream_id()
4449 : kDefaultStreamId);
4450 const std::string& sender_id = params.id;
4451 uint32_t ssrc = params.first_ssrc();
4452
Niels Möllere7cc8832022-01-04 15:20:03 +01004453 rtc::scoped_refptr<MediaStreamInterface> stream(
4454 remote_streams_->find(stream_id));
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004455 if (!stream) {
4456 // This is a new MediaStream. Create a new remote MediaStream.
4457 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
4458 MediaStream::Create(stream_id));
Harald Alvestrand6f04b652020-10-09 11:42:17 +00004459 remote_streams_->AddStream(stream);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004460 new_streams->AddStream(stream);
4461 }
4462
Harald Alvestrande15fb152020-10-19 13:28:05 +00004463 const RtpSenderInfo* sender_info =
4464 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004465 if (!sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004466 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
Niels Möllerafb246b2022-04-20 14:26:50 +02004467 rtp_manager()->OnRemoteSenderAdded(current_senders->back(), stream.get(),
Harald Alvestrande15fb152020-10-19 13:28:05 +00004468 media_type);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004469 }
4470 }
4471
4472 // Add default sender if necessary.
4473 if (default_sender_needed) {
Niels Möllere7cc8832022-01-04 15:20:03 +01004474 rtc::scoped_refptr<MediaStreamInterface> default_stream(
4475 remote_streams_->find(kDefaultStreamId));
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004476 if (!default_stream) {
4477 // Create the new default MediaStream.
4478 default_stream = MediaStreamProxy::Create(
4479 rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId));
Harald Alvestrand6f04b652020-10-09 11:42:17 +00004480 remote_streams_->AddStream(default_stream);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004481 new_streams->AddStream(default_stream);
4482 }
4483 std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO)
4484 ? kDefaultAudioSenderId
4485 : kDefaultVideoSenderId;
Harald Alvestrande15fb152020-10-19 13:28:05 +00004486 const RtpSenderInfo* default_sender_info = rtp_manager()->FindSenderInfo(
4487 *current_senders, kDefaultStreamId, default_sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004488 if (!default_sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:05 +00004489 current_senders->push_back(
4490 RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0));
4491 rtp_manager()->OnRemoteSenderAdded(current_senders->back(),
Niels Möllerafb246b2022-04-20 14:26:50 +02004492 default_stream.get(), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004493 }
4494 }
4495}
4496
Taylor Brandstetterd0acbd82021-01-25 13:44:55 -08004497void SdpOfferAnswerHandler::EnableSending() {
Markus Handell518669d2021-06-07 13:30:46 +02004498 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::EnableSending");
Taylor Brandstetterd0acbd82021-01-25 13:44:55 -08004499 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004500 if (!ConfiguredForMedia()) {
4501 return;
4502 }
Harald Alvestrand85466662021-04-19 21:21:36 +00004503 for (const auto& transceiver : transceivers()->ListInternal()) {
4504 cricket::ChannelInterface* channel = transceiver->channel();
Tommi1959f8f2021-04-26 10:20:19 +02004505 if (channel) {
Taylor Brandstetterd0acbd82021-01-25 13:44:55 -08004506 channel->Enable(true);
4507 }
4508 }
Taylor Brandstetterd0acbd82021-01-25 13:44:55 -08004509}
4510
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004511RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
4512 SdpType type,
Henrik Boströmf8187e02021-04-26 21:04:26 +02004513 cricket::ContentSource source,
4514 const std::map<std::string, const cricket::ContentGroup*>&
4515 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 13:30:46 +02004516 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownMediaDescription");
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004517 const SessionDescriptionInterface* sdesc =
4518 (source == cricket::CS_LOCAL ? local_description()
4519 : remote_description());
4520 RTC_DCHECK_RUN_ON(signaling_thread());
4521 RTC_DCHECK(sdesc);
4522
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004523 if (ConfiguredForMedia()) {
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02004524 // Note: This will perform a BlockingCall over to the worker thread, which
4525 // we'll also do in a loop below.
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004526 if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) {
4527 // Note that this is never expected to fail, since RtpDemuxer doesn't
4528 // return an error when changing payload type demux criteria, which is all
4529 // this does.
4530 return RTCError(RTCErrorType::INTERNAL_ERROR,
4531 "Failed to update payload type demuxing state.");
Taylor Brandstetterd0acbd82021-01-25 13:44:55 -08004532 }
Tommicc7a3682021-05-04 14:59:38 +02004533
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004534 // Push down the new SDP media section for each audio/video transceiver.
4535 auto rtp_transceivers = transceivers()->ListInternal();
4536 std::vector<
4537 std::pair<cricket::ChannelInterface*, const MediaContentDescription*>>
4538 channels;
4539 for (const auto& transceiver : rtp_transceivers) {
4540 const ContentInfo* content_info =
4541 FindMediaSectionForTransceiver(transceiver, sdesc);
4542 cricket::ChannelInterface* channel = transceiver->channel();
4543 if (!channel || !content_info || content_info->rejected) {
4544 continue;
4545 }
4546 const MediaContentDescription* content_desc =
4547 content_info->media_description();
4548 if (!content_desc) {
4549 continue;
4550 }
Tommicc7a3682021-05-04 14:59:38 +02004551
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004552 transceiver->OnNegotiationUpdate(type, content_desc);
4553 channels.push_back(std::make_pair(channel, content_desc));
4554 }
4555
4556 // This for-loop of invokes helps audio impairment during re-negotiations.
4557 // One of the causes is that downstairs decoder creation is synchronous at
4558 // the moment, and that a decoder is created for each codec listed in the
4559 // SDP.
4560 //
4561 // TODO(bugs.webrtc.org/12840): consider merging the invokes again after
4562 // these projects have shipped:
4563 // - bugs.webrtc.org/12462
4564 // - crbug.com/1157227
4565 // - crbug.com/1187289
4566 for (const auto& entry : channels) {
4567 std::string error;
4568 bool success =
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02004569 context_->worker_thread()->BlockingCall([&]() {
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004570 return (source == cricket::CS_LOCAL)
4571 ? entry.first->SetLocalContent(entry.second, type, error)
4572 : entry.first->SetRemoteContent(entry.second, type,
4573 error);
4574 });
4575 if (!success) {
4576 return RTCError(RTCErrorType::INVALID_PARAMETER, error);
4577 }
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004578 }
4579 }
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004580 // Need complete offer/answer with an SCTP m= section before starting SCTP,
4581 // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
4582 if (pc_->sctp_mid() && local_description() && remote_description()) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004583 auto local_sctp_description = cricket::GetFirstSctpDataContentDescription(
4584 local_description()->description());
4585 auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription(
4586 remote_description()->description());
Tomas Gunnarsson92eebef2021-02-10 13:05:44 +01004587 if (local_sctp_description && remote_sctp_description) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004588 int max_message_size;
4589 // A remote max message size of zero means "any size supported".
4590 // We configure the connection with our own max message size.
4591 if (remote_sctp_description->max_message_size() == 0) {
4592 max_message_size = local_sctp_description->max_message_size();
4593 } else {
4594 max_message_size =
4595 std::min(local_sctp_description->max_message_size(),
4596 remote_sctp_description->max_message_size());
4597 }
Tomas Gunnarsson92eebef2021-02-10 13:05:44 +01004598 pc_->StartSctpTransport(local_sctp_description->port(),
4599 remote_sctp_description->port(),
4600 max_message_size);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004601 }
4602 }
4603
4604 return RTCError::OK();
4605}
4606
4607RTCError SdpOfferAnswerHandler::PushdownTransportDescription(
4608 cricket::ContentSource source,
4609 SdpType type) {
Markus Handell518669d2021-06-07 13:30:46 +02004610 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownTransportDescription");
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004611 RTC_DCHECK_RUN_ON(signaling_thread());
4612
4613 if (source == cricket::CS_LOCAL) {
4614 const SessionDescriptionInterface* sdesc = local_description();
4615 RTC_DCHECK(sdesc);
Harald Alvestrandbc32c562022-02-09 12:08:47 +00004616 return transport_controller_s()->SetLocalDescription(type,
4617 sdesc->description());
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004618 } else {
4619 const SessionDescriptionInterface* sdesc = remote_description();
4620 RTC_DCHECK(sdesc);
Harald Alvestrandbc32c562022-02-09 12:08:47 +00004621 return transport_controller_s()->SetRemoteDescription(type,
4622 sdesc->description());
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004623 }
4624}
4625
4626void SdpOfferAnswerHandler::RemoveStoppedTransceivers() {
Markus Handell518669d2021-06-07 13:30:46 +02004627 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveStoppedTransceivers");
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004628 RTC_DCHECK_RUN_ON(signaling_thread());
4629 // 3.2.10.1: For each transceiver in the connection's set of transceivers
4630 // run the following steps:
4631 if (!IsUnifiedPlan())
4632 return;
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004633 if (!ConfiguredForMedia()) {
4634 return;
4635 }
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004636 // Traverse a copy of the transceiver list.
Harald Alvestrande15fb152020-10-19 13:28:05 +00004637 auto transceiver_list = transceivers()->List();
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004638 for (auto transceiver : transceiver_list) {
4639 // 3.2.10.1.1: If transceiver is stopped, associated with an m= section
4640 // and the associated m= section is rejected in
4641 // connection.[[CurrentLocalDescription]] or
4642 // connection.[[CurrentRemoteDescription]], remove the
4643 // transceiver from the connection's set of transceivers.
4644 if (!transceiver->stopped()) {
4645 continue;
4646 }
Harald Alvestrand85466662021-04-19 21:21:36 +00004647 const ContentInfo* local_content = FindMediaSectionForTransceiver(
4648 transceiver->internal(), local_description());
4649 const ContentInfo* remote_content = FindMediaSectionForTransceiver(
4650 transceiver->internal(), remote_description());
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004651 if ((local_content && local_content->rejected) ||
4652 (remote_content && remote_content->rejected)) {
4653 RTC_LOG(LS_INFO) << "Dissociating transceiver"
Tomas Gunnarsson7fa8d462021-04-16 14:28:26 +02004654 " since the media section is being recycled.";
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004655 transceiver->internal()->set_mid(absl::nullopt);
4656 transceiver->internal()->set_mline_index(absl::nullopt);
Tomas Gunnarsson7fa8d462021-04-16 14:28:26 +02004657 } else if (!local_content && !remote_content) {
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004658 // TODO(bugs.webrtc.org/11973): Consider if this should be removed already
4659 // See https://github.com/w3c/webrtc-pc/issues/2576
4660 RTC_LOG(LS_INFO)
4661 << "Dropping stopped transceiver that was never associated";
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004662 }
Tomas Gunnarsson7fa8d462021-04-16 14:28:26 +02004663 transceivers()->Remove(transceiver);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004664 }
4665}
4666
4667void SdpOfferAnswerHandler::RemoveUnusedChannels(
4668 const SessionDescription* desc) {
4669 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004670 if (ConfiguredForMedia()) {
4671 // Destroy video channel first since it may have a pointer to the
4672 // voice channel.
4673 const cricket::ContentInfo* video_info =
4674 cricket::GetFirstVideoContent(desc);
4675 if (!video_info || video_info->rejected) {
4676 rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel();
4677 }
Artem Titovc6c02ef2022-05-09 08:30:09 +00004678
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00004679 const cricket::ContentInfo* audio_info =
4680 cricket::GetFirstAudioContent(desc);
4681 if (!audio_info || audio_info->rejected) {
4682 rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel();
4683 }
Artem Titovc6c02ef2022-05-09 08:30:09 +00004684 }
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004685 const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc);
Florent Castellidcb9ffc2021-06-29 14:58:23 +02004686 if (!data_info) {
4687 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA,
4688 "No data channel section in the description.");
4689 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
4690 DestroyDataChannelTransport(error);
4691 } else if (data_info->rejected) {
4692 rtc::StringBuilder sb;
4693 sb << "Rejected data channel with mid=" << data_info->name << ".";
4694
4695 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
4696 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
4697 DestroyDataChannelTransport(error);
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004698 }
4699}
4700
4701void SdpOfferAnswerHandler::ReportNegotiatedSdpSemantics(
4702 const SessionDescriptionInterface& answer) {
4703 SdpSemanticNegotiated semantics_negotiated;
4704 switch (answer.description()->msid_signaling()) {
4705 case 0:
4706 semantics_negotiated = kSdpSemanticNegotiatedNone;
4707 break;
4708 case cricket::kMsidSignalingMediaSection:
4709 semantics_negotiated = kSdpSemanticNegotiatedUnifiedPlan;
4710 break;
4711 case cricket::kMsidSignalingSsrcAttribute:
4712 semantics_negotiated = kSdpSemanticNegotiatedPlanB;
4713 break;
4714 case cricket::kMsidSignalingMediaSection |
4715 cricket::kMsidSignalingSsrcAttribute:
4716 semantics_negotiated = kSdpSemanticNegotiatedMixed;
4717 break;
4718 default:
Artem Titovd3251962021-11-15 16:57:07 +01004719 RTC_DCHECK_NOTREACHED();
Harald Alvestranda474fbf2020-10-01 16:47:23 +00004720 }
4721 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpSemanticNegotiated",
4722 semantics_negotiated, kSdpSemanticNegotiatedMax);
4723}
4724
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004725void SdpOfferAnswerHandler::UpdateEndedRemoteMediaStreams() {
4726 RTC_DCHECK_RUN_ON(signaling_thread());
4727 std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_to_remove;
Harald Alvestrand6f04b652020-10-09 11:42:17 +00004728 for (size_t i = 0; i < remote_streams_->count(); ++i) {
4729 MediaStreamInterface* stream = remote_streams_->at(i);
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004730 if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) {
Niels Möllere7cc8832022-01-04 15:20:03 +01004731 streams_to_remove.push_back(
4732 rtc::scoped_refptr<MediaStreamInterface>(stream));
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004733 }
4734 }
4735
4736 for (auto& stream : streams_to_remove) {
Niels Möllerafb246b2022-04-20 14:26:50 +02004737 remote_streams_->RemoveStream(stream.get());
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004738 pc_->Observer()->OnRemoveStream(std::move(stream));
4739 }
4740}
4741
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01004742bool SdpOfferAnswerHandler::UseCandidatesInRemoteDescription() {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004743 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01004744 auto* remote_desc = remote_description();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004745 if (!remote_desc) {
4746 return true;
4747 }
4748 bool ret = true;
4749
4750 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
4751 const IceCandidateCollection* candidates = remote_desc->candidates(m);
4752 for (size_t n = 0; n < candidates->count(); ++n) {
4753 const IceCandidateInterface* candidate = candidates->at(n);
4754 bool valid = false;
4755 if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) {
4756 if (valid) {
4757 RTC_LOG(LS_INFO)
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01004758 << "UseCandidatesInRemoteDescription: Not ready to use "
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004759 "candidate.";
4760 }
4761 continue;
4762 }
4763 ret = UseCandidate(candidate);
4764 if (!ret) {
4765 break;
4766 }
4767 }
4768 }
4769 return ret;
4770}
4771
4772bool SdpOfferAnswerHandler::UseCandidate(
4773 const IceCandidateInterface* candidate) {
4774 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson8cb97062021-02-08 18:57:04 +01004775
4776 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
4777
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004778 RTCErrorOr<const cricket::ContentInfo*> result =
4779 FindContentInfo(remote_description(), candidate);
Tomas Gunnarsson8cb97062021-02-08 18:57:04 +01004780 if (!result.ok())
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004781 return false;
Tomas Gunnarsson8cb97062021-02-08 18:57:04 +01004782
4783 const cricket::Candidate& c = candidate->candidate();
4784 RTCError error = cricket::VerifyCandidate(c);
Tomas Gunnarsson27bc6e22021-02-12 13:16:26 +01004785 if (!error.ok()) {
4786 RTC_LOG(LS_WARNING) << "Invalid candidate: " << c.ToString();
4787 return true;
4788 }
Tomas Gunnarsson8cb97062021-02-08 18:57:04 +01004789
4790 pc_->AddRemoteCandidate(result.value()->name, c);
4791
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004792 return true;
4793}
4794
4795// We need to check the local/remote description for the Transport instead of
4796// the session, because a new Transport added during renegotiation may have
4797// them unset while the session has them set from the previous negotiation.
4798// Not doing so may trigger the auto generation of transport description and
4799// mess up DTLS identity information, ICE credential, etc.
4800bool SdpOfferAnswerHandler::ReadyToUseRemoteCandidate(
4801 const IceCandidateInterface* candidate,
4802 const SessionDescriptionInterface* remote_desc,
4803 bool* valid) {
4804 RTC_DCHECK_RUN_ON(signaling_thread());
4805 *valid = true;
4806
4807 const SessionDescriptionInterface* current_remote_desc =
4808 remote_desc ? remote_desc : remote_description();
4809
4810 if (!current_remote_desc) {
4811 return false;
4812 }
4813
4814 RTCErrorOr<const cricket::ContentInfo*> result =
4815 FindContentInfo(current_remote_desc, candidate);
4816 if (!result.ok()) {
4817 RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. "
4818 << result.error().message();
4819
4820 *valid = false;
4821 return false;
4822 }
4823
Tomas Gunnarsson45de8b32021-04-02 12:33:39 +02004824 return true;
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004825}
4826
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004827RTCErrorOr<const cricket::ContentInfo*> SdpOfferAnswerHandler::FindContentInfo(
4828 const SessionDescriptionInterface* description,
4829 const IceCandidateInterface* candidate) {
Philipp Hancke31e06cb2021-02-26 09:23:53 +01004830 if (!candidate->sdp_mid().empty()) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004831 auto& contents = description->description()->contents();
4832 auto it = absl::c_find_if(
4833 contents, [candidate](const cricket::ContentInfo& content_info) {
4834 return content_info.mid() == candidate->sdp_mid();
4835 });
4836 if (it == contents.end()) {
4837 return RTCError(
4838 RTCErrorType::INVALID_PARAMETER,
4839 "Mid " + candidate->sdp_mid() +
4840 " specified but no media section with that mid found.");
4841 } else {
4842 return &*it;
4843 }
Philipp Hancke31e06cb2021-02-26 09:23:53 +01004844 } else if (candidate->sdp_mline_index() >= 0) {
4845 size_t mediacontent_index =
4846 static_cast<size_t>(candidate->sdp_mline_index());
4847 size_t content_size = description->description()->contents().size();
4848 if (mediacontent_index < content_size) {
4849 return &description->description()->contents()[mediacontent_index];
4850 } else {
4851 return RTCError(RTCErrorType::INVALID_RANGE,
4852 "Media line index (" +
4853 rtc::ToString(candidate->sdp_mline_index()) +
4854 ") out of range (number of mlines: " +
4855 rtc::ToString(content_size) + ").");
4856 }
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004857 }
4858
4859 return RTCError(RTCErrorType::INVALID_PARAMETER,
4860 "Neither sdp_mline_index nor sdp_mid specified.");
4861}
4862
4863RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) {
Markus Handell518669d2021-06-07 13:30:46 +02004864 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateChannels");
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004865 // Creating the media channels. Transports should already have been created
4866 // at this point.
4867 RTC_DCHECK_RUN_ON(signaling_thread());
4868 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc);
4869 if (voice && !voice->rejected &&
Harald Alvestrande15fb152020-10-19 13:28:05 +00004870 !rtp_manager()->GetAudioTransceiver()->internal()->channel()) {
Harald Alvestrand8f429922022-05-04 10:32:30 +00004871 auto error =
4872 rtp_manager()->GetAudioTransceiver()->internal()->CreateChannel(
4873 voice->name, pc_->call_ptr(), pc_->configuration()->media_config,
4874 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
4875 video_options(), video_bitrate_allocator_factory_.get(),
4876 [&](absl::string_view mid) {
4877 RTC_DCHECK_RUN_ON(network_thread());
4878 return transport_controller_n()->GetRtpTransport(mid);
4879 });
4880 if (!error.ok()) {
4881 return error;
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004882 }
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004883 }
4884
4885 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc);
4886 if (video && !video->rejected &&
Harald Alvestrande15fb152020-10-19 13:28:05 +00004887 !rtp_manager()->GetVideoTransceiver()->internal()->channel()) {
Harald Alvestrand8f429922022-05-04 10:32:30 +00004888 auto error =
4889 rtp_manager()->GetVideoTransceiver()->internal()->CreateChannel(
4890 video->name, pc_->call_ptr(), pc_->configuration()->media_config,
4891 pc_->SrtpRequired(), pc_->GetCryptoOptions(),
4892
4893 audio_options(), video_options(),
4894 video_bitrate_allocator_factory_.get(), [&](absl::string_view mid) {
4895 RTC_DCHECK_RUN_ON(network_thread());
4896 return transport_controller_n()->GetRtpTransport(mid);
4897 });
4898 if (!error.ok()) {
4899 return error;
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004900 }
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004901 }
4902
4903 const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc);
Florent Castelli516e2842021-04-19 15:29:50 +02004904 if (data && !data->rejected &&
4905 !data_channel_controller()->data_channel_transport()) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004906 if (!CreateDataChannel(data->name)) {
Tomas Gunnarsson0dd75392022-01-17 19:19:56 +01004907 return RTCError(RTCErrorType::INTERNAL_ERROR,
4908 "Failed to create data channel.");
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004909 }
4910 }
4911
4912 return RTCError::OK();
4913}
4914
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004915bool SdpOfferAnswerHandler::CreateDataChannel(const std::string& mid) {
4916 RTC_DCHECK_RUN_ON(signaling_thread());
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02004917 if (!context_->network_thread()->BlockingCall([this, &mid] {
Harald Alvestrand66c40362022-01-28 17:41:30 +00004918 RTC_DCHECK_RUN_ON(context_->network_thread());
Florent Castelli516e2842021-04-19 15:29:50 +02004919 return pc_->SetupDataChannelTransport_n(mid);
4920 })) {
4921 return false;
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004922 }
Florent Castelli516e2842021-04-19 15:29:50 +02004923 // TODO(tommi): Is this necessary? SetupDataChannelTransport_n() above
4924 // will have queued up updating the transport name on the signaling thread
4925 // and could update the mid at the same time. This here is synchronous
4926 // though, but it changes the state of PeerConnection and makes it be
4927 // out of sync (transport name not set while the mid is set).
4928 pc_->SetSctpDataMid(mid);
Tomas Gunnarsson00f4fd92021-04-08 14:39:47 +02004929 return true;
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004930}
4931
Florent Castellidcb9ffc2021-06-29 14:58:23 +02004932void SdpOfferAnswerHandler::DestroyDataChannelTransport(RTCError error) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004933 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarssond69e0702021-04-07 15:14:43 +02004934 const bool has_sctp = pc_->sctp_mid().has_value();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004935
Harald Alvestrand7af57c62021-04-16 11:12:14 +00004936 if (has_sctp)
Florent Castellidcb9ffc2021-06-29 14:58:23 +02004937 data_channel_controller()->OnTransportChannelClosed(error);
Tomas Gunnarssond69e0702021-04-07 15:14:43 +02004938
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02004939 context_->network_thread()->BlockingCall([this] {
Harald Alvestrand66c40362022-01-28 17:41:30 +00004940 RTC_DCHECK_RUN_ON(context_->network_thread());
Tomas Gunnarssond69e0702021-04-07 15:14:43 +02004941 pc_->TeardownDataChannelTransport_n();
4942 });
4943
4944 if (has_sctp)
Harald Alvestrand653429c2020-10-19 16:05:20 +00004945 pc_->ResetSctpDataMid();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004946}
4947
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004948void SdpOfferAnswerHandler::DestroyAllChannels() {
4949 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrande15fb152020-10-19 13:28:05 +00004950 if (!transceivers()) {
4951 return;
4952 }
Tommife041642021-04-07 10:08:28 +02004953
4954 RTC_LOG_THREAD_BLOCK_COUNT();
4955
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004956 // Destroy video channels first since they may have a pointer to a voice
4957 // channel.
Tommife041642021-04-07 10:08:28 +02004958 auto list = transceivers()->List();
4959 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
4960
4961 for (const auto& transceiver : list) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004962 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Harald Alvestrand19ebabc2022-04-28 13:31:17 +00004963 transceiver->internal()->ClearChannel();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004964 }
4965 }
Tommife041642021-04-07 10:08:28 +02004966 for (const auto& transceiver : list) {
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004967 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Harald Alvestrand19ebabc2022-04-28 13:31:17 +00004968 transceiver->internal()->ClearChannel();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004969 }
4970 }
Tommife041642021-04-07 10:08:28 +02004971
Florent Castellidcb9ffc2021-06-29 14:58:23 +02004972 DestroyDataChannelTransport({});
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00004973}
4974
4975void SdpOfferAnswerHandler::GenerateMediaDescriptionOptions(
4976 const SessionDescriptionInterface* session_desc,
4977 RtpTransceiverDirection audio_direction,
4978 RtpTransceiverDirection video_direction,
4979 absl::optional<size_t>* audio_index,
4980 absl::optional<size_t>* video_index,
4981 absl::optional<size_t>* data_index,
4982 cricket::MediaSessionOptions* session_options) {
4983 RTC_DCHECK_RUN_ON(signaling_thread());
4984 for (const cricket::ContentInfo& content :
4985 session_desc->description()->contents()) {
4986 if (IsAudioContent(&content)) {
4987 // If we already have an audio m= section, reject this extra one.
4988 if (*audio_index) {
4989 session_options->media_description_options.push_back(
4990 cricket::MediaDescriptionOptions(
4991 cricket::MEDIA_TYPE_AUDIO, content.name,
4992 RtpTransceiverDirection::kInactive, /*stopped=*/true));
4993 } else {
4994 bool stopped = (audio_direction == RtpTransceiverDirection::kInactive);
4995 session_options->media_description_options.push_back(
4996 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO,
4997 content.name, audio_direction,
4998 stopped));
4999 *audio_index = session_options->media_description_options.size() - 1;
5000 }
5001 session_options->media_description_options.back().header_extensions =
Harald Alvestrand35f4b4c2022-05-16 10:36:43 +00005002 media_engine()->voice().GetRtpHeaderExtensions();
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005003 } else if (IsVideoContent(&content)) {
5004 // If we already have an video m= section, reject this extra one.
5005 if (*video_index) {
5006 session_options->media_description_options.push_back(
5007 cricket::MediaDescriptionOptions(
5008 cricket::MEDIA_TYPE_VIDEO, content.name,
5009 RtpTransceiverDirection::kInactive, /*stopped=*/true));
5010 } else {
5011 bool stopped = (video_direction == RtpTransceiverDirection::kInactive);
5012 session_options->media_description_options.push_back(
5013 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO,
5014 content.name, video_direction,
5015 stopped));
5016 *video_index = session_options->media_description_options.size() - 1;
5017 }
5018 session_options->media_description_options.back().header_extensions =
Harald Alvestrand35f4b4c2022-05-16 10:36:43 +00005019 media_engine()->video().GetRtpHeaderExtensions();
Philipp Hancke4e8c1152020-10-13 12:43:15 +02005020 } else if (IsUnsupportedContent(&content)) {
5021 session_options->media_description_options.push_back(
5022 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_UNSUPPORTED,
5023 content.name,
5024 RtpTransceiverDirection::kInactive,
5025 /*stopped=*/true));
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005026 } else {
5027 RTC_DCHECK(IsDataContent(&content));
5028 // If we already have an data m= section, reject this extra one.
5029 if (*data_index) {
5030 session_options->media_description_options.push_back(
5031 GetMediaDescriptionOptionsForRejectedData(content.name));
5032 } else {
5033 session_options->media_description_options.push_back(
5034 GetMediaDescriptionOptionsForActiveData(content.name));
5035 *data_index = session_options->media_description_options.size() - 1;
5036 }
5037 }
5038 }
5039}
5040
5041cricket::MediaDescriptionOptions
5042SdpOfferAnswerHandler::GetMediaDescriptionOptionsForActiveData(
5043 const std::string& mid) const {
5044 RTC_DCHECK_RUN_ON(signaling_thread());
5045 // Direction for data sections is meaningless, but legacy endpoints might
5046 // expect sendrecv.
5047 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5048 RtpTransceiverDirection::kSendRecv,
5049 /*stopped=*/false);
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005050 return options;
5051}
5052
5053cricket::MediaDescriptionOptions
5054SdpOfferAnswerHandler::GetMediaDescriptionOptionsForRejectedData(
5055 const std::string& mid) const {
5056 RTC_DCHECK_RUN_ON(signaling_thread());
5057 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5058 RtpTransceiverDirection::kInactive,
5059 /*stopped=*/true);
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005060 return options;
5061}
5062
Taylor Brandstetterd0acbd82021-01-25 13:44:55 -08005063bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
Henrik Boströmf8187e02021-04-26 21:04:26 +02005064 cricket::ContentSource source,
5065 const std::map<std::string, const cricket::ContentGroup*>&
5066 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 13:30:46 +02005067 TRACE_EVENT0("webrtc",
5068 "SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState");
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005069 RTC_DCHECK_RUN_ON(signaling_thread());
5070 // We may need to delete any created default streams and disable creation of
5071 // new ones on the basis of payload type. This is needed to avoid SSRC
5072 // collisions in Call's RtpDemuxer, in the case that a transceiver has
5073 // created a default stream, and then some other channel gets the SSRC
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005074 // signaled in the corresponding Unified Plan "m=" section. Specifically, we
5075 // need to disable payload type based demuxing when two bundled "m=" sections
5076 // are using the same payload type(s). For more context
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005077 // see https://bugs.chromium.org/p/webrtc/issues/detail?id=11477
5078 const SessionDescriptionInterface* sdesc =
5079 (source == cricket::CS_LOCAL ? local_description()
5080 : remote_description());
Henrik Boströmf8187e02021-04-26 21:04:26 +02005081 struct PayloadTypes {
5082 std::set<int> audio_payload_types;
5083 std::set<int> video_payload_types;
Henrik Boström4ea80f32021-06-09 10:29:50 +02005084 bool pt_demuxing_possible_audio = true;
5085 bool pt_demuxing_possible_video = true;
Henrik Boströmf8187e02021-04-26 21:04:26 +02005086 };
5087 std::map<const cricket::ContentGroup*, PayloadTypes> payload_types_by_bundle;
Henrik Boström4ea80f32021-06-09 10:29:50 +02005088 // If the MID is missing from *any* receiving m= section, this is set to true.
5089 bool mid_header_extension_missing_audio = false;
5090 bool mid_header_extension_missing_video = false;
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005091 for (auto& content_info : sdesc->description()->contents()) {
Henrik Boströmf8187e02021-04-26 21:04:26 +02005092 auto it = bundle_groups_by_mid.find(content_info.name);
5093 const cricket::ContentGroup* bundle_group =
5094 it != bundle_groups_by_mid.end() ? it->second : nullptr;
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005095 // If this m= section isn't bundled, it's safe to demux by payload type
5096 // since other m= sections using the same payload type will also be using
5097 // different transports.
Henrik Boströmf8187e02021-04-26 21:04:26 +02005098 if (!bundle_group) {
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005099 continue;
5100 }
Henrik Boströmf8187e02021-04-26 21:04:26 +02005101 PayloadTypes* payload_types = &payload_types_by_bundle[bundle_group];
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005102 if (content_info.rejected ||
5103 (source == cricket::ContentSource::CS_LOCAL &&
5104 !RtpTransceiverDirectionHasRecv(
5105 content_info.media_description()->direction())) ||
5106 (source == cricket::ContentSource::CS_REMOTE &&
5107 !RtpTransceiverDirectionHasSend(
5108 content_info.media_description()->direction()))) {
5109 // Ignore transceivers that are not receiving.
5110 continue;
5111 }
5112 switch (content_info.media_description()->type()) {
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005113 case cricket::MediaType::MEDIA_TYPE_AUDIO: {
Henrik Boström4ea80f32021-06-09 10:29:50 +02005114 if (!mid_header_extension_missing_audio) {
5115 mid_header_extension_missing_audio =
5116 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5117 }
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005118 const cricket::AudioContentDescription* audio_desc =
5119 content_info.media_description()->as_audio();
5120 for (const cricket::AudioCodec& audio : audio_desc->codecs()) {
Henrik Boströmf8187e02021-04-26 21:04:26 +02005121 if (payload_types->audio_payload_types.count(audio.id)) {
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005122 // Two m= sections are using the same payload type, thus demuxing
5123 // by payload type is not possible.
Henrik Boström4ea80f32021-06-09 10:29:50 +02005124 payload_types->pt_demuxing_possible_audio = false;
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005125 }
Henrik Boströmf8187e02021-04-26 21:04:26 +02005126 payload_types->audio_payload_types.insert(audio.id);
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005127 }
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005128 break;
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005129 }
5130 case cricket::MediaType::MEDIA_TYPE_VIDEO: {
Henrik Boström4ea80f32021-06-09 10:29:50 +02005131 if (!mid_header_extension_missing_video) {
5132 mid_header_extension_missing_video =
5133 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5134 }
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005135 const cricket::VideoContentDescription* video_desc =
5136 content_info.media_description()->as_video();
5137 for (const cricket::VideoCodec& video : video_desc->codecs()) {
Henrik Boströmf8187e02021-04-26 21:04:26 +02005138 if (payload_types->video_payload_types.count(video.id)) {
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005139 // Two m= sections are using the same payload type, thus demuxing
5140 // by payload type is not possible.
Henrik Boström4ea80f32021-06-09 10:29:50 +02005141 payload_types->pt_demuxing_possible_video = false;
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005142 }
Henrik Boströmf8187e02021-04-26 21:04:26 +02005143 payload_types->video_payload_types.insert(video.id);
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005144 }
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005145 break;
Taylor Brandstetterd3ef4992020-10-15 18:22:57 -07005146 }
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005147 default:
5148 // Ignore data channels.
5149 continue;
5150 }
5151 }
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005152
Henrik Boström4ea80f32021-06-09 10:29:50 +02005153 // In Unified Plan, payload type demuxing is useful for legacy endpoints that
5154 // don't support the MID header extension, but it can also cause incorrrect
5155 // forwarding of packets when going from one m= section to multiple m=
5156 // sections in the same BUNDLE. This only happens if media arrives prior to
5157 // negotiation, but this can cause missing video and unsignalled ssrc bugs
5158 // severe enough to warrant disabling PT demuxing in such cases. Therefore, if
5159 // a MID header extension is present on all m= sections for a given kind
5160 // (audio/video) then we use that as an OK to disable payload type demuxing in
5161 // BUNDLEs of that kind. However if PT demuxing was ever turned on (e.g. MID
5162 // was ever removed on ANY m= section of that kind) then we continue to allow
5163 // PT demuxing in order to prevent disabling it in follow-up O/A exchanges and
5164 // allowing early media by PT.
5165 bool bundled_pt_demux_allowed_audio = !IsUnifiedPlan() ||
5166 mid_header_extension_missing_audio ||
5167 pt_demuxing_has_been_used_audio_;
5168 bool bundled_pt_demux_allowed_video = !IsUnifiedPlan() ||
5169 mid_header_extension_missing_video ||
5170 pt_demuxing_has_been_used_video_;
Henrik Boström4ea80f32021-06-09 10:29:50 +02005171
Tomas Gunnarsson92f9b742022-01-02 20:40:22 +00005172 // Gather all updates ahead of time so that all channels can be updated in a
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02005173 // single BlockingCall; necessary due to thread guards.
Tomas Gunnarsson92f9b742022-01-02 20:40:22 +00005174 std::vector<std::pair<bool, cricket::ChannelInterface*>> channels_to_update;
5175 for (const auto& transceiver : transceivers()->ListInternal()) {
5176 cricket::ChannelInterface* channel = transceiver->channel();
5177 const ContentInfo* content =
5178 FindMediaSectionForTransceiver(transceiver, sdesc);
5179 if (!channel || !content) {
5180 continue;
5181 }
5182
5183 const cricket::MediaType media_type = channel->media_type();
5184 if (media_type != cricket::MediaType::MEDIA_TYPE_AUDIO &&
5185 media_type != cricket::MediaType::MEDIA_TYPE_VIDEO) {
5186 continue;
5187 }
5188
5189 RtpTransceiverDirection local_direction =
5190 content->media_description()->direction();
5191 if (source == cricket::CS_REMOTE) {
5192 local_direction = RtpTransceiverDirectionReversed(local_direction);
5193 }
5194
Tomas Gunnarsson5411b172022-01-24 08:45:26 +01005195 auto bundle_it = bundle_groups_by_mid.find(channel->mid());
Tomas Gunnarsson92f9b742022-01-02 20:40:22 +00005196 const cricket::ContentGroup* bundle_group =
5197 bundle_it != bundle_groups_by_mid.end() ? bundle_it->second : nullptr;
5198 bool pt_demux_enabled = RtpTransceiverDirectionHasRecv(local_direction);
5199 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
5200 pt_demux_enabled &=
5201 !bundle_group ||
5202 (bundled_pt_demux_allowed_audio &&
5203 payload_types_by_bundle[bundle_group].pt_demuxing_possible_audio);
5204 if (pt_demux_enabled) {
5205 pt_demuxing_has_been_used_audio_ = true;
5206 }
5207 } else {
5208 RTC_DCHECK_EQ(media_type, cricket::MediaType::MEDIA_TYPE_VIDEO);
5209 pt_demux_enabled &=
5210 !bundle_group ||
5211 (bundled_pt_demux_allowed_video &&
5212 payload_types_by_bundle[bundle_group].pt_demuxing_possible_video);
5213 if (pt_demux_enabled) {
5214 pt_demuxing_has_been_used_video_ = true;
5215 }
5216 }
5217
5218 channels_to_update.emplace_back(pt_demux_enabled, transceiver->channel());
5219 }
5220
5221 if (channels_to_update.empty()) {
5222 return true;
5223 }
5224
Danil Chapovalov9e09a1f2022-09-08 18:38:10 +02005225 // TODO(bugs.webrtc.org/11993): This BlockingCall() will also block on the
5226 // network thread for every demuxer sink that needs to be updated. The demuxer
5227 // state needs to be fully (and only) managed on the network thread and once
5228 // that's the case, there's no need to stop by on the worker. Ideally we could
5229 // also do this without blocking.
5230 return context_->worker_thread()->BlockingCall([&channels_to_update]() {
5231 for (const auto& it : channels_to_update) {
5232 if (!it.second->SetPayloadTypeDemuxingEnabled(it.first)) {
5233 // Note that the state has already been irrevocably changed at this
5234 // point. Is it useful to stop the loop?
5235 return false;
5236 }
5237 }
5238 return true;
5239 });
Harald Alvestrandbc9ca252020-10-05 13:08:41 +00005240}
5241
Harald Alvestrand8101e7b2022-05-23 14:57:47 +00005242bool SdpOfferAnswerHandler::ConfiguredForMedia() const {
5243 return context_->media_engine();
5244}
5245
Harald Alvestrandcdcfab02020-09-28 13:02:07 +00005246} // namespace webrtc