blob: 758a50c2c911dd9e2cfdf92e7c166f3885249086 [file] [log] [blame]
Harald Alvestrandcdcfab02020-09-28 13:02:071/*
2 * Copyright 2020 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "pc/sdp_offer_answer.h"
12
Harald Alvestrandc06e3742020-10-01 10:23:3313#include <algorithm>
Harald Alvestrand981c5722022-02-09 12:42:2714#include <cstddef>
Harald Alvestrand1f7eab62020-10-18 16:51:4715#include <iterator>
Harald Alvestrand3eaee6b2020-10-19 06:35:5516#include <map>
Mirko Bonadei9ff450d2021-08-02 08:56:3317#include <memory>
Harald Alvestrandc06e3742020-10-01 10:23:3318#include <queue>
Harald Alvestrand981c5722022-02-09 12:42:2719#include <string>
Harald Alvestrandf01bd6c2020-10-23 13:30:4620#include <utility>
Harald Alvestrandc06e3742020-10-01 10:23:3321
Harald Alvestrand1f7eab62020-10-18 16:51:4722#include "absl/algorithm/container.h"
Harald Alvestrand9cd199d2020-10-27 07:10:4323#include "absl/memory/memory.h"
Tomas Gunnarsson0dd75392022-01-17 18:19:5624#include "absl/strings/match.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4725#include "absl/strings/string_view.h"
26#include "api/array_view.h"
27#include "api/crypto/crypto_options.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4728#include "api/dtls_transport_interface.h"
Harald Alvestrand8f429922022-05-04 10:32:3029#include "api/field_trials_view.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4730#include "api/rtp_parameters.h"
31#include "api/rtp_receiver_interface.h"
32#include "api/rtp_sender_interface.h"
Harald Alvestrand763f5a92020-10-22 10:39:4033#include "api/video/builtin_video_bitrate_allocator_factory.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4734#include "media/base/codec.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4735#include "media/base/rid_description.h"
Harald Alvestrand85466662021-04-19 21:21:3636#include "p2p/base/ice_transport_internal.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4737#include "p2p/base/p2p_constants.h"
38#include "p2p/base/p2p_transport_channel.h"
39#include "p2p/base/port.h"
40#include "p2p/base/transport_description.h"
41#include "p2p/base/transport_description_factory.h"
42#include "p2p/base/transport_info.h"
Harald Alvestrand981c5722022-02-09 12:42:2743#include "pc/channel_interface.h"
Harald Alvestrand85466662021-04-19 21:21:3644#include "pc/dtls_transport.h"
Henrik Boströmf7859892022-07-04 12:36:3745#include "pc/legacy_stats_collector.h"
Harald Alvestrandcdcfab02020-09-28 13:02:0746#include "pc/media_stream.h"
Markus Handella1b82012021-05-26 16:56:3047#include "pc/media_stream_proxy.h"
Harald Alvestrand8e344192022-02-08 12:55:4248#include "pc/peer_connection_internal.h"
Harald Alvestrand1090e442020-10-05 07:01:0949#include "pc/peer_connection_message_handler.h"
Harald Alvestrandcdcfab02020-09-28 13:02:0750#include "pc/rtp_media_utils.h"
Harald Alvestrand981c5722022-02-09 12:42:2751#include "pc/rtp_receiver_proxy.h"
Harald Alvestrand3eaee6b2020-10-19 06:35:5552#include "pc/rtp_sender.h"
Harald Alvestrand981c5722022-02-09 12:42:2753#include "pc/rtp_sender_proxy.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4754#include "pc/simulcast_description.h"
55#include "pc/usage_pattern.h"
Harald Alvestrand763f5a92020-10-22 10:39:4056#include "pc/webrtc_session_description_factory.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4757#include "rtc_base/helpers.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4758#include "rtc_base/logging.h"
Harald Alvestrand763f5a92020-10-22 10:39:4059#include "rtc_base/rtc_certificate.h"
Harald Alvestrand1f7eab62020-10-18 16:51:4760#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:0763#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:3388typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions
89 RTCOfferAnswerOptions;
90
Harald Alvestrandcdcfab02020-09-28 13:02:0791// 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:31107const char kSdpWithoutSdesCrypto[] = "Called with SDP without SDES crypto.";
Harald Alvestrandcdcfab02020-09-28 13:02:07108
Harald Alvestranda474fbf2020-10-01 16:47:23109const char kSessionError[] = "Session error code: ";
110const char kSessionErrorDesc[] = "Session error description: ";
111
Harald Alvestrandcdcfab02020-09-28 13:02:07112// 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:41119// The length of RTCP CNAMEs.
120static const int kRtcpCnameLength = 16;
121
Philipp Hancke187e9d42021-11-08 08:58:15122// The maximum length of the MID attribute.
Philipp Hancke063bb382021-11-29 11:05:38123// TODO(bugs.webrtc.org/12517) - reduce to 16 again.
124static constexpr size_t kMidMaxSize = 32;
Philipp Hancke187e9d42021-11-08 08:58:15125
Harald Alvestranda474fbf2020-10-01 16:47:23126const 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:07131void NoteAddIceCandidateResult(int result) {
132 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result,
133 kAddIceCandidateMax);
134}
135
Henrik Boströmf8187e02021-04-26 19:04:26136std::map<std::string, const cricket::ContentGroup*> GetBundleGroupsByMid(
137 const SessionDescription* desc) {
138 std::vector<const cricket::ContentGroup*> bundle_groups =
139 desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
140 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid;
141 for (const cricket::ContentGroup* bundle_group : bundle_groups) {
142 for (const std::string& content_name : bundle_group->content_names()) {
143 bundle_groups_by_mid[content_name] = bundle_group;
144 }
145 }
146 return bundle_groups_by_mid;
147}
148
Artem Titov880fa812021-07-30 20:30:23149// Returns true if `new_desc` requests an ICE restart (i.e., new ufrag/pwd).
Harald Alvestrandcdcfab02020-09-28 13:02:07150bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc,
151 const SessionDescriptionInterface* new_desc,
152 const std::string& content_name) {
153 if (!old_desc) {
154 return false;
155 }
156 const SessionDescription* new_sd = new_desc->description();
157 const SessionDescription* old_sd = old_desc->description();
158 const ContentInfo* cinfo = new_sd->GetContentByName(content_name);
159 if (!cinfo || cinfo->rejected) {
160 return false;
161 }
162 // If the content isn't rejected, check if ufrag and password has changed.
163 const cricket::TransportDescription* new_transport_desc =
164 new_sd->GetTransportDescriptionByName(content_name);
165 const cricket::TransportDescription* old_transport_desc =
166 old_sd->GetTransportDescriptionByName(content_name);
167 if (!new_transport_desc || !old_transport_desc) {
168 // No transport description exists. This is not an ICE restart.
169 return false;
170 }
171 if (cricket::IceCredentialsChanged(
172 old_transport_desc->ice_ufrag, old_transport_desc->ice_pwd,
173 new_transport_desc->ice_ufrag, new_transport_desc->ice_pwd)) {
174 RTC_LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name
175 << ".";
176 return true;
177 }
178 return false;
179}
180
181// Generates a string error message for SetLocalDescription/SetRemoteDescription
182// from an RTCError.
183std::string GetSetDescriptionErrorMessage(cricket::ContentSource source,
184 SdpType type,
185 const RTCError& error) {
186 rtc::StringBuilder oss;
187 oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote")
Tomas Gunnarsson0dd75392022-01-17 18:19:56188 << " " << SdpTypeToString(type) << " sdp: ";
189 RTC_DCHECK(!absl::StartsWith(error.message(), oss.str())) << error.message();
190 oss << error.message();
Harald Alvestrandcdcfab02020-09-28 13:02:07191 return oss.Release();
192}
193
194std::string GetStreamIdsString(rtc::ArrayView<const std::string> stream_ids) {
195 std::string output = "streams=[";
196 const char* separator = "";
197 for (const auto& stream_id : stream_ids) {
198 output.append(separator).append(stream_id);
199 separator = ", ";
200 }
201 output.append("]");
202 return output;
203}
204
205void ReportSimulcastApiVersion(const char* name,
206 const SessionDescription& session) {
207 bool has_legacy = false;
208 bool has_spec_compliant = false;
209 for (const ContentInfo& content : session.contents()) {
210 if (!content.media_description()) {
211 continue;
212 }
213 has_spec_compliant |= content.media_description()->HasSimulcast();
214 for (const StreamParams& sp : content.media_description()->streams()) {
215 has_legacy |= sp.has_ssrc_group(cricket::kSimSsrcGroupSemantics);
216 }
217 }
218
219 if (has_legacy) {
220 RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionLegacy,
221 kSimulcastApiVersionMax);
222 }
223 if (has_spec_compliant) {
224 RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionSpecCompliant,
225 kSimulcastApiVersionMax);
226 }
227 if (!has_legacy && !has_spec_compliant) {
228 RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionNone,
229 kSimulcastApiVersionMax);
230 }
231}
232
233const ContentInfo* FindTransceiverMSection(
Harald Alvestrand85466662021-04-19 21:21:36234 RtpTransceiver* transceiver,
Harald Alvestrandcdcfab02020-09-28 13:02:07235 const SessionDescriptionInterface* session_description) {
236 return transceiver->mid()
237 ? session_description->description()->GetContentByName(
238 *transceiver->mid())
239 : nullptr;
240}
241
242// If the direction is "recvonly" or "inactive", treat the description
243// as containing no streams.
244// See: https://code.google.com/p/webrtc/issues/detail?id=5054
245std::vector<cricket::StreamParams> GetActiveStreams(
246 const cricket::MediaContentDescription* desc) {
247 return RtpTransceiverDirectionHasSend(desc->direction())
248 ? desc->streams()
249 : std::vector<cricket::StreamParams>();
250}
251
252// Logic to decide if an m= section can be recycled. This means that the new
253// m= section is not rejected, but the old local or remote m= section is
Artem Titov880fa812021-07-30 20:30:23254// rejected. `old_content_one` and `old_content_two` refer to the m= section
Harald Alvestrandcdcfab02020-09-28 13:02:07255// of the old remote and old local descriptions in no particular order.
256// We need to check both the old local and remote because either
257// could be the most current from the latest negotation.
258bool IsMediaSectionBeingRecycled(SdpType type,
259 const ContentInfo& content,
260 const ContentInfo* old_content_one,
261 const ContentInfo* old_content_two) {
262 return type == SdpType::kOffer && !content.rejected &&
263 ((old_content_one && old_content_one->rejected) ||
264 (old_content_two && old_content_two->rejected));
265}
266
Artem Titov880fa812021-07-30 20:30:23267// Verify that the order of media sections in `new_desc` matches
268// `current_desc`. The number of m= sections in `new_desc` should be no
269// less than `current_desc`. In the case of checking an answer's
270// `new_desc`, the `current_desc` is the last offer that was set as the
271// local or remote. In the case of checking an offer's `new_desc` we
Harald Alvestrandcdcfab02020-09-28 13:02:07272// check against the local and remote descriptions stored from the last
273// negotiation, because either of these could be the most up to date for
Artem Titov880fa812021-07-30 20:30:23274// possible rejected m sections. These are the `current_desc` and
275// `secondary_current_desc`.
Harald Alvestrandcdcfab02020-09-28 13:02:07276bool MediaSectionsInSameOrder(const SessionDescription& current_desc,
277 const SessionDescription* secondary_current_desc,
278 const SessionDescription& new_desc,
279 const SdpType type) {
280 if (current_desc.contents().size() > new_desc.contents().size()) {
281 return false;
282 }
283
284 for (size_t i = 0; i < current_desc.contents().size(); ++i) {
285 const cricket::ContentInfo* secondary_content_info = nullptr;
286 if (secondary_current_desc &&
287 i < secondary_current_desc->contents().size()) {
288 secondary_content_info = &secondary_current_desc->contents()[i];
289 }
290 if (IsMediaSectionBeingRecycled(type, new_desc.contents()[i],
291 &current_desc.contents()[i],
292 secondary_content_info)) {
293 // For new offer descriptions, if the media section can be recycled, it's
294 // valid for the MID and media type to change.
295 continue;
296 }
297 if (new_desc.contents()[i].name != current_desc.contents()[i].name) {
298 return false;
299 }
300 const MediaContentDescription* new_desc_mdesc =
301 new_desc.contents()[i].media_description();
302 const MediaContentDescription* current_desc_mdesc =
303 current_desc.contents()[i].media_description();
304 if (new_desc_mdesc->type() != current_desc_mdesc->type()) {
305 return false;
306 }
307 }
308 return true;
309}
310
311bool MediaSectionsHaveSameCount(const SessionDescription& desc1,
312 const SessionDescription& desc2) {
313 return desc1.contents().size() == desc2.contents().size();
314}
Harald Alvestrand0d018412021-11-04 13:52:31315// Checks that each non-rejected content has SDES crypto keys or a DTLS
Harald Alvestrandcdcfab02020-09-28 13:02:07316// fingerprint, unless it's in a BUNDLE group, in which case only the
317// BUNDLE-tag section (first media section/description in the BUNDLE group)
318// needs a ufrag and pwd. Mismatches, such as replying with a DTLS fingerprint
319// to SDES keys, will be caught in JsepTransport negotiation, and backstopped
Artem Titov880fa812021-07-30 20:30:23320// by Channel's `srtp_required` check.
Henrik Boströmf8187e02021-04-26 19:04:26321RTCError VerifyCrypto(const SessionDescription* desc,
Harald Alvestrand0d018412021-11-04 13:52:31322 bool dtls_enabled,
Henrik Boströmf8187e02021-04-26 19:04:26323 const std::map<std::string, const cricket::ContentGroup*>&
324 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:07325 for (const cricket::ContentInfo& content_info : desc->contents()) {
326 if (content_info.rejected) {
327 continue;
328 }
Harald Alvestrand22d32f12022-10-03 13:16:28329#if !defined(WEBRTC_FUCHSIA)
330 RTC_CHECK(dtls_enabled) << "SDES protocol is only allowed in Fuchsia";
331#endif
Harald Alvestrandcdcfab02020-09-28 13:02:07332 const std::string& mid = content_info.name;
Henrik Boströmf8187e02021-04-26 19:04:26333 auto it = bundle_groups_by_mid.find(mid);
334 const cricket::ContentGroup* bundle =
335 it != bundle_groups_by_mid.end() ? it->second : nullptr;
336 if (bundle && mid != *(bundle->FirstContentName())) {
Harald Alvestrandcdcfab02020-09-28 13:02:07337 // This isn't the first media section in the BUNDLE group, so it's not
338 // required to have crypto attributes, since only the crypto attributes
339 // from the first section actually get used.
340 continue;
341 }
342
343 // If the content isn't rejected or bundled into another m= section, crypto
344 // must be present.
345 const MediaContentDescription* media = content_info.media_description();
346 const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
347 if (!media || !tinfo) {
348 // Something is not right.
349 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
350 }
Harald Alvestrand0d018412021-11-04 13:52:31351 if (dtls_enabled) {
352 if (!tinfo->description.identity_fingerprint) {
353 RTC_LOG(LS_WARNING)
354 << "Session description must have DTLS fingerprint if "
355 "DTLS enabled.";
356 return RTCError(RTCErrorType::INVALID_PARAMETER,
357 kSdpWithoutDtlsFingerprint);
358 }
359 } else {
360 if (media->cryptos().empty()) {
361 RTC_LOG(LS_WARNING)
362 << "Session description must have SDES when DTLS disabled.";
363 return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto);
364 }
Harald Alvestrandcdcfab02020-09-28 13:02:07365 }
366 }
367 return RTCError::OK();
368}
369
370// Checks that each non-rejected content has ice-ufrag and ice-pwd set, unless
371// it's in a BUNDLE group, in which case only the BUNDLE-tag section (first
372// media section/description in the BUNDLE group) needs a ufrag and pwd.
Henrik Boströmf8187e02021-04-26 19:04:26373bool VerifyIceUfragPwdPresent(
374 const SessionDescription* desc,
375 const std::map<std::string, const cricket::ContentGroup*>&
376 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:07377 for (const cricket::ContentInfo& content_info : desc->contents()) {
378 if (content_info.rejected) {
379 continue;
380 }
381 const std::string& mid = content_info.name;
Henrik Boströmf8187e02021-04-26 19:04:26382 auto it = bundle_groups_by_mid.find(mid);
383 const cricket::ContentGroup* bundle =
384 it != bundle_groups_by_mid.end() ? it->second : nullptr;
385 if (bundle && mid != *(bundle->FirstContentName())) {
Harald Alvestrandcdcfab02020-09-28 13:02:07386 // This isn't the first media section in the BUNDLE group, so it's not
387 // required to have ufrag/password, since only the ufrag/password from
388 // the first section actually get used.
389 continue;
390 }
391
392 // If the content isn't rejected or bundled into another m= section,
393 // ice-ufrag and ice-pwd must be present.
394 const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
395 if (!tinfo) {
396 // Something is not right.
397 RTC_LOG(LS_ERROR) << kInvalidSdp;
398 return false;
399 }
400 if (tinfo->description.ice_ufrag.empty() ||
401 tinfo->description.ice_pwd.empty()) {
402 RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
403 return false;
404 }
405 }
406 return true;
407}
408
Harald Alvestrand85466662021-04-19 21:21:36409RTCError ValidateMids(const cricket::SessionDescription& description) {
Harald Alvestrandcdcfab02020-09-28 13:02:07410 std::set<std::string> mids;
Philipp Hancke063bb382021-11-29 11:05:38411 size_t max_length = 0;
Harald Alvestrandcdcfab02020-09-28 13:02:07412 for (const cricket::ContentInfo& content : description.contents()) {
413 if (content.name.empty()) {
414 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
415 "A media section is missing a MID attribute.");
416 }
Philipp Hancke063bb382021-11-29 11:05:38417 max_length = std::max(max_length, content.name.size());
Philipp Hancke187e9d42021-11-08 08:58:15418 if (content.name.size() > kMidMaxSize) {
419 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
420 "The MID attribute exceeds the maximum supported "
Philipp Hancke063bb382021-11-29 11:05:38421 "length of 32 characters.");
Philipp Hancke187e9d42021-11-08 08:58:15422 }
Harald Alvestrandcdcfab02020-09-28 13:02:07423 if (!mids.insert(content.name).second) {
424 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
425 "Duplicate a=mid value '" + content.name + "'.");
426 }
427 }
Philipp Hancke063bb382021-11-29 11:05:38428 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.PeerConnection.Mid.Size", max_length, 0,
429 31, 32);
Harald Alvestrandcdcfab02020-09-28 13:02:07430 return RTCError::OK();
431}
432
433bool IsValidOfferToReceiveMedia(int value) {
434 typedef PeerConnectionInterface::RTCOfferAnswerOptions Options;
435 return (value >= Options::kUndefined) &&
436 (value <= Options::kMaxOfferToReceiveMedia);
437}
438
439bool ValidateOfferAnswerOptions(
440 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) {
441 return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) &&
442 IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video);
443}
444
Harald Alvestrandcdcfab02020-09-28 13:02:07445// This method will extract any send encodings that were sent by the remote
446// connection. This is currently only relevant for Simulcast scenario (where
447// the number of layers may be communicated by the server).
Harald Alvestrand85466662021-04-19 21:21:36448std::vector<RtpEncodingParameters> GetSendEncodingsFromRemoteDescription(
Harald Alvestrandcdcfab02020-09-28 13:02:07449 const MediaContentDescription& desc) {
450 if (!desc.HasSimulcast()) {
451 return {};
452 }
453 std::vector<RtpEncodingParameters> result;
454 const SimulcastDescription& simulcast = desc.simulcast_description();
455
456 // This is a remote description, the parameters we are after should appear
457 // as receive streams.
458 for (const auto& alternatives : simulcast.receive_layers()) {
459 RTC_DCHECK(!alternatives.empty());
460 // There is currently no way to specify or choose from alternatives.
461 // We will always use the first alternative, which is the most preferred.
462 const SimulcastLayer& layer = alternatives[0];
463 RtpEncodingParameters parameters;
464 parameters.rid = layer.rid;
465 parameters.active = !layer.is_paused;
466 result.push_back(parameters);
467 }
468
469 return result;
470}
471
Harald Alvestrand85466662021-04-19 21:21:36472RTCError UpdateSimulcastLayerStatusInSender(
Harald Alvestrandcdcfab02020-09-28 13:02:07473 const std::vector<SimulcastLayer>& layers,
474 rtc::scoped_refptr<RtpSenderInternal> sender) {
475 RTC_DCHECK(sender);
Harald Alvestrand0166be82022-08-25 11:31:01476 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
Harald Alvestrandcdcfab02020-09-28 13:02:07477 std::vector<std::string> disabled_layers;
478
479 // The simulcast envelope cannot be changed, only the status of the streams.
480 // So we will iterate over the send encodings rather than the layers.
481 for (RtpEncodingParameters& encoding : parameters.encodings) {
482 auto iter = std::find_if(layers.begin(), layers.end(),
483 [&encoding](const SimulcastLayer& layer) {
484 return layer.rid == encoding.rid;
485 });
486 // A layer that cannot be found may have been removed by the remote party.
487 if (iter == layers.end()) {
488 disabled_layers.push_back(encoding.rid);
489 continue;
490 }
491
492 encoding.active = !iter->is_paused;
493 }
494
Harald Alvestrand0166be82022-08-25 11:31:01495 RTCError result = sender->SetParametersInternalWithAllLayers(parameters);
Harald Alvestrandcdcfab02020-09-28 13:02:07496 if (result.ok()) {
497 result = sender->DisableEncodingLayers(disabled_layers);
498 }
499
500 return result;
501}
502
Harald Alvestrand85466662021-04-19 21:21:36503bool SimulcastIsRejected(const ContentInfo* local_content,
Lennart Grahl0d0ed762021-05-17 14:06:37504 const MediaContentDescription& answer_media_desc,
505 bool enable_encrypted_rtp_header_extensions) {
Harald Alvestrandcdcfab02020-09-28 13:02:07506 bool simulcast_offered = local_content &&
507 local_content->media_description() &&
508 local_content->media_description()->HasSimulcast();
509 bool simulcast_answered = answer_media_desc.HasSimulcast();
510 bool rids_supported = RtpExtension::FindHeaderExtensionByUri(
Lennart Grahl0d0ed762021-05-17 14:06:37511 answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri,
512 enable_encrypted_rtp_header_extensions
513 ? RtpExtension::Filter::kPreferEncryptedExtension
514 : RtpExtension::Filter::kDiscardEncryptedExtension);
Harald Alvestrandcdcfab02020-09-28 13:02:07515 return simulcast_offered && (!simulcast_answered || !rids_supported);
516}
517
Harald Alvestrand85466662021-04-19 21:21:36518RTCError DisableSimulcastInSender(
Harald Alvestrandcdcfab02020-09-28 13:02:07519 rtc::scoped_refptr<RtpSenderInternal> sender) {
520 RTC_DCHECK(sender);
Harald Alvestrand0166be82022-08-25 11:31:01521 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
Harald Alvestrandcdcfab02020-09-28 13:02:07522 if (parameters.encodings.size() <= 1) {
523 return RTCError::OK();
524 }
525
526 std::vector<std::string> disabled_layers;
527 std::transform(
528 parameters.encodings.begin() + 1, parameters.encodings.end(),
529 std::back_inserter(disabled_layers),
530 [](const RtpEncodingParameters& encoding) { return encoding.rid; });
531 return sender->DisableEncodingLayers(disabled_layers);
532}
533
Harald Alvestrandc06e3742020-10-01 10:23:33534// The SDP parser used to populate these values by default for the 'content
535// name' if an a=mid line was absent.
Harald Alvestrand85466662021-04-19 21:21:36536absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) {
Harald Alvestrandc06e3742020-10-01 10:23:33537 switch (media_type) {
538 case cricket::MEDIA_TYPE_AUDIO:
539 return cricket::CN_AUDIO;
540 case cricket::MEDIA_TYPE_VIDEO:
541 return cricket::CN_VIDEO;
542 case cricket::MEDIA_TYPE_DATA:
543 return cricket::CN_DATA;
Philipp Hancke4e8c1152020-10-13 10:43:15544 case cricket::MEDIA_TYPE_UNSUPPORTED:
545 return "not supported";
Harald Alvestrandc06e3742020-10-01 10:23:33546 }
Artem Titovd3251962021-11-15 15:57:07547 RTC_DCHECK_NOTREACHED();
Harald Alvestrandc06e3742020-10-01 10:23:33548 return "";
549}
550
Artem Titov880fa812021-07-30 20:30:23551// Add options to |[audio/video]_media_description_options| from `senders`.
Harald Alvestrandc06e3742020-10-01 10:23:33552void AddPlanBRtpSenderOptions(
553 const std::vector<rtc::scoped_refptr<
554 RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
555 cricket::MediaDescriptionOptions* audio_media_description_options,
556 cricket::MediaDescriptionOptions* video_media_description_options,
557 int num_sim_layers) {
558 for (const auto& sender : senders) {
559 if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
560 if (audio_media_description_options) {
561 audio_media_description_options->AddAudioSender(
562 sender->id(), sender->internal()->stream_ids());
563 }
564 } else {
565 RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO);
566 if (video_media_description_options) {
567 video_media_description_options->AddVideoSender(
568 sender->id(), sender->internal()->stream_ids(), {},
569 SimulcastLayerList(), num_sim_layers);
570 }
571 }
572 }
573}
574
Harald Alvestrand85466662021-04-19 21:21:36575cricket::MediaDescriptionOptions GetMediaDescriptionOptionsForTransceiver(
576 RtpTransceiver* transceiver,
Harald Alvestrandc06e3742020-10-01 10:23:33577 const std::string& mid,
578 bool is_create_offer) {
579 // NOTE: a stopping transceiver should be treated as a stopped one in
580 // createOffer as specified in
581 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
582 bool stopped =
583 is_create_offer ? transceiver->stopping() : transceiver->stopped();
584 cricket::MediaDescriptionOptions media_description_options(
585 transceiver->media_type(), mid, transceiver->direction(), stopped);
586 media_description_options.codec_preferences =
587 transceiver->codec_preferences();
588 media_description_options.header_extensions =
589 transceiver->HeaderExtensionsToOffer();
590 // This behavior is specified in JSEP. The gist is that:
591 // 1. The MSID is included if the RtpTransceiver's direction is sendonly or
592 // sendrecv.
593 // 2. If the MSID is included, then it must be included in any subsequent
594 // offer/answer exactly the same until the RtpTransceiver is stopped.
595 if (stopped || (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
Harald Alvestrand85466662021-04-19 21:21:36596 !transceiver->has_ever_been_used_to_send())) {
Harald Alvestrandc06e3742020-10-01 10:23:33597 return media_description_options;
598 }
599
600 cricket::SenderOptions sender_options;
601 sender_options.track_id = transceiver->sender()->id();
602 sender_options.stream_ids = transceiver->sender()->stream_ids();
603
604 // The following sets up RIDs and Simulcast.
605 // RIDs are included if Simulcast is requested or if any RID was specified.
606 RtpParameters send_parameters =
Harald Alvestrand0166be82022-08-25 11:31:01607 transceiver->sender_internal()->GetParametersInternalWithAllLayers();
Harald Alvestrandc06e3742020-10-01 10:23:33608 bool has_rids = std::any_of(send_parameters.encodings.begin(),
609 send_parameters.encodings.end(),
610 [](const RtpEncodingParameters& encoding) {
611 return !encoding.rid.empty();
612 });
613
614 std::vector<RidDescription> send_rids;
615 SimulcastLayerList send_layers;
616 for (const RtpEncodingParameters& encoding : send_parameters.encodings) {
617 if (encoding.rid.empty()) {
618 continue;
619 }
620 send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend));
621 send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
622 }
623
624 if (has_rids) {
625 sender_options.rids = send_rids;
626 }
627
628 sender_options.simulcast_layers = send_layers;
629 // When RIDs are configured, we must set num_sim_layers to 0 to.
630 // Otherwise, num_sim_layers must be 1 because either there is no
631 // simulcast, or simulcast is acheived by munging the SDP.
632 sender_options.num_sim_layers = has_rids ? 0 : 1;
633 media_description_options.sender_options.push_back(sender_options);
634
635 return media_description_options;
636}
637
Artem Titov880fa812021-07-30 20:30:23638// Returns the ContentInfo at mline index `i`, or null if none exists.
Harald Alvestrand85466662021-04-19 21:21:36639const ContentInfo* GetContentByIndex(const SessionDescriptionInterface* sdesc,
640 size_t i) {
Harald Alvestrandc06e3742020-10-01 10:23:33641 if (!sdesc) {
642 return nullptr;
643 }
644 const ContentInfos& contents = sdesc->description()->contents();
645 return (i < contents.size() ? &contents[i] : nullptr);
646}
647
Artem Titov880fa812021-07-30 20:30:23648// From `rtc_options`, fill parts of `session_options` shared by all generated
Harald Alvestrandc06e3742020-10-01 10:23:33649// m= sectionss (in other words, nothing that involves a map/array).
650void ExtractSharedMediaSessionOptions(
651 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
652 cricket::MediaSessionOptions* session_options) {
653 session_options->vad_enabled = rtc_options.voice_activity_detection;
654 session_options->bundle_enabled = rtc_options.use_rtp_mux;
655 session_options->raw_packetization_for_video =
656 rtc_options.raw_packetization_for_video;
657}
658
Harald Alvestrandbc9ca252020-10-05 13:08:41659// Generate a RTCP CNAME when a PeerConnection is created.
660std::string GenerateRtcpCname() {
661 std::string cname;
662 if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) {
663 RTC_LOG(LS_ERROR) << "Failed to generate CNAME.";
Artem Titovd3251962021-11-15 15:57:07664 RTC_DCHECK_NOTREACHED();
Harald Alvestrandbc9ca252020-10-05 13:08:41665 }
666 return cname;
667}
668
Artem Titov880fa812021-07-30 20:30:23669// Check if we can send `new_stream` on a PeerConnection.
Harald Alvestrand6f04b6532020-10-09 11:42:17670bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams,
671 webrtc::MediaStreamInterface* new_stream) {
672 if (!new_stream || !current_streams) {
673 return false;
674 }
675 if (current_streams->find(new_stream->id()) != nullptr) {
676 RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id()
677 << " is already added.";
678 return false;
679 }
680 return true;
681}
682
Tomas Gunnarsson92eebef2021-02-10 12:05:44683rtc::scoped_refptr<webrtc::DtlsTransport> LookupDtlsTransportByMid(
684 rtc::Thread* network_thread,
685 JsepTransportController* controller,
686 const std::string& mid) {
687 // TODO(tommi): Can we post this (and associated operations where this
Danil Chapovalov9e09a1f2022-09-08 16:38:10688 // function is called) to the network thread and avoid this BlockingCall?
Tomas Gunnarsson92eebef2021-02-10 12:05:44689 // We might be able to simplify a few things if we set the transport on
690 // the network thread and then update the implementation to check that
691 // the set_ and relevant get methods are always called on the network
692 // thread (we'll need to update proxy maps).
Danil Chapovalov9e09a1f2022-09-08 16:38:10693 return network_thread->BlockingCall(
Tomas Gunnarsson92eebef2021-02-10 12:05:44694 [controller, &mid] { return controller->LookupDtlsTransportByMid(mid); });
695}
696
Henrik Boström4ea80f32021-06-09 08:29:50697bool ContentHasHeaderExtension(const cricket::ContentInfo& content_info,
698 absl::string_view header_extension_uri) {
699 for (const RtpExtension& rtp_header_extension :
700 content_info.media_description()->rtp_header_extensions()) {
701 if (rtp_header_extension.uri == header_extension_uri) {
702 return true;
703 }
704 }
705 return false;
706}
707
Harald Alvestrandcdcfab02020-09-28 13:02:07708} // namespace
709
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04710// This class stores state related to a SetRemoteDescription operation, captures
711// and reports potential errors that migth occur and makes sure to notify the
712// observer of the operation and the operations chain of completion.
713class SdpOfferAnswerHandler::RemoteDescriptionOperation {
714 public:
715 RemoteDescriptionOperation(
716 SdpOfferAnswerHandler* handler,
717 std::unique_ptr<SessionDescriptionInterface> desc,
718 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer,
719 std::function<void()> operations_chain_callback)
720 : handler_(handler),
721 desc_(std::move(desc)),
722 observer_(std::move(observer)),
Tomas Gunnarsson0dd75392022-01-17 18:19:56723 operations_chain_callback_(std::move(operations_chain_callback)),
724 unified_plan_(handler_->IsUnifiedPlan()) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04725 if (!desc_) {
Tomas Gunnarsson0dd75392022-01-17 18:19:56726 type_ = static_cast<SdpType>(-1);
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04727 InvalidParam("SessionDescription is NULL.");
728 } else {
729 type_ = desc_->GetType();
730 }
731 }
732
733 ~RemoteDescriptionOperation() {
734 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
735 SignalCompletion();
736 operations_chain_callback_();
737 }
738
739 bool ok() const { return error_.ok(); }
740
741 // Notifies the observer that the operation is complete and releases the
742 // reference to the observer.
743 void SignalCompletion() {
Tomas Gunnarsson0dd75392022-01-17 18:19:56744 if (!observer_)
745 return;
746
747 if (!error_.ok() && type_ != static_cast<SdpType>(-1)) {
748 std::string error_message =
749 GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type_, error_);
750 RTC_LOG(LS_ERROR) << error_message;
751 error_.set_message(std::move(error_message));
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04752 }
Tomas Gunnarsson0dd75392022-01-17 18:19:56753
754 observer_->OnSetRemoteDescriptionComplete(error_);
755 observer_ = nullptr; // Only fire the notification once.
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04756 }
757
758 // If a session error has occurred the PeerConnection is in a possibly
759 // inconsistent state so fail right away.
760 bool HaveSessionError() {
761 RTC_DCHECK(ok());
Tomas Gunnarsson0dd75392022-01-17 18:19:56762 if (handler_->session_error() != SessionError::kNone)
763 InternalError(handler_->GetSessionErrorMsg());
764 return !ok();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04765 }
766
767 // Returns true if the operation was a rollback operation. If this function
768 // returns true, the caller should consider the operation complete. Otherwise
769 // proceed to the next step.
770 bool MaybeRollback() {
771 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
772 RTC_DCHECK(ok());
773 if (type_ != SdpType::kRollback) {
774 // Check if we can do an implicit rollback.
Tomas Gunnarsson0dd75392022-01-17 18:19:56775 if (type_ == SdpType::kOffer && unified_plan_ &&
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04776 handler_->pc_->configuration()->enable_implicit_rollback &&
777 handler_->signaling_state() ==
778 PeerConnectionInterface::kHaveLocalOffer) {
779 handler_->Rollback(type_);
780 }
781 return false;
782 }
783
Tomas Gunnarsson0dd75392022-01-17 18:19:56784 if (unified_plan_) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04785 error_ = handler_->Rollback(type_);
786 } else if (type_ == SdpType::kRollback) {
787 Unsupported("Rollback not supported in Plan B");
788 }
789
790 return true;
791 }
792
793 // Report to UMA the format of the received offer or answer.
794 void ReportOfferAnswerUma() {
795 RTC_DCHECK(ok());
796 if (type_ == SdpType::kOffer || type_ == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04797 handler_->pc_->ReportSdpBundleUsage(*desc_.get());
798 }
799 }
800
801 // Checks if the session description for the operation is valid. If not, the
802 // function captures error information and returns false. Note that if the
803 // return value is false, the operation should be considered done.
804 bool IsDescriptionValid() {
805 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
806 RTC_DCHECK(ok());
807 RTC_DCHECK(bundle_groups_by_mid_.empty()) << "Already called?";
808 bundle_groups_by_mid_ = GetBundleGroupsByMid(description());
809 error_ = handler_->ValidateSessionDescription(
810 desc_.get(), cricket::CS_REMOTE, bundle_groups_by_mid_);
Tomas Gunnarsson0dd75392022-01-17 18:19:56811 return ok();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04812 }
813
Tomas Gunnarsson0dd75392022-01-17 18:19:56814 // Transfers ownership of the session description object over to `handler_`.
815 bool ReplaceRemoteDescriptionAndCheckEror() {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04816 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
817 RTC_DCHECK(ok());
Tomas Gunnarsson0dd75392022-01-17 18:19:56818 RTC_DCHECK(desc_);
819 RTC_DCHECK(!replaced_remote_description_);
820#if RTC_DCHECK_IS_ON
821 const auto* existing_remote_description = handler_->remote_description();
822#endif
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04823
Tomas Gunnarsson0dd75392022-01-17 18:19:56824 error_ = handler_->ReplaceRemoteDescription(std::move(desc_), type_,
825 &replaced_remote_description_);
826
827 if (ok()) {
828#if RTC_DCHECK_IS_ON
829 // Sanity check that our `old_remote_description()` method always returns
830 // the same value as `remote_description()` did before the call to
831 // ReplaceRemoteDescription.
832 RTC_DCHECK_EQ(existing_remote_description, old_remote_description());
833#endif
834 } else {
835 SetAsSessionError();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04836 }
837
Tomas Gunnarsson0dd75392022-01-17 18:19:56838 return ok();
839 }
840
841 bool UpdateChannels() {
842 RTC_DCHECK(ok());
843 RTC_DCHECK(!desc_) << "ReplaceRemoteDescription hasn't been called";
844
845 const auto* remote_description = handler_->remote_description();
846
847 const cricket::SessionDescription* session_desc =
848 remote_description->description();
849
850 // Transport and Media channels will be created only when offer is set.
851 if (unified_plan_) {
852 error_ = handler_->UpdateTransceiversAndDataChannels(
853 cricket::CS_REMOTE, *remote_description,
854 handler_->local_description(), old_remote_description(),
855 bundle_groups_by_mid_);
856 } else {
857 // Media channels will be created only when offer is set. These may use
858 // new transports just created by PushdownTransportDescription.
859 if (type_ == SdpType::kOffer) {
860 // TODO(mallinath) - Handle CreateChannel failure, as new local
861 // description is applied. Restore back to old description.
862 error_ = handler_->CreateChannels(*session_desc);
863 }
864 // Remove unused channels if MediaContentDescription is rejected.
865 handler_->RemoveUnusedChannels(session_desc);
866 }
867
868 return ok();
869 }
870
871 bool UpdateSessionState() {
872 RTC_DCHECK(ok());
873 error_ = handler_->UpdateSessionState(
874 type_, cricket::CS_REMOTE,
875 handler_->remote_description()->description(), bundle_groups_by_mid_);
876 if (!ok())
877 SetAsSessionError();
878 return ok();
879 }
880
881 bool UseCandidatesInRemoteDescription() {
882 RTC_DCHECK(ok());
883 if (handler_->local_description() &&
884 !handler_->UseCandidatesInRemoteDescription()) {
885 InvalidParam(kInvalidCandidates);
886 }
887 return ok();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04888 }
889
890 // Convenience getter for desc_->GetType().
891 SdpType type() const { return type_; }
Tomas Gunnarsson0dd75392022-01-17 18:19:56892 bool unified_plan() const { return unified_plan_; }
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04893 cricket::SessionDescription* description() { return desc_->description(); }
894
Tomas Gunnarsson0dd75392022-01-17 18:19:56895 const SessionDescriptionInterface* old_remote_description() const {
896 RTC_DCHECK(!desc_) << "Called before replacing the remote description";
897 if (type_ == SdpType::kAnswer)
898 return replaced_remote_description_.get();
899 return replaced_remote_description_
900 ? replaced_remote_description_.get()
901 : handler_->current_remote_description();
902 }
903
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04904 // Returns a reference to a cached map of bundle groups ordered by mid.
905 // Note that this will only be valid after a successful call to
906 // `IsDescriptionValid`.
907 const std::map<std::string, const cricket::ContentGroup*>&
908 bundle_groups_by_mid() const {
909 RTC_DCHECK(ok());
910 return bundle_groups_by_mid_;
911 }
912
913 private:
914 // Convenience methods for populating the embedded `error_` object.
915 void Unsupported(std::string message) {
916 SetError(RTCErrorType::UNSUPPORTED_OPERATION, std::move(message));
917 }
918
919 void InvalidParam(std::string message) {
920 SetError(RTCErrorType::INVALID_PARAMETER, std::move(message));
921 }
922
Tomas Gunnarsson0dd75392022-01-17 18:19:56923 void InternalError(std::string message) {
924 SetError(RTCErrorType::INTERNAL_ERROR, std::move(message));
925 }
926
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04927 void SetError(RTCErrorType type, std::string message) {
928 RTC_DCHECK(ok()) << "Overwriting an existing error?";
929 error_ = RTCError(type, std::move(message));
930 }
931
Tomas Gunnarsson0dd75392022-01-17 18:19:56932 // Called when the PeerConnection could be in an inconsistent state and we set
933 // the session error so that future calls to
934 // SetLocalDescription/SetRemoteDescription fail.
935 void SetAsSessionError() {
936 RTC_DCHECK(!ok());
937 handler_->SetSessionError(SessionError::kContent, error_.message());
938 }
939
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04940 SdpOfferAnswerHandler* const handler_;
941 std::unique_ptr<SessionDescriptionInterface> desc_;
Tomas Gunnarsson0dd75392022-01-17 18:19:56942 // Keeps the replaced session description object alive while the operation
943 // is taking place since methods that depend on `old_remote_description()`
944 // for updating the state, need it.
945 std::unique_ptr<SessionDescriptionInterface> replaced_remote_description_;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04946 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer_;
947 std::function<void()> operations_chain_callback_;
948 RTCError error_ = RTCError::OK();
949 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid_;
950 SdpType type_;
Tomas Gunnarsson0dd75392022-01-17 18:19:56951 const bool unified_plan_;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:04952};
Harald Alvestrandcdcfab02020-09-28 13:02:07953// Used by parameterless SetLocalDescription() to create an offer or answer.
954// Upon completion of creating the session description, SetLocalDescription() is
955// invoked with the result.
956class SdpOfferAnswerHandler::ImplicitCreateSessionDescriptionObserver
957 : public CreateSessionDescriptionObserver {
958 public:
959 ImplicitCreateSessionDescriptionObserver(
960 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler,
961 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
962 set_local_description_observer)
963 : sdp_handler_(std::move(sdp_handler)),
964 set_local_description_observer_(
965 std::move(set_local_description_observer)) {}
966 ~ImplicitCreateSessionDescriptionObserver() override {
967 RTC_DCHECK(was_called_);
968 }
969
970 void SetOperationCompleteCallback(
971 std::function<void()> operation_complete_callback) {
972 operation_complete_callback_ = std::move(operation_complete_callback);
973 }
974
975 bool was_called() const { return was_called_; }
976
977 void OnSuccess(SessionDescriptionInterface* desc_ptr) override {
978 RTC_DCHECK(!was_called_);
979 std::unique_ptr<SessionDescriptionInterface> desc(desc_ptr);
980 was_called_ = true;
981
Artem Titov880fa812021-07-30 20:30:23982 // Abort early if `pc_` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:07983 if (!sdp_handler_) {
984 operation_complete_callback_();
985 return;
986 }
987 // DoSetLocalDescription() is a synchronous operation that invokes
Artem Titov880fa812021-07-30 20:30:23988 // `set_local_description_observer_` with the result.
Harald Alvestrandcdcfab02020-09-28 13:02:07989 sdp_handler_->DoSetLocalDescription(
990 std::move(desc), std::move(set_local_description_observer_));
991 operation_complete_callback_();
992 }
993
994 void OnFailure(RTCError error) override {
995 RTC_DCHECK(!was_called_);
996 was_called_ = true;
997 set_local_description_observer_->OnSetLocalDescriptionComplete(RTCError(
998 error.type(), std::string("SetLocalDescription failed to create "
999 "session description - ") +
1000 error.message()));
1001 operation_complete_callback_();
1002 }
1003
1004 private:
1005 bool was_called_ = false;
1006 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler_;
1007 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
1008 set_local_description_observer_;
1009 std::function<void()> operation_complete_callback_;
1010};
1011
1012// Wraps a CreateSessionDescriptionObserver and an OperationsChain operation
1013// complete callback. When the observer is invoked, the wrapped observer is
1014// invoked followed by invoking the completion callback.
1015class CreateSessionDescriptionObserverOperationWrapper
1016 : public CreateSessionDescriptionObserver {
1017 public:
1018 CreateSessionDescriptionObserverOperationWrapper(
1019 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer,
1020 std::function<void()> operation_complete_callback)
1021 : observer_(std::move(observer)),
1022 operation_complete_callback_(std::move(operation_complete_callback)) {
1023 RTC_DCHECK(observer_);
1024 }
1025 ~CreateSessionDescriptionObserverOperationWrapper() override {
Tomas Gunnarsson36992362020-10-05 19:41:361026#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071027 RTC_DCHECK(was_called_);
Tomas Gunnarsson36992362020-10-05 19:41:361028#endif
Harald Alvestrandcdcfab02020-09-28 13:02:071029 }
1030
1031 void OnSuccess(SessionDescriptionInterface* desc) override {
Tomas Gunnarsson36992362020-10-05 19:41:361032#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071033 RTC_DCHECK(!was_called_);
Harald Alvestrandcdcfab02020-09-28 13:02:071034 was_called_ = true;
1035#endif // RTC_DCHECK_IS_ON
1036 // Completing the operation before invoking the observer allows the observer
1037 // to execute SetLocalDescription() without delay.
1038 operation_complete_callback_();
1039 observer_->OnSuccess(desc);
1040 }
1041
1042 void OnFailure(RTCError error) override {
Tomas Gunnarsson36992362020-10-05 19:41:361043#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071044 RTC_DCHECK(!was_called_);
Harald Alvestrandcdcfab02020-09-28 13:02:071045 was_called_ = true;
1046#endif // RTC_DCHECK_IS_ON
1047 operation_complete_callback_();
1048 observer_->OnFailure(std::move(error));
1049 }
1050
1051 private:
Tomas Gunnarsson36992362020-10-05 19:41:361052#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:071053 bool was_called_ = false;
1054#endif // RTC_DCHECK_IS_ON
1055 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer_;
1056 std::function<void()> operation_complete_callback_;
1057};
1058
1059// Wrapper for SetSessionDescriptionObserver that invokes the success or failure
1060// callback in a posted message handled by the peer connection. This introduces
1061// a delay that prevents recursive API calls by the observer, but this also
1062// means that the PeerConnection can be modified before the observer sees the
1063// result of the operation. This is ill-advised for synchronizing states.
1064//
1065// Implements both the SetLocalDescriptionObserverInterface and the
1066// SetRemoteDescriptionObserverInterface.
1067class SdpOfferAnswerHandler::SetSessionDescriptionObserverAdapter
1068 : public SetLocalDescriptionObserverInterface,
1069 public SetRemoteDescriptionObserverInterface {
1070 public:
1071 SetSessionDescriptionObserverAdapter(
1072 rtc::WeakPtr<SdpOfferAnswerHandler> handler,
1073 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer)
1074 : handler_(std::move(handler)),
1075 inner_observer_(std::move(inner_observer)) {}
1076
1077 // SetLocalDescriptionObserverInterface implementation.
1078 void OnSetLocalDescriptionComplete(RTCError error) override {
1079 OnSetDescriptionComplete(std::move(error));
1080 }
1081 // SetRemoteDescriptionObserverInterface implementation.
1082 void OnSetRemoteDescriptionComplete(RTCError error) override {
1083 OnSetDescriptionComplete(std::move(error));
1084 }
1085
1086 private:
1087 void OnSetDescriptionComplete(RTCError error) {
1088 if (!handler_)
1089 return;
1090 if (error.ok()) {
Harald Alvestrand1090e442020-10-05 07:01:091091 handler_->pc_->message_handler()->PostSetSessionDescriptionSuccess(
Niels Möllerafb246b2022-04-20 12:26:501092 inner_observer_.get());
Harald Alvestrandcdcfab02020-09-28 13:02:071093 } else {
Harald Alvestrand1090e442020-10-05 07:01:091094 handler_->pc_->message_handler()->PostSetSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:501095 inner_observer_.get(), std::move(error));
Harald Alvestrandcdcfab02020-09-28 13:02:071096 }
1097 }
1098
1099 rtc::WeakPtr<SdpOfferAnswerHandler> handler_;
1100 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer_;
1101};
1102
1103class SdpOfferAnswerHandler::LocalIceCredentialsToReplace {
1104 public:
1105 // Sets the ICE credentials that need restarting to the ICE credentials of
1106 // the current and pending descriptions.
1107 void SetIceCredentialsFromLocalDescriptions(
1108 const SessionDescriptionInterface* current_local_description,
1109 const SessionDescriptionInterface* pending_local_description) {
1110 ice_credentials_.clear();
1111 if (current_local_description) {
1112 AppendIceCredentialsFromSessionDescription(*current_local_description);
1113 }
1114 if (pending_local_description) {
1115 AppendIceCredentialsFromSessionDescription(*pending_local_description);
1116 }
1117 }
1118
1119 void ClearIceCredentials() { ice_credentials_.clear(); }
1120
1121 // Returns true if we have ICE credentials that need restarting.
1122 bool HasIceCredentials() const { return !ice_credentials_.empty(); }
1123
Artem Titov880fa812021-07-30 20:30:231124 // Returns true if `local_description` shares no ICE credentials with the
Harald Alvestrandcdcfab02020-09-28 13:02:071125 // ICE credentials that need restarting.
1126 bool SatisfiesIceRestart(
1127 const SessionDescriptionInterface& local_description) const {
1128 for (const auto& transport_info :
1129 local_description.description()->transport_infos()) {
1130 if (ice_credentials_.find(std::make_pair(
1131 transport_info.description.ice_ufrag,
1132 transport_info.description.ice_pwd)) != ice_credentials_.end()) {
1133 return false;
1134 }
1135 }
1136 return true;
1137 }
1138
1139 private:
1140 void AppendIceCredentialsFromSessionDescription(
1141 const SessionDescriptionInterface& desc) {
1142 for (const auto& transport_info : desc.description()->transport_infos()) {
1143 ice_credentials_.insert(
1144 std::make_pair(transport_info.description.ice_ufrag,
1145 transport_info.description.ice_pwd));
1146 }
1147 }
1148
1149 std::set<std::pair<std::string, std::string>> ice_credentials_;
1150};
1151
Harald Alvestrand66c40362022-01-28 17:41:301152SdpOfferAnswerHandler::SdpOfferAnswerHandler(PeerConnectionSdpMethods* pc,
1153 ConnectionContext* context)
Harald Alvestrandcdcfab02020-09-28 13:02:071154 : pc_(pc),
Harald Alvestrand66c40362022-01-28 17:41:301155 context_(context),
Harald Alvestrand6f04b6532020-10-09 11:42:171156 local_streams_(StreamCollection::Create()),
1157 remote_streams_(StreamCollection::Create()),
Harald Alvestrandcdcfab02020-09-28 13:02:071158 operations_chain_(rtc::OperationsChain::Create()),
Harald Alvestrandbc9ca252020-10-05 13:08:411159 rtcp_cname_(GenerateRtcpCname()),
Harald Alvestrandcdcfab02020-09-28 13:02:071160 local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()),
1161 weak_ptr_factory_(this) {
1162 operations_chain_->SetOnChainEmptyCallback(
1163 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr()]() {
1164 if (!this_weak_ptr)
1165 return;
1166 this_weak_ptr->OnOperationsChainEmpty();
1167 });
1168}
1169
1170SdpOfferAnswerHandler::~SdpOfferAnswerHandler() {}
1171
Harald Alvestrand9cd199d2020-10-27 07:10:431172// Static
1173std::unique_ptr<SdpOfferAnswerHandler> SdpOfferAnswerHandler::Create(
Harald Alvestrand5b661302022-01-28 13:08:341174 PeerConnectionSdpMethods* pc,
Harald Alvestrand9cd199d2020-10-27 07:10:431175 const PeerConnectionInterface::RTCConfiguration& configuration,
Harald Alvestrand66c40362022-01-28 17:41:301176 PeerConnectionDependencies& dependencies,
1177 ConnectionContext* context) {
1178 auto handler = absl::WrapUnique(new SdpOfferAnswerHandler(pc, context));
Jonas Orelanded99dae2022-03-09 08:28:101179 handler->Initialize(configuration, dependencies, context);
Harald Alvestrand9cd199d2020-10-27 07:10:431180 return handler;
1181}
1182
Harald Alvestrand763f5a92020-10-22 10:39:401183void SdpOfferAnswerHandler::Initialize(
1184 const PeerConnectionInterface::RTCConfiguration& configuration,
Jonas Orelanded99dae2022-03-09 08:28:101185 PeerConnectionDependencies& dependencies,
1186 ConnectionContext* context) {
Harald Alvestrand763f5a92020-10-22 10:39:401187 RTC_DCHECK_RUN_ON(signaling_thread());
Henrik Boström6344bf12022-05-10 07:34:271188 // 100 kbps is used by default, but can be overriden by a non-standard
1189 // RTCConfiguration value (not available on Web).
Harald Alvestrand763f5a92020-10-22 10:39:401190 video_options_.screencast_min_bitrate_kbps =
Henrik Boström6344bf12022-05-10 07:34:271191 configuration.screencast_min_bitrate.value_or(100);
Harald Alvestrand763f5a92020-10-22 10:39:401192 audio_options_.combined_audio_video_bwe =
1193 configuration.combined_audio_video_bwe;
1194
1195 audio_options_.audio_jitter_buffer_max_packets =
1196 configuration.audio_jitter_buffer_max_packets;
1197
1198 audio_options_.audio_jitter_buffer_fast_accelerate =
1199 configuration.audio_jitter_buffer_fast_accelerate;
1200
1201 audio_options_.audio_jitter_buffer_min_delay_ms =
1202 configuration.audio_jitter_buffer_min_delay_ms;
1203
Harald Alvestrand763f5a92020-10-22 10:39:401204 // Obtain a certificate from RTCConfiguration if any were provided (optional).
1205 rtc::scoped_refptr<rtc::RTCCertificate> certificate;
1206 if (!configuration.certificates.empty()) {
1207 // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of
1208 // just picking the first one. The decision should be made based on the DTLS
1209 // handshake. The DTLS negotiations need to know about all certificates.
1210 certificate = configuration.certificates[0];
1211 }
1212
1213 webrtc_session_desc_factory_ =
1214 std::make_unique<WebRtcSessionDescriptionFactory>(
Jonas Orelanded99dae2022-03-09 08:28:101215 context, this, pc_->session_id(), pc_->dtls_enabled(),
Danil Chapovalovb7da8162022-08-22 14:39:341216 std::move(dependencies.cert_generator), std::move(certificate),
Harald Alvestranda0947872020-11-09 14:15:001217 [this](const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
Harald Alvestrandbc32c562022-02-09 12:08:471218 RTC_DCHECK_RUN_ON(signaling_thread());
1219 transport_controller_s()->SetLocalCertificate(certificate);
Jonas Oreland6c7f9842022-04-19 15:24:101220 },
1221 pc_->trials());
Harald Alvestrand763f5a92020-10-22 10:39:401222
Harald Alvestrand0d018412021-11-04 13:52:311223 if (pc_->options()->disable_encryption) {
1224 webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
1225 }
1226
Harald Alvestrand763f5a92020-10-22 10:39:401227 webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions(
1228 pc_->GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions);
1229 webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan());
1230
Harald Alvestrand9cd199d2020-10-27 07:10:431231 if (dependencies.video_bitrate_allocator_factory) {
Harald Alvestrand763f5a92020-10-22 10:39:401232 video_bitrate_allocator_factory_ =
Harald Alvestrand9cd199d2020-10-27 07:10:431233 std::move(dependencies.video_bitrate_allocator_factory);
Harald Alvestrand763f5a92020-10-22 10:39:401234 } else {
1235 video_bitrate_allocator_factory_ =
1236 CreateBuiltinVideoBitrateAllocatorFactory();
1237 }
1238}
1239
Harald Alvestrandbc9ca252020-10-05 13:08:411240// ==================================================================
Artem Titovc6c02ef2022-05-09 08:30:091241// Access to pc_ variables
Harald Alvestrand35f4b4c2022-05-16 10:36:431242cricket::MediaEngineInterface* SdpOfferAnswerHandler::media_engine() const {
1243 RTC_DCHECK(context_);
Harald Alvestrandc3fa7c32022-05-22 10:57:011244 return context_->media_engine();
Harald Alvestrand35f4b4c2022-05-16 10:36:431245}
1246
Harald Alvestrande15fb152020-10-19 13:28:051247TransceiverList* SdpOfferAnswerHandler::transceivers() {
1248 if (!pc_->rtp_manager()) {
1249 return nullptr;
1250 }
1251 return pc_->rtp_manager()->transceivers();
Harald Alvestrandbc9ca252020-10-05 13:08:411252}
Harald Alvestrand35f4b4c2022-05-16 10:36:431253
Harald Alvestrande15fb152020-10-19 13:28:051254const TransceiverList* SdpOfferAnswerHandler::transceivers() const {
1255 if (!pc_->rtp_manager()) {
1256 return nullptr;
1257 }
1258 return pc_->rtp_manager()->transceivers();
Harald Alvestrandbc9ca252020-10-05 13:08:411259}
Harald Alvestrandbc32c562022-02-09 12:08:471260JsepTransportController* SdpOfferAnswerHandler::transport_controller_s() {
1261 return pc_->transport_controller_s();
Harald Alvestrandbc9ca252020-10-05 13:08:411262}
Harald Alvestrandbc32c562022-02-09 12:08:471263JsepTransportController* SdpOfferAnswerHandler::transport_controller_n() {
1264 return pc_->transport_controller_n();
1265}
1266const JsepTransportController* SdpOfferAnswerHandler::transport_controller_s()
Harald Alvestrandf01bd6c2020-10-23 13:30:461267 const {
Harald Alvestrandbc32c562022-02-09 12:08:471268 return pc_->transport_controller_s();
1269}
1270const JsepTransportController* SdpOfferAnswerHandler::transport_controller_n()
1271 const {
1272 return pc_->transport_controller_n();
Harald Alvestrandf01bd6c2020-10-23 13:30:461273}
Harald Alvestrandbc9ca252020-10-05 13:08:411274DataChannelController* SdpOfferAnswerHandler::data_channel_controller() {
Harald Alvestrand653429c2020-10-19 16:05:201275 return pc_->data_channel_controller();
Harald Alvestrandbc9ca252020-10-05 13:08:411276}
1277const DataChannelController* SdpOfferAnswerHandler::data_channel_controller()
1278 const {
Harald Alvestrand653429c2020-10-19 16:05:201279 return pc_->data_channel_controller();
Harald Alvestrandbc9ca252020-10-05 13:08:411280}
1281cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() {
Harald Alvestrand653429c2020-10-19 16:05:201282 return pc_->port_allocator();
Harald Alvestrandbc9ca252020-10-05 13:08:411283}
1284const cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() const {
Harald Alvestrand653429c2020-10-19 16:05:201285 return pc_->port_allocator();
Harald Alvestrandbc9ca252020-10-05 13:08:411286}
Harald Alvestrande15fb152020-10-19 13:28:051287RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() {
1288 return pc_->rtp_manager();
1289}
1290const RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() const {
1291 return pc_->rtp_manager();
1292}
Harald Alvestrandbc9ca252020-10-05 13:08:411293
1294// ===================================================================
1295
Harald Alvestrandcdcfab02020-09-28 13:02:071296void SdpOfferAnswerHandler::PrepareForShutdown() {
1297 RTC_DCHECK_RUN_ON(signaling_thread());
1298 weak_ptr_factory_.InvalidateWeakPtrs();
1299}
1300
1301void SdpOfferAnswerHandler::Close() {
1302 ChangeSignalingState(PeerConnectionInterface::kClosed);
1303}
1304
1305void SdpOfferAnswerHandler::RestartIce() {
1306 RTC_DCHECK_RUN_ON(signaling_thread());
1307 local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions(
1308 current_local_description(), pending_local_description());
1309 UpdateNegotiationNeeded();
1310}
1311
1312rtc::Thread* SdpOfferAnswerHandler::signaling_thread() const {
Harald Alvestrand66c40362022-01-28 17:41:301313 return context_->signaling_thread();
Harald Alvestrandcdcfab02020-09-28 13:02:071314}
1315
Harald Alvestrandbc32c562022-02-09 12:08:471316rtc::Thread* SdpOfferAnswerHandler::network_thread() const {
1317 return context_->network_thread();
1318}
1319
Harald Alvestrandcdcfab02020-09-28 13:02:071320void SdpOfferAnswerHandler::CreateOffer(
1321 CreateSessionDescriptionObserver* observer,
1322 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
1323 RTC_DCHECK_RUN_ON(signaling_thread());
1324 // Chain this operation. If asynchronous operations are pending on the chain,
1325 // this operation will be queued to be invoked, otherwise the contents of the
1326 // lambda will execute immediately.
1327 operations_chain_->ChainOperation(
1328 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1329 observer_refptr =
1330 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
1331 options](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 20:30:231332 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071333 if (!this_weak_ptr) {
1334 observer_refptr->OnFailure(
1335 RTCError(RTCErrorType::INTERNAL_ERROR,
1336 "CreateOffer failed because the session was shut down"));
1337 operations_chain_callback();
1338 return;
1339 }
1340 // The operation completes asynchronously when the wrapper is invoked.
Niels Möllerb7aac6f52021-08-23 13:48:061341 auto observer_wrapper = rtc::make_ref_counted<
1342 CreateSessionDescriptionObserverOperationWrapper>(
1343 std::move(observer_refptr), std::move(operations_chain_callback));
Harald Alvestrandcdcfab02020-09-28 13:02:071344 this_weak_ptr->DoCreateOffer(options, observer_wrapper);
1345 });
1346}
1347
1348void SdpOfferAnswerHandler::SetLocalDescription(
1349 SetSessionDescriptionObserver* observer,
1350 SessionDescriptionInterface* desc_ptr) {
1351 RTC_DCHECK_RUN_ON(signaling_thread());
1352 // Chain this operation. If asynchronous operations are pending on the chain,
1353 // this operation will be queued to be invoked, otherwise the contents of the
1354 // lambda will execute immediately.
1355 operations_chain_->ChainOperation(
1356 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1357 observer_refptr =
1358 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1359 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1360 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 20:30:231361 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071362 if (!this_weak_ptr) {
1363 // For consistency with SetSessionDescriptionObserverAdapter whose
1364 // posted messages doesn't get processed when the PC is destroyed, we
Artem Titov880fa812021-07-30 20:30:231365 // do not inform `observer_refptr` that the operation failed.
Harald Alvestrandcdcfab02020-09-28 13:02:071366 operations_chain_callback();
1367 return;
1368 }
1369 // SetSessionDescriptionObserverAdapter takes care of making sure the
Artem Titov880fa812021-07-30 20:30:231370 // `observer_refptr` is invoked in a posted message.
Harald Alvestrandcdcfab02020-09-28 13:02:071371 this_weak_ptr->DoSetLocalDescription(
1372 std::move(desc),
Niels Möllerb7aac6f52021-08-23 13:48:061373 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1374 this_weak_ptr, observer_refptr));
Harald Alvestrandcdcfab02020-09-28 13:02:071375 // For backwards-compatability reasons, we declare the operation as
1376 // completed here (rather than in a post), so that the operation chain
1377 // is not blocked by this operation when the observer is invoked. This
1378 // allows the observer to trigger subsequent offer/answer operations
1379 // synchronously if the operation chain is now empty.
1380 operations_chain_callback();
1381 });
1382}
1383
1384void SdpOfferAnswerHandler::SetLocalDescription(
1385 std::unique_ptr<SessionDescriptionInterface> desc,
1386 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1387 RTC_DCHECK_RUN_ON(signaling_thread());
1388 // Chain this operation. If asynchronous operations are pending on the chain,
1389 // this operation will be queued to be invoked, otherwise the contents of the
1390 // lambda will execute immediately.
1391 operations_chain_->ChainOperation(
1392 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1393 desc = std::move(desc)](
1394 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 20:30:231395 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071396 if (!this_weak_ptr) {
1397 observer->OnSetLocalDescriptionComplete(RTCError(
1398 RTCErrorType::INTERNAL_ERROR,
1399 "SetLocalDescription failed because the session was shut down"));
1400 operations_chain_callback();
1401 return;
1402 }
1403 this_weak_ptr->DoSetLocalDescription(std::move(desc), observer);
1404 // DoSetLocalDescription() is implemented as a synchronous operation.
Artem Titov880fa812021-07-30 20:30:231405 // The `observer` will already have been informed that it completed, and
Harald Alvestrandcdcfab02020-09-28 13:02:071406 // we can mark this operation as complete without any loose ends.
1407 operations_chain_callback();
1408 });
1409}
1410
1411void SdpOfferAnswerHandler::SetLocalDescription(
1412 SetSessionDescriptionObserver* observer) {
1413 RTC_DCHECK_RUN_ON(signaling_thread());
1414 SetLocalDescription(
Niels Möllerb7aac6f52021-08-23 13:48:061415 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
Niels Möllere7cc8832022-01-04 14:20:031416 weak_ptr_factory_.GetWeakPtr(),
1417 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer)));
Harald Alvestrandcdcfab02020-09-28 13:02:071418}
1419
1420void SdpOfferAnswerHandler::SetLocalDescription(
1421 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1422 RTC_DCHECK_RUN_ON(signaling_thread());
Artem Titov880fa812021-07-30 20:30:231423 // The `create_sdp_observer` handles performing DoSetLocalDescription() with
Harald Alvestrandcdcfab02020-09-28 13:02:071424 // the resulting description as well as completing the operation.
Niels Möllerb7aac6f52021-08-23 13:48:061425 auto create_sdp_observer =
1426 rtc::make_ref_counted<ImplicitCreateSessionDescriptionObserver>(
1427 weak_ptr_factory_.GetWeakPtr(), observer);
Harald Alvestrandcdcfab02020-09-28 13:02:071428 // Chain this operation. If asynchronous operations are pending on the chain,
1429 // this operation will be queued to be invoked, otherwise the contents of the
1430 // lambda will execute immediately.
1431 operations_chain_->ChainOperation(
1432 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1433 create_sdp_observer](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 20:30:231434 // The `create_sdp_observer` is responsible for completing the
Harald Alvestrandcdcfab02020-09-28 13:02:071435 // operation.
1436 create_sdp_observer->SetOperationCompleteCallback(
1437 std::move(operations_chain_callback));
Artem Titov880fa812021-07-30 20:30:231438 // Abort early if `this_weak_ptr` is no longer valid. This triggers the
Harald Alvestrandcdcfab02020-09-28 13:02:071439 // same code path as if DoCreateOffer() or DoCreateAnswer() failed.
1440 if (!this_weak_ptr) {
1441 create_sdp_observer->OnFailure(RTCError(
1442 RTCErrorType::INTERNAL_ERROR,
1443 "SetLocalDescription failed because the session was shut down"));
1444 return;
1445 }
1446 switch (this_weak_ptr->signaling_state()) {
1447 case PeerConnectionInterface::kStable:
1448 case PeerConnectionInterface::kHaveLocalOffer:
1449 case PeerConnectionInterface::kHaveRemotePrAnswer:
1450 // TODO(hbos): If [LastCreatedOffer] exists and still represents the
1451 // current state of the system, use that instead of creating another
1452 // offer.
1453 this_weak_ptr->DoCreateOffer(
1454 PeerConnectionInterface::RTCOfferAnswerOptions(),
1455 create_sdp_observer);
1456 break;
1457 case PeerConnectionInterface::kHaveLocalPrAnswer:
1458 case PeerConnectionInterface::kHaveRemoteOffer:
1459 // TODO(hbos): If [LastCreatedAnswer] exists and still represents
1460 // the current state of the system, use that instead of creating
1461 // another answer.
1462 this_weak_ptr->DoCreateAnswer(
1463 PeerConnectionInterface::RTCOfferAnswerOptions(),
1464 create_sdp_observer);
1465 break;
1466 case PeerConnectionInterface::kClosed:
1467 create_sdp_observer->OnFailure(RTCError(
1468 RTCErrorType::INVALID_STATE,
1469 "SetLocalDescription called when PeerConnection is closed."));
1470 break;
1471 }
1472 });
1473}
1474
1475RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
Henrik Boströmf8187e02021-04-26 19:04:261476 std::unique_ptr<SessionDescriptionInterface> desc,
1477 const std::map<std::string, const cricket::ContentGroup*>&
1478 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:461479 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyLocalDescription");
Harald Alvestrandcdcfab02020-09-28 13:02:071480 RTC_DCHECK_RUN_ON(signaling_thread());
1481 RTC_DCHECK(desc);
1482
Philipp Hancke7baa63f2022-09-01 13:39:501483 // Invalidate the stats caches to make sure that they get
1484 // updated the next time getStats() gets called, as updating the session
1485 // description affects the stats.
1486 pc_->ClearStatsCache();
Harald Alvestrandcdcfab02020-09-28 13:02:071487
1488 // Take a reference to the old local description since it's used below to
1489 // compare against the new local description. When setting the new local
1490 // description, grab ownership of the replaced session description in case it
Artem Titov880fa812021-07-30 20:30:231491 // is the same as `old_local_description`, to keep it alive for the duration
Harald Alvestrandcdcfab02020-09-28 13:02:071492 // of the method.
1493 const SessionDescriptionInterface* old_local_description =
1494 local_description();
1495 std::unique_ptr<SessionDescriptionInterface> replaced_local_description;
1496 SdpType type = desc->GetType();
1497 if (type == SdpType::kAnswer) {
1498 replaced_local_description = pending_local_description_
1499 ? std::move(pending_local_description_)
1500 : std::move(current_local_description_);
1501 current_local_description_ = std::move(desc);
1502 pending_local_description_ = nullptr;
1503 current_remote_description_ = std::move(pending_remote_description_);
1504 } else {
1505 replaced_local_description = std::move(pending_local_description_);
1506 pending_local_description_ = std::move(desc);
1507 }
Harald Alvestrandbc32c562022-02-09 12:08:471508 if (!initial_offerer_) {
1509 initial_offerer_.emplace(type == SdpType::kOffer);
1510 }
Harald Alvestrandcdcfab02020-09-28 13:02:071511 // The session description to apply now must be accessed by
Artem Titov880fa812021-07-30 20:30:231512 // `local_description()`.
Harald Alvestrandcdcfab02020-09-28 13:02:071513 RTC_DCHECK(local_description());
1514
1515 // Report statistics about any use of simulcast.
1516 ReportSimulcastApiVersion(kSimulcastVersionApplyLocalDescription,
1517 *local_description()->description());
1518
1519 if (!is_caller_) {
1520 if (remote_description()) {
1521 // Remote description was applied first, so this PC is the callee.
1522 is_caller_ = false;
1523 } else {
1524 // Local description is applied first, so this PC is the caller.
1525 is_caller_ = true;
1526 }
1527 }
1528
Harald Alvestranda474fbf2020-10-01 16:47:231529 RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type);
Harald Alvestrandcdcfab02020-09-28 13:02:071530 if (!error.ok()) {
1531 return error;
1532 }
1533
1534 if (IsUnifiedPlan()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561535 error = UpdateTransceiversAndDataChannels(
Harald Alvestrandcdcfab02020-09-28 13:02:071536 cricket::CS_LOCAL, *local_description(), old_local_description,
Henrik Boströmf8187e02021-04-26 19:04:261537 remote_description(), bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:071538 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561539 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1540 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:071541 return error;
1542 }
Harald Alvestrand8101e7b2022-05-23 14:57:471543 if (ConfiguredForMedia()) {
1544 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
1545 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
1546 for (const auto& transceiver_ext : transceivers()->List()) {
1547 auto transceiver = transceiver_ext->internal();
1548 if (transceiver->stopped()) {
1549 continue;
Harald Alvestrandc48ad732022-05-06 15:15:341550 }
Harald Alvestrand8101e7b2022-05-23 14:57:471551
1552 // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots.
1553 // Note that code paths that don't set MID won't be able to use
1554 // information about DTLS transports.
1555 if (transceiver->mid()) {
1556 auto dtls_transport = LookupDtlsTransportByMid(
1557 context_->network_thread(), transport_controller_s(),
1558 *transceiver->mid());
1559 transceiver->sender_internal()->set_transport(dtls_transport);
1560 transceiver->receiver_internal()->set_transport(dtls_transport);
1561 }
1562
1563 const ContentInfo* content =
1564 FindMediaSectionForTransceiver(transceiver, local_description());
1565 if (!content) {
1566 continue;
1567 }
1568 const MediaContentDescription* media_desc =
1569 content->media_description();
1570 // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run
1571 // the following steps:
1572 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
1573 // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and
1574 // transceiver's [[FiredDirection]] slot is either "sendrecv" or
1575 // "recvonly", process the removal of a remote track for the media
1576 // description, given transceiver, removeList, and muteTracks.
1577 if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
1578 (transceiver->fired_direction() &&
1579 RtpTransceiverDirectionHasRecv(
1580 *transceiver->fired_direction()))) {
1581 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
1582 &removed_streams);
1583 }
1584 // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and
1585 // [[FiredDirection]] slots to direction.
1586 transceiver->set_current_direction(media_desc->direction());
1587 transceiver->set_fired_direction(media_desc->direction());
1588 }
Harald Alvestrandcdcfab02020-09-28 13:02:071589 }
Harald Alvestrand8101e7b2022-05-23 14:57:471590 auto observer = pc_->Observer();
1591 for (const auto& transceiver : remove_list) {
1592 observer->OnRemoveTrack(transceiver->receiver());
1593 }
1594 for (const auto& stream : removed_streams) {
1595 observer->OnRemoveStream(stream);
1596 }
Harald Alvestrandcdcfab02020-09-28 13:02:071597 }
1598 } else {
1599 // Media channels will be created only when offer is set. These may use new
1600 // transports just created by PushdownTransportDescription.
1601 if (type == SdpType::kOffer) {
1602 // TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local
1603 // description is applied. Restore back to old description.
Harald Alvestrandbc9ca252020-10-05 13:08:411604 RTCError error = CreateChannels(*local_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:071605 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561606 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1607 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:071608 return error;
1609 }
1610 }
1611 // Remove unused channels if MediaContentDescription is rejected.
Harald Alvestranda474fbf2020-10-01 16:47:231612 RemoveUnusedChannels(local_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:071613 }
1614
1615 error = UpdateSessionState(type, cricket::CS_LOCAL,
Henrik Boströmf8187e02021-04-26 19:04:261616 local_description()->description(),
1617 bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:071618 if (!error.ok()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:561619 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1620 << ")";
Harald Alvestrandcdcfab02020-09-28 13:02:071621 return error;
1622 }
1623
Tomas Gunnarsson0dd75392022-01-17 18:19:561624 // Now that we have a local description, we can push down remote candidates.
1625 UseCandidatesInRemoteDescription();
Harald Alvestrandcdcfab02020-09-28 13:02:071626
1627 pending_ice_restarts_.clear();
Harald Alvestranda474fbf2020-10-01 16:47:231628 if (session_error() != SessionError::kNone) {
1629 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg());
Harald Alvestrandcdcfab02020-09-28 13:02:071630 }
1631
1632 // If setting the description decided our SSL role, allocate any necessary
1633 // SCTP sids.
1634 rtc::SSLRole role;
Florent Castelli516e2842021-04-19 13:29:501635 if (pc_->GetSctpSslRole(&role)) {
Harald Alvestrandbc9ca252020-10-05 13:08:411636 data_channel_controller()->AllocateSctpSids(role);
Harald Alvestrandcdcfab02020-09-28 13:02:071637 }
1638
1639 if (IsUnifiedPlan()) {
Harald Alvestrand8101e7b2022-05-23 14:57:471640 if (ConfiguredForMedia()) {
1641 // We must use List and not ListInternal here because
1642 // transceivers()->StableState() is indexed by the non-internal refptr.
1643 for (const auto& transceiver_ext : transceivers()->List()) {
1644 auto transceiver = transceiver_ext->internal();
1645 if (transceiver->stopped()) {
1646 continue;
1647 }
1648 const ContentInfo* content =
1649 FindMediaSectionForTransceiver(transceiver, local_description());
1650 if (!content) {
1651 continue;
1652 }
1653 cricket::ChannelInterface* channel = transceiver->channel();
1654 if (content->rejected || !channel || channel->local_streams().empty()) {
1655 // 0 is a special value meaning "this sender has no associated send
1656 // stream". Need to call this so the sender won't attempt to configure
1657 // a no longer existing stream and run into DCHECKs in the lower
1658 // layers.
1659 transceiver->sender_internal()->SetSsrc(0);
1660 } else {
1661 // Get the StreamParams from the channel which could generate SSRCs.
1662 const std::vector<StreamParams>& streams = channel->local_streams();
1663 transceiver->sender_internal()->set_stream_ids(
1664 streams[0].stream_ids());
1665 auto encodings =
1666 transceiver->sender_internal()->init_send_encodings();
1667 transceiver->sender_internal()->SetSsrc(streams[0].first_ssrc());
1668 if (!encodings.empty()) {
1669 transceivers()
1670 ->StableState(transceiver_ext)
1671 ->SetInitSendEncodings(encodings);
1672 }
Eldar Rello950d6b92021-04-06 19:38:001673 }
Harald Alvestrandcdcfab02020-09-28 13:02:071674 }
1675 }
1676 } else {
1677 // Plan B semantics.
1678
1679 // Update state and SSRC of local MediaStreams and DataChannels based on the
1680 // local session description.
1681 const cricket::ContentInfo* audio_content =
1682 GetFirstAudioContent(local_description()->description());
1683 if (audio_content) {
1684 if (audio_content->rejected) {
Harald Alvestranda474fbf2020-10-01 16:47:231685 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
Harald Alvestrandcdcfab02020-09-28 13:02:071686 } else {
1687 const cricket::AudioContentDescription* audio_desc =
1688 audio_content->media_description()->as_audio();
Harald Alvestranda474fbf2020-10-01 16:47:231689 UpdateLocalSenders(audio_desc->streams(), audio_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:071690 }
1691 }
1692
1693 const cricket::ContentInfo* video_content =
1694 GetFirstVideoContent(local_description()->description());
1695 if (video_content) {
1696 if (video_content->rejected) {
Harald Alvestranda474fbf2020-10-01 16:47:231697 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
Harald Alvestrandcdcfab02020-09-28 13:02:071698 } else {
1699 const cricket::VideoContentDescription* video_desc =
1700 video_content->media_description()->as_video();
Harald Alvestranda474fbf2020-10-01 16:47:231701 UpdateLocalSenders(video_desc->streams(), video_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:071702 }
1703 }
1704 }
1705
Harald Alvestrand7af57c62021-04-16 11:12:141706 // This function does nothing with data content.
Harald Alvestrandcdcfab02020-09-28 13:02:071707
1708 if (type == SdpType::kAnswer &&
1709 local_ice_credentials_to_replace_->SatisfiesIceRestart(
1710 *current_local_description_)) {
1711 local_ice_credentials_to_replace_->ClearIceCredentials();
1712 }
1713
1714 return RTCError::OK();
1715}
1716
1717void SdpOfferAnswerHandler::SetRemoteDescription(
1718 SetSessionDescriptionObserver* observer,
1719 SessionDescriptionInterface* desc_ptr) {
1720 RTC_DCHECK_RUN_ON(signaling_thread());
1721 // Chain this operation. If asynchronous operations are pending on the chain,
1722 // this operation will be queued to be invoked, otherwise the contents of the
1723 // lambda will execute immediately.
1724 operations_chain_->ChainOperation(
1725 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1726 observer_refptr =
1727 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1728 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1729 std::function<void()> operations_chain_callback) mutable {
Artem Titov880fa812021-07-30 20:30:231730 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071731 if (!this_weak_ptr) {
1732 // For consistency with SetSessionDescriptionObserverAdapter whose
1733 // posted messages doesn't get processed when the PC is destroyed, we
Artem Titov880fa812021-07-30 20:30:231734 // do not inform `observer_refptr` that the operation failed.
Harald Alvestrandcdcfab02020-09-28 13:02:071735 operations_chain_callback();
1736 return;
1737 }
1738 // SetSessionDescriptionObserverAdapter takes care of making sure the
Artem Titov880fa812021-07-30 20:30:231739 // `observer_refptr` is invoked in a posted message.
Harald Alvestrandcdcfab02020-09-28 13:02:071740 this_weak_ptr->DoSetRemoteDescription(
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041741 std::make_unique<RemoteDescriptionOperation>(
1742 this_weak_ptr.get(), std::move(desc),
1743 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1744 this_weak_ptr, observer_refptr),
1745 std::move(operations_chain_callback)));
Harald Alvestrandcdcfab02020-09-28 13:02:071746 });
1747}
1748
1749void SdpOfferAnswerHandler::SetRemoteDescription(
1750 std::unique_ptr<SessionDescriptionInterface> desc,
1751 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer) {
1752 RTC_DCHECK_RUN_ON(signaling_thread());
1753 // Chain this operation. If asynchronous operations are pending on the chain,
1754 // this operation will be queued to be invoked, otherwise the contents of the
1755 // lambda will execute immediately.
1756 operations_chain_->ChainOperation(
1757 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1758 desc = std::move(desc)](
1759 std::function<void()> operations_chain_callback) mutable {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041760 if (!observer) {
1761 RTC_DLOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
1762 operations_chain_callback();
1763 return;
1764 }
1765
Artem Titov880fa812021-07-30 20:30:231766 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:071767 if (!this_weak_ptr) {
1768 observer->OnSetRemoteDescriptionComplete(RTCError(
1769 RTCErrorType::INTERNAL_ERROR,
1770 "SetRemoteDescription failed because the session was shut down"));
1771 operations_chain_callback();
1772 return;
1773 }
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041774
1775 this_weak_ptr->DoSetRemoteDescription(
1776 std::make_unique<RemoteDescriptionOperation>(
1777 this_weak_ptr.get(), std::move(desc), std::move(observer),
1778 std::move(operations_chain_callback)));
Harald Alvestrandcdcfab02020-09-28 13:02:071779 });
1780}
1781
Tomas Gunnarsson0dd75392022-01-17 18:19:561782RTCError SdpOfferAnswerHandler::ReplaceRemoteDescription(
Henrik Boströmf8187e02021-04-26 19:04:261783 std::unique_ptr<SessionDescriptionInterface> desc,
Tomas Gunnarsson0dd75392022-01-17 18:19:561784 SdpType sdp_type,
1785 std::unique_ptr<SessionDescriptionInterface>* replaced_description) {
1786 RTC_DCHECK(replaced_description);
1787 if (sdp_type == SdpType::kAnswer) {
1788 *replaced_description = pending_remote_description_
1789 ? std::move(pending_remote_description_)
1790 : std::move(current_remote_description_);
1791 current_remote_description_ = std::move(desc);
1792 pending_remote_description_ = nullptr;
1793 current_local_description_ = std::move(pending_local_description_);
1794 } else {
1795 *replaced_description = std::move(pending_remote_description_);
1796 pending_remote_description_ = std::move(desc);
1797 }
1798
1799 // The session description to apply now must be accessed by
1800 // `remote_description()`.
1801 const cricket::SessionDescription* session_desc =
1802 remote_description()->description();
1803
1804 // Report statistics about any use of simulcast.
1805 ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription,
1806 *session_desc);
1807
Danil Chapovalov9e09a1f2022-09-08 16:38:101808 // NOTE: This will perform a BlockingCall() to the network thread.
Harald Alvestrandbc32c562022-02-09 12:08:471809 return transport_controller_s()->SetRemoteDescription(sdp_type, session_desc);
Tomas Gunnarsson0dd75392022-01-17 18:19:561810}
1811
1812void SdpOfferAnswerHandler::ApplyRemoteDescription(
1813 std::unique_ptr<RemoteDescriptionOperation> operation) {
Markus Handell518669d2021-06-07 11:30:461814 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyRemoteDescription");
Harald Alvestrandcdcfab02020-09-28 13:02:071815 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson0dd75392022-01-17 18:19:561816 RTC_DCHECK(operation->description());
Harald Alvestrandcdcfab02020-09-28 13:02:071817
Philipp Hancke7baa63f2022-09-01 13:39:501818 // Invalidate the stats caches to make sure that they get
1819 // updated next time getStats() gets called, as updating the session
1820 // description affects the stats.
1821 pc_->ClearStatsCache();
Harald Alvestrandcdcfab02020-09-28 13:02:071822
Tomas Gunnarsson0dd75392022-01-17 18:19:561823 if (!operation->ReplaceRemoteDescriptionAndCheckEror())
1824 return;
Harald Alvestrandcdcfab02020-09-28 13:02:071825
Tomas Gunnarsson0dd75392022-01-17 18:19:561826 if (!operation->UpdateChannels())
1827 return;
Harald Alvestrandcdcfab02020-09-28 13:02:071828
1829 // NOTE: Candidates allocation will be initiated only when
1830 // SetLocalDescription is called.
Tomas Gunnarsson0dd75392022-01-17 18:19:561831 if (!operation->UpdateSessionState())
1832 return;
Harald Alvestrandcdcfab02020-09-28 13:02:071833
Tomas Gunnarsson0dd75392022-01-17 18:19:561834 if (!operation->UseCandidatesInRemoteDescription())
1835 return;
Harald Alvestrandcdcfab02020-09-28 13:02:071836
Tomas Gunnarsson0dd75392022-01-17 18:19:561837 if (operation->old_remote_description()) {
Harald Alvestrandcdcfab02020-09-28 13:02:071838 for (const cricket::ContentInfo& content :
Tomas Gunnarsson0dd75392022-01-17 18:19:561839 operation->old_remote_description()->description()->contents()) {
Harald Alvestrandcdcfab02020-09-28 13:02:071840 // Check if this new SessionDescription contains new ICE ufrag and
1841 // password that indicates the remote peer requests an ICE restart.
1842 // TODO(deadbeef): When we start storing both the current and pending
1843 // remote description, this should reset pending_ice_restarts and compare
1844 // against the current description.
Tomas Gunnarsson0dd75392022-01-17 18:19:561845 if (CheckForRemoteIceRestart(operation->old_remote_description(),
1846 remote_description(), content.name)) {
1847 if (operation->type() == SdpType::kOffer) {
Harald Alvestrandcdcfab02020-09-28 13:02:071848 pending_ice_restarts_.insert(content.name);
1849 }
1850 } else {
1851 // We retain all received candidates only if ICE is not restarted.
1852 // When ICE is restarted, all previous candidates belong to an old
1853 // generation and should not be kept.
1854 // TODO(deadbeef): This goes against the W3C spec which says the remote
1855 // description should only contain candidates from the last set remote
1856 // description plus any candidates added since then. We should remove
1857 // this once we're sure it won't break anything.
1858 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
Tomas Gunnarsson0dd75392022-01-17 18:19:561859 operation->old_remote_description(), content.name,
1860 mutable_remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:071861 }
1862 }
1863 }
1864
Tomas Gunnarsson0dd75392022-01-17 18:19:561865 if (operation->HaveSessionError())
1866 return;
Harald Alvestrandcdcfab02020-09-28 13:02:071867
1868 // Set the the ICE connection state to connecting since the connection may
1869 // become writable with peer reflexive candidates before any remote candidate
1870 // is signaled.
1871 // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix
1872 // is to have a new signal the indicates a change in checking state from the
1873 // transport and expose a new checking() member from transport that can be
1874 // read to determine the current checking state. The existing SignalConnecting
1875 // actually means "gathering candidates", so cannot be be used here.
1876 if (remote_description()->GetType() != SdpType::kOffer &&
1877 remote_description()->number_of_mediasections() > 0u &&
Harald Alvestrand5b661302022-01-28 13:08:341878 pc_->ice_connection_state_internal() ==
Harald Alvestrandcdcfab02020-09-28 13:02:071879 PeerConnectionInterface::kIceConnectionNew) {
1880 pc_->SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1881 }
1882
1883 // If setting the description decided our SSL role, allocate any necessary
1884 // SCTP sids.
1885 rtc::SSLRole role;
Florent Castelli516e2842021-04-19 13:29:501886 if (pc_->GetSctpSslRole(&role)) {
Harald Alvestrandbc9ca252020-10-05 13:08:411887 data_channel_controller()->AllocateSctpSids(role);
Harald Alvestrandcdcfab02020-09-28 13:02:071888 }
1889
Tomas Gunnarsson0dd75392022-01-17 18:19:561890 if (operation->unified_plan()) {
1891 ApplyRemoteDescriptionUpdateTransceiverState(operation->type());
Harald Alvestrandcdcfab02020-09-28 13:02:071892 }
1893
Harald Alvestrandcdcfab02020-09-28 13:02:071894 const cricket::AudioContentDescription* audio_desc =
1895 GetFirstAudioContentDescription(remote_description()->description());
1896 const cricket::VideoContentDescription* video_desc =
1897 GetFirstVideoContentDescription(remote_description()->description());
Harald Alvestrandcdcfab02020-09-28 13:02:071898
1899 // Check if the descriptions include streams, just in case the peer supports
1900 // MSID, but doesn't indicate so with "a=msid-semantic".
1901 if (remote_description()->description()->msid_supported() ||
1902 (audio_desc && !audio_desc->streams().empty()) ||
1903 (video_desc && !video_desc->streams().empty())) {
1904 remote_peer_supports_msid_ = true;
1905 }
1906
Tomas Gunnarsson0dd75392022-01-17 18:19:561907 if (!operation->unified_plan()) {
Tomas Gunnarssonb625edf2022-01-07 16:22:141908 PlanBUpdateSendersAndReceivers(
1909 GetFirstAudioContent(remote_description()->description()), audio_desc,
1910 GetFirstVideoContent(remote_description()->description()), video_desc);
Harald Alvestrandcdcfab02020-09-28 13:02:071911 }
1912
Tomas Gunnarsson0dd75392022-01-17 18:19:561913 if (operation->type() == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:041914 if (local_ice_credentials_to_replace_->SatisfiesIceRestart(
1915 *current_local_description_)) {
1916 local_ice_credentials_to_replace_->ClearIceCredentials();
1917 }
1918
1919 RemoveStoppedTransceivers();
Harald Alvestrandcdcfab02020-09-28 13:02:071920 }
1921
Tomas Gunnarsson0dd75392022-01-17 18:19:561922 // Consider the operation complete at this point.
1923 operation->SignalCompletion();
1924
1925 SetRemoteDescriptionPostProcess(operation->type() == SdpType::kAnswer);
Harald Alvestrandcdcfab02020-09-28 13:02:071926}
1927
Tomas Gunnarsson651586c2022-01-07 18:33:121928void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState(
1929 SdpType sdp_type) {
1930 RTC_DCHECK_RUN_ON(signaling_thread());
1931 RTC_DCHECK(IsUnifiedPlan());
Harald Alvestrand8101e7b2022-05-23 14:57:471932 if (!ConfiguredForMedia()) {
1933 return;
1934 }
Tomas Gunnarsson651586c2022-01-07 18:33:121935 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
1936 now_receiving_transceivers;
1937 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
1938 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
1939 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
1940 for (const auto& transceiver_ext : transceivers()->List()) {
1941 const auto transceiver = transceiver_ext->internal();
1942 const ContentInfo* content =
1943 FindMediaSectionForTransceiver(transceiver, remote_description());
1944 if (!content) {
1945 continue;
1946 }
1947 const MediaContentDescription* media_desc = content->media_description();
1948 RtpTransceiverDirection local_direction =
1949 RtpTransceiverDirectionReversed(media_desc->direction());
Henrik Boström0a162762022-05-02 13:47:521950 // Remember the previous remote streams if this is a remote offer. This
1951 // makes it possible to rollback modifications to the streams.
1952 if (sdp_type == SdpType::kOffer) {
1953 transceivers()
1954 ->StableState(transceiver_ext)
1955 ->SetRemoteStreamIds(transceiver->receiver()->stream_ids());
1956 }
Tomas Gunnarsson651586c2022-01-07 18:33:121957 // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the
1958 // RTCSessionDescription: Set the associated remote streams given
1959 // transceiver.[[Receiver]], msids, addList, and removeList".
1960 // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription
1961 if (RtpTransceiverDirectionHasRecv(local_direction)) {
1962 std::vector<std::string> stream_ids;
1963 if (!media_desc->streams().empty()) {
1964 // The remote description has signaled the stream IDs.
1965 stream_ids = media_desc->streams()[0].stream_ids();
1966 }
Tomas Gunnarsson651586c2022-01-07 18:33:121967
1968 RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name
1969 << " (" << GetStreamIdsString(stream_ids) << ").";
1970 SetAssociatedRemoteStreams(transceiver->receiver_internal(), stream_ids,
1971 &added_streams, &removed_streams);
1972 // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6
1973 // "Set the RTCSessionDescription: If direction is sendrecv or recvonly,
1974 // and transceiver's current direction is neither sendrecv nor recvonly,
1975 // process the addition of a remote track for the media description.
1976 if (!transceiver->fired_direction() ||
1977 !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) {
1978 RTC_LOG(LS_INFO) << "Processing the addition of a remote track for MID="
1979 << content->name << ".";
1980 // Since the transceiver is passed to the user in an
1981 // OnTrack event, we must use the proxied transceiver.
1982 now_receiving_transceivers.push_back(transceiver_ext);
1983 }
1984 }
1985 // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's
1986 // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the
1987 // removal of a remote track for the media description, given transceiver,
1988 // removeList, and muteTracks.
1989 if (!RtpTransceiverDirectionHasRecv(local_direction) &&
1990 (transceiver->fired_direction() &&
1991 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) {
1992 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
1993 &removed_streams);
1994 }
1995 // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction.
Henrik Boström0a162762022-05-02 13:47:521996 if (sdp_type == SdpType::kOffer) {
1997 // Remember the previous fired direction if this is a remote offer. This
1998 // makes it possible to rollback modifications to [[FiredDirection]],
1999 // which is necessary for "ontrack" to fire in or after rollback.
2000 transceivers()
2001 ->StableState(transceiver_ext)
2002 ->SetFiredDirection(transceiver->fired_direction());
2003 }
Tomas Gunnarsson651586c2022-01-07 18:33:122004 transceiver->set_fired_direction(local_direction);
2005 // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run
2006 // the following steps:
2007 if (sdp_type == SdpType::kPrAnswer || sdp_type == SdpType::kAnswer) {
2008 // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to
2009 // direction.
2010 transceiver->set_current_direction(local_direction);
2011 // 2.2.8.1.11.[3-6]: Set the transport internal slots.
2012 if (transceiver->mid()) {
2013 auto dtls_transport = LookupDtlsTransportByMid(
Harald Alvestrandbc32c562022-02-09 12:08:472014 context_->network_thread(), transport_controller_s(),
Harald Alvestrand66c40362022-01-28 17:41:302015 *transceiver->mid());
Tomas Gunnarsson651586c2022-01-07 18:33:122016 transceiver->sender_internal()->set_transport(dtls_transport);
2017 transceiver->receiver_internal()->set_transport(dtls_transport);
2018 }
2019 }
2020 // 2.2.8.1.12: If the media description is rejected, and transceiver is
2021 // not already stopped, stop the RTCRtpTransceiver transceiver.
2022 if (content->rejected && !transceiver->stopped()) {
2023 RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name
2024 << " since the media section was rejected.";
2025 transceiver->StopTransceiverProcedure();
2026 }
2027 if (!content->rejected && RtpTransceiverDirectionHasRecv(local_direction)) {
2028 if (!media_desc->streams().empty() &&
2029 media_desc->streams()[0].has_ssrcs()) {
2030 uint32_t ssrc = media_desc->streams()[0].first_ssrc();
2031 transceiver->receiver_internal()->SetupMediaChannel(ssrc);
2032 } else {
2033 transceiver->receiver_internal()->SetupUnsignaledMediaChannel();
2034 }
2035 }
2036 }
2037 // Once all processing has finished, fire off callbacks.
2038 auto observer = pc_->Observer();
2039 for (const auto& transceiver : now_receiving_transceivers) {
Henrik Boströmf7859892022-07-04 12:36:372040 pc_->legacy_stats()->AddTrack(transceiver->receiver()->track().get());
Tomas Gunnarsson651586c2022-01-07 18:33:122041 observer->OnTrack(transceiver);
2042 observer->OnAddTrack(transceiver->receiver(),
2043 transceiver->receiver()->streams());
2044 }
2045 for (const auto& stream : added_streams) {
2046 observer->OnAddStream(stream);
2047 }
2048 for (const auto& transceiver : remove_list) {
2049 observer->OnRemoveTrack(transceiver->receiver());
2050 }
2051 for (const auto& stream : removed_streams) {
2052 observer->OnRemoveStream(stream);
2053 }
2054}
2055
Tomas Gunnarssonb625edf2022-01-07 16:22:142056void SdpOfferAnswerHandler::PlanBUpdateSendersAndReceivers(
2057 const cricket::ContentInfo* audio_content,
2058 const cricket::AudioContentDescription* audio_desc,
2059 const cricket::ContentInfo* video_content,
2060 const cricket::VideoContentDescription* video_desc) {
2061 RTC_DCHECK_RUN_ON(signaling_thread());
2062 RTC_DCHECK(!IsUnifiedPlan());
2063
2064 // We wait to signal new streams until we finish processing the description,
2065 // since only at that point will new streams have all their tracks.
2066 rtc::scoped_refptr<StreamCollection> new_streams(StreamCollection::Create());
2067
2068 // TODO(steveanton): When removing RTP senders/receivers in response to a
2069 // rejected media section, there is some cleanup logic that expects the
2070 // voice/ video channel to still be set. But in this method the voice/video
2071 // channel would have been destroyed by the SetRemoteDescription caller
2072 // above so the cleanup that relies on them fails to run. The RemoveSenders
2073 // calls should be moved to right before the DestroyChannel calls to fix
2074 // this.
2075
2076 // Find all audio rtp streams and create corresponding remote AudioTracks
2077 // and MediaStreams.
2078 if (audio_content) {
2079 if (audio_content->rejected) {
2080 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
2081 } else {
2082 bool default_audio_track_needed =
2083 !remote_peer_supports_msid_ &&
2084 RtpTransceiverDirectionHasSend(audio_desc->direction());
2085 UpdateRemoteSendersList(GetActiveStreams(audio_desc),
2086 default_audio_track_needed, audio_desc->type(),
Niels Möllerafb246b2022-04-20 12:26:502087 new_streams.get());
Tomas Gunnarssonb625edf2022-01-07 16:22:142088 }
2089 }
2090
2091 // Find all video rtp streams and create corresponding remote VideoTracks
2092 // and MediaStreams.
2093 if (video_content) {
2094 if (video_content->rejected) {
2095 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
2096 } else {
2097 bool default_video_track_needed =
2098 !remote_peer_supports_msid_ &&
2099 RtpTransceiverDirectionHasSend(video_desc->direction());
2100 UpdateRemoteSendersList(GetActiveStreams(video_desc),
2101 default_video_track_needed, video_desc->type(),
Niels Möllerafb246b2022-04-20 12:26:502102 new_streams.get());
Tomas Gunnarssonb625edf2022-01-07 16:22:142103 }
2104 }
2105
2106 // Iterate new_streams and notify the observer about new MediaStreams.
2107 auto observer = pc_->Observer();
2108 for (size_t i = 0; i < new_streams->count(); ++i) {
2109 MediaStreamInterface* new_stream = new_streams->at(i);
Henrik Boströmf7859892022-07-04 12:36:372110 pc_->legacy_stats()->AddStream(new_stream);
Tomas Gunnarssonb625edf2022-01-07 16:22:142111 observer->OnAddStream(rtc::scoped_refptr<MediaStreamInterface>(new_stream));
2112 }
2113
2114 UpdateEndedRemoteMediaStreams();
2115}
2116
Harald Alvestrandcdcfab02020-09-28 13:02:072117void SdpOfferAnswerHandler::DoSetLocalDescription(
2118 std::unique_ptr<SessionDescriptionInterface> desc,
2119 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
2120 RTC_DCHECK_RUN_ON(signaling_thread());
2121 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetLocalDescription");
2122
2123 if (!observer) {
2124 RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL.";
2125 return;
2126 }
2127
2128 if (!desc) {
2129 observer->OnSetLocalDescriptionComplete(
2130 RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL."));
2131 return;
2132 }
2133
2134 // If a session error has occurred the PeerConnection is in a possibly
2135 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:232136 if (session_error() != SessionError::kNone) {
2137 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:072138 RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message;
2139 observer->OnSetLocalDescriptionComplete(
2140 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2141 return;
2142 }
2143
2144 // For SLD we support only explicit rollback.
2145 if (desc->GetType() == SdpType::kRollback) {
2146 if (IsUnifiedPlan()) {
2147 observer->OnSetLocalDescriptionComplete(Rollback(desc->GetType()));
2148 } else {
2149 observer->OnSetLocalDescriptionComplete(
2150 RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2151 "Rollback not supported in Plan B"));
2152 }
2153 return;
2154 }
2155
Henrik Boströmf8187e02021-04-26 19:04:262156 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid =
2157 GetBundleGroupsByMid(desc->description());
2158 RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL,
2159 bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:072160 if (!error.ok()) {
2161 std::string error_message = GetSetDescriptionErrorMessage(
2162 cricket::CS_LOCAL, desc->GetType(), error);
2163 RTC_LOG(LS_ERROR) << error_message;
2164 observer->OnSetLocalDescriptionComplete(
2165 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2166 return;
2167 }
2168
2169 // Grab the description type before moving ownership to ApplyLocalDescription,
2170 // which may destroy it before returning.
2171 const SdpType type = desc->GetType();
2172
Henrik Boströmf8187e02021-04-26 19:04:262173 error = ApplyLocalDescription(std::move(desc), bundle_groups_by_mid);
Artem Titov880fa812021-07-30 20:30:232174 // `desc` may be destroyed at this point.
Harald Alvestrandcdcfab02020-09-28 13:02:072175
2176 if (!error.ok()) {
2177 // If ApplyLocalDescription fails, the PeerConnection could be in an
2178 // inconsistent state, so act conservatively here and set the session error
2179 // so that future calls to SetLocalDescription/SetRemoteDescription fail.
Harald Alvestranda474fbf2020-10-01 16:47:232180 SetSessionError(SessionError::kContent, error.message());
Harald Alvestrandcdcfab02020-09-28 13:02:072181 std::string error_message =
2182 GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error);
2183 RTC_LOG(LS_ERROR) << error_message;
2184 observer->OnSetLocalDescriptionComplete(
2185 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2186 return;
2187 }
2188 RTC_DCHECK(local_description());
2189
2190 if (local_description()->GetType() == SdpType::kAnswer) {
Harald Alvestranda474fbf2020-10-01 16:47:232191 RemoveStoppedTransceivers();
Harald Alvestrandcdcfab02020-09-28 13:02:072192
2193 // TODO(deadbeef): We already had to hop to the network thread for
2194 // MaybeStartGathering...
Danil Chapovalov9e09a1f2022-09-08 16:38:102195 context_->network_thread()->BlockingCall(
2196 [this] { port_allocator()->DiscardCandidatePool(); });
Harald Alvestrandcdcfab02020-09-28 13:02:072197 }
2198
2199 observer->OnSetLocalDescriptionComplete(RTCError::OK());
Harald Alvestrand44d0dff2020-10-09 05:43:532200 pc_->NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED);
Harald Alvestrandcdcfab02020-09-28 13:02:072201
2202 // Check if negotiation is needed. We must do this after informing the
2203 // observer that SetLocalDescription() has completed to ensure negotiation is
2204 // not needed prior to the promise resolving.
2205 if (IsUnifiedPlan()) {
2206 bool was_negotiation_needed = is_negotiation_needed_;
2207 UpdateNegotiationNeeded();
2208 if (signaling_state() == PeerConnectionInterface::kStable &&
2209 was_negotiation_needed && is_negotiation_needed_) {
2210 // Legacy version.
2211 pc_->Observer()->OnRenegotiationNeeded();
2212 // Spec-compliant version; the event may get invalidated before firing.
2213 GenerateNegotiationNeededEvent();
2214 }
2215 }
2216
2217 // MaybeStartGathering needs to be called after informing the observer so that
2218 // we don't signal any candidates before signaling that SetLocalDescription
2219 // completed.
Harald Alvestrandbc32c562022-02-09 12:08:472220 transport_controller_s()->MaybeStartGathering();
Harald Alvestrandcdcfab02020-09-28 13:02:072221}
2222
2223void SdpOfferAnswerHandler::DoCreateOffer(
2224 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2225 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2226 RTC_DCHECK_RUN_ON(signaling_thread());
2227 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateOffer");
2228
2229 if (!observer) {
2230 RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
2231 return;
2232 }
2233
2234 if (pc_->IsClosed()) {
2235 std::string error = "CreateOffer called when PeerConnection is closed.";
2236 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:092237 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502238 observer.get(),
2239 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:072240 return;
2241 }
2242
2243 // If a session error has occurred the PeerConnection is in a possibly
2244 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:232245 if (session_error() != SessionError::kNone) {
2246 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:072247 RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message;
Harald Alvestrand1090e442020-10-05 07:01:092248 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502249 observer.get(),
Harald Alvestrandcdcfab02020-09-28 13:02:072250 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2251 return;
2252 }
2253
2254 if (!ValidateOfferAnswerOptions(options)) {
2255 std::string error = "CreateOffer called with invalid options.";
2256 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:092257 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502258 observer.get(),
2259 RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:072260 return;
2261 }
2262
2263 // Legacy handling for offer_to_receive_audio and offer_to_receive_video.
2264 // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions".
2265 if (IsUnifiedPlan()) {
Harald Alvestranda474fbf2020-10-01 16:47:232266 RTCError error = HandleLegacyOfferOptions(options);
Harald Alvestrandcdcfab02020-09-28 13:02:072267 if (!error.ok()) {
Harald Alvestrand1090e442020-10-05 07:01:092268 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502269 observer.get(), std::move(error));
Harald Alvestrandcdcfab02020-09-28 13:02:072270 return;
2271 }
2272 }
2273
2274 cricket::MediaSessionOptions session_options;
Harald Alvestrandc06e3742020-10-01 10:23:332275 GetOptionsForOffer(options, &session_options);
Niels Möllerafb246b2022-04-20 12:26:502276 webrtc_session_desc_factory_->CreateOffer(observer.get(), options,
2277 session_options);
Harald Alvestrandcdcfab02020-09-28 13:02:072278}
2279
2280void SdpOfferAnswerHandler::CreateAnswer(
2281 CreateSessionDescriptionObserver* observer,
2282 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
Markus Handell518669d2021-06-07 11:30:462283 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateAnswer");
Harald Alvestrandcdcfab02020-09-28 13:02:072284 RTC_DCHECK_RUN_ON(signaling_thread());
2285 // Chain this operation. If asynchronous operations are pending on the chain,
2286 // this operation will be queued to be invoked, otherwise the contents of the
2287 // lambda will execute immediately.
2288 operations_chain_->ChainOperation(
2289 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2290 observer_refptr =
2291 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
2292 options](std::function<void()> operations_chain_callback) {
Artem Titov880fa812021-07-30 20:30:232293 // Abort early if `this_weak_ptr` is no longer valid.
Harald Alvestrandcdcfab02020-09-28 13:02:072294 if (!this_weak_ptr) {
2295 observer_refptr->OnFailure(RTCError(
2296 RTCErrorType::INTERNAL_ERROR,
2297 "CreateAnswer failed because the session was shut down"));
2298 operations_chain_callback();
2299 return;
2300 }
2301 // The operation completes asynchronously when the wrapper is invoked.
Niels Möllerb7aac6f52021-08-23 13:48:062302 auto observer_wrapper = rtc::make_ref_counted<
2303 CreateSessionDescriptionObserverOperationWrapper>(
2304 std::move(observer_refptr), std::move(operations_chain_callback));
Harald Alvestrandcdcfab02020-09-28 13:02:072305 this_weak_ptr->DoCreateAnswer(options, observer_wrapper);
2306 });
2307}
2308
2309void SdpOfferAnswerHandler::DoCreateAnswer(
2310 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2311 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2312 RTC_DCHECK_RUN_ON(signaling_thread());
2313 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateAnswer");
2314 if (!observer) {
2315 RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
2316 return;
2317 }
2318
2319 // If a session error has occurred the PeerConnection is in a possibly
2320 // inconsistent state so fail right away.
Harald Alvestranda474fbf2020-10-01 16:47:232321 if (session_error() != SessionError::kNone) {
2322 std::string error_message = GetSessionErrorMsg();
Harald Alvestrandcdcfab02020-09-28 13:02:072323 RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message;
Harald Alvestrand1090e442020-10-05 07:01:092324 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502325 observer.get(),
Harald Alvestrandcdcfab02020-09-28 13:02:072326 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2327 return;
2328 }
2329
2330 if (!(signaling_state_ == PeerConnectionInterface::kHaveRemoteOffer ||
2331 signaling_state_ == PeerConnectionInterface::kHaveLocalPrAnswer)) {
2332 std::string error =
2333 "PeerConnection cannot create an answer in a state other than "
2334 "have-remote-offer or have-local-pranswer.";
2335 RTC_LOG(LS_ERROR) << error;
Harald Alvestrand1090e442020-10-05 07:01:092336 pc_->message_handler()->PostCreateSessionDescriptionFailure(
Niels Möllerafb246b2022-04-20 12:26:502337 observer.get(),
2338 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
Harald Alvestrandcdcfab02020-09-28 13:02:072339 return;
2340 }
2341
2342 // The remote description should be set if we're in the right state.
2343 RTC_DCHECK(remote_description());
2344
2345 if (IsUnifiedPlan()) {
2346 if (options.offer_to_receive_audio !=
2347 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2348 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not "
2349 "supported with Unified Plan semantics. Use the "
2350 "RtpTransceiver API instead.";
2351 }
2352 if (options.offer_to_receive_video !=
2353 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2354 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not "
2355 "supported with Unified Plan semantics. Use the "
2356 "RtpTransceiver API instead.";
2357 }
2358 }
2359
2360 cricket::MediaSessionOptions session_options;
Harald Alvestrandc06e3742020-10-01 10:23:332361 GetOptionsForAnswer(options, &session_options);
Niels Möllerafb246b2022-04-20 12:26:502362 webrtc_session_desc_factory_->CreateAnswer(observer.get(), session_options);
Harald Alvestrandcdcfab02020-09-28 13:02:072363}
2364
2365void SdpOfferAnswerHandler::DoSetRemoteDescription(
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042366 std::unique_ptr<RemoteDescriptionOperation> operation) {
Harald Alvestrandcdcfab02020-09-28 13:02:072367 RTC_DCHECK_RUN_ON(signaling_thread());
2368 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetRemoteDescription");
2369
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042370 if (!operation->ok())
Harald Alvestrandcdcfab02020-09-28 13:02:072371 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072372
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042373 if (operation->HaveSessionError())
Harald Alvestrandcdcfab02020-09-28 13:02:072374 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072375
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042376 if (operation->MaybeRollback())
Harald Alvestrandcdcfab02020-09-28 13:02:072377 return;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042378
2379 operation->ReportOfferAnswerUma();
2380
2381 // Handle remote descriptions missing a=mid lines for interop with legacy
2382 // end points.
2383 FillInMissingRemoteMids(operation->description());
2384 if (!operation->IsDescriptionValid())
Harald Alvestrandcdcfab02020-09-28 13:02:072385 return;
Harald Alvestrandcdcfab02020-09-28 13:02:072386
Tomas Gunnarsson0dd75392022-01-17 18:19:562387 ApplyRemoteDescription(std::move(operation));
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042388}
Harald Alvestrandcdcfab02020-09-28 13:02:072389
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042390// Called after a DoSetRemoteDescription operation completes.
2391void SdpOfferAnswerHandler::SetRemoteDescriptionPostProcess(bool was_answer) {
Harald Alvestrandcdcfab02020-09-28 13:02:072392 RTC_DCHECK(remote_description());
2393
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042394 if (was_answer) {
Harald Alvestrandcdcfab02020-09-28 13:02:072395 // TODO(deadbeef): We already had to hop to the network thread for
2396 // MaybeStartGathering...
Danil Chapovalov9e09a1f2022-09-08 16:38:102397 context_->network_thread()->BlockingCall(
2398 [this] { port_allocator()->DiscardCandidatePool(); });
Harald Alvestrandcdcfab02020-09-28 13:02:072399 }
2400
Harald Alvestrand44d0dff2020-10-09 05:43:532401 pc_->NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED);
Harald Alvestrandcdcfab02020-09-28 13:02:072402
2403 // Check if negotiation is needed. We must do this after informing the
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042404 // observer that SetRemoteDescription() has completed to ensure negotiation
2405 // is not needed prior to the promise resolving.
Harald Alvestrandcdcfab02020-09-28 13:02:072406 if (IsUnifiedPlan()) {
2407 bool was_negotiation_needed = is_negotiation_needed_;
2408 UpdateNegotiationNeeded();
2409 if (signaling_state() == PeerConnectionInterface::kStable &&
2410 was_negotiation_needed && is_negotiation_needed_) {
2411 // Legacy version.
2412 pc_->Observer()->OnRenegotiationNeeded();
2413 // Spec-compliant version; the event may get invalidated before firing.
2414 GenerateNegotiationNeededEvent();
2415 }
2416 }
2417}
2418
2419void SdpOfferAnswerHandler::SetAssociatedRemoteStreams(
2420 rtc::scoped_refptr<RtpReceiverInternal> receiver,
2421 const std::vector<std::string>& stream_ids,
2422 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* added_streams,
2423 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
2424 RTC_DCHECK_RUN_ON(signaling_thread());
2425 std::vector<rtc::scoped_refptr<MediaStreamInterface>> media_streams;
2426 for (const std::string& stream_id : stream_ids) {
Niels Möllere7cc8832022-01-04 14:20:032427 rtc::scoped_refptr<MediaStreamInterface> stream(
2428 remote_streams_->find(stream_id));
Harald Alvestrandcdcfab02020-09-28 13:02:072429 if (!stream) {
2430 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
2431 MediaStream::Create(stream_id));
Harald Alvestrand6f04b6532020-10-09 11:42:172432 remote_streams_->AddStream(stream);
Harald Alvestrandcdcfab02020-09-28 13:02:072433 added_streams->push_back(stream);
2434 }
2435 media_streams.push_back(stream);
2436 }
2437 // Special case: "a=msid" missing, use random stream ID.
2438 if (media_streams.empty() &&
2439 !(remote_description()->description()->msid_signaling() &
2440 cricket::kMsidSignalingMediaSection)) {
2441 if (!missing_msid_default_stream_) {
2442 missing_msid_default_stream_ = MediaStreamProxy::Create(
2443 rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid()));
2444 added_streams->push_back(missing_msid_default_stream_);
2445 }
2446 media_streams.push_back(missing_msid_default_stream_);
2447 }
2448 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
2449 receiver->streams();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042450 // SetStreams() will add/remove the receiver's track to/from the streams.
2451 // This differs from the spec - the spec uses an "addList" and "removeList"
2452 // to update the stream-track relationships in a later step. We do this
2453 // earlier, changing the order of things, but the end-result is the same.
Harald Alvestrandcdcfab02020-09-28 13:02:072454 // TODO(hbos): When we remove remote_streams(), use set_stream_ids()
2455 // instead. https://crbug.com/webrtc/9480
2456 receiver->SetStreams(media_streams);
Harald Alvestranda474fbf2020-10-01 16:47:232457 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
Harald Alvestrandcdcfab02020-09-28 13:02:072458}
2459
2460bool SdpOfferAnswerHandler::AddIceCandidate(
2461 const IceCandidateInterface* ice_candidate) {
Tomas Gunnarsson2efb8a52021-04-01 14:26:572462 const AddIceCandidateResult result = AddIceCandidateInternal(ice_candidate);
2463 NoteAddIceCandidateResult(result);
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042464 // If the return value is kAddIceCandidateFailNotReady, the candidate has
2465 // been added, although not 'ready', but that's a success.
Tomas Gunnarsson2efb8a52021-04-01 14:26:572466 return result == kAddIceCandidateSuccess ||
2467 result == kAddIceCandidateFailNotReady;
2468}
2469
2470AddIceCandidateResult SdpOfferAnswerHandler::AddIceCandidateInternal(
2471 const IceCandidateInterface* ice_candidate) {
Harald Alvestrandcdcfab02020-09-28 13:02:072472 RTC_DCHECK_RUN_ON(signaling_thread());
2473 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
2474 if (pc_->IsClosed()) {
2475 RTC_LOG(LS_ERROR) << "AddIceCandidate: PeerConnection is closed.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572476 return kAddIceCandidateFailClosed;
Harald Alvestrandcdcfab02020-09-28 13:02:072477 }
2478
2479 if (!remote_description()) {
2480 RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added "
2481 "without any remote session description.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572482 return kAddIceCandidateFailNoRemoteDescription;
Harald Alvestrandcdcfab02020-09-28 13:02:072483 }
2484
2485 if (!ice_candidate) {
2486 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572487 return kAddIceCandidateFailNullCandidate;
Harald Alvestrandcdcfab02020-09-28 13:02:072488 }
2489
2490 bool valid = false;
Harald Alvestrandbc9ca252020-10-05 13:08:412491 bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid);
Harald Alvestrandcdcfab02020-09-28 13:02:072492 if (!valid) {
Tomas Gunnarsson2efb8a52021-04-01 14:26:572493 return kAddIceCandidateFailNotValid;
Harald Alvestrandcdcfab02020-09-28 13:02:072494 }
2495
2496 // Add this candidate to the remote session description.
2497 if (!mutable_remote_description()->AddCandidate(ice_candidate)) {
2498 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572499 return kAddIceCandidateFailInAddition;
Harald Alvestrandcdcfab02020-09-28 13:02:072500 }
2501
Tomas Gunnarsson2efb8a52021-04-01 14:26:572502 if (!ready) {
Harald Alvestrandcdcfab02020-09-28 13:02:072503 RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate.";
Tomas Gunnarsson2efb8a52021-04-01 14:26:572504 return kAddIceCandidateFailNotReady;
Harald Alvestrandcdcfab02020-09-28 13:02:072505 }
Tomas Gunnarsson2efb8a52021-04-01 14:26:572506
2507 if (!UseCandidate(ice_candidate)) {
2508 return kAddIceCandidateFailNotUsable;
2509 }
2510
2511 pc_->NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED);
2512
2513 return kAddIceCandidateSuccess;
Harald Alvestrandcdcfab02020-09-28 13:02:072514}
2515
2516void SdpOfferAnswerHandler::AddIceCandidate(
2517 std::unique_ptr<IceCandidateInterface> candidate,
2518 std::function<void(RTCError)> callback) {
Markus Handell518669d2021-06-07 11:30:462519 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
Harald Alvestrandcdcfab02020-09-28 13:02:072520 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042521 // Chain this operation. If asynchronous operations are pending on the
2522 // chain, this operation will be queued to be invoked, otherwise the
2523 // contents of the lambda will execute immediately.
Harald Alvestrandcdcfab02020-09-28 13:02:072524 operations_chain_->ChainOperation(
2525 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2526 candidate = std::move(candidate), callback = std::move(callback)](
2527 std::function<void()> operations_chain_callback) {
Tomas Gunnarsson2efb8a52021-04-01 14:26:572528 auto result =
2529 this_weak_ptr
2530 ? this_weak_ptr->AddIceCandidateInternal(candidate.get())
2531 : kAddIceCandidateFailClosed;
2532 NoteAddIceCandidateResult(result);
2533 operations_chain_callback();
Henrik Boström347488e2022-01-21 14:18:082534 switch (result) {
2535 case AddIceCandidateResult::kAddIceCandidateSuccess:
2536 case AddIceCandidateResult::kAddIceCandidateFailNotReady:
2537 // Success!
2538 callback(RTCError::OK());
2539 break;
2540 case AddIceCandidateResult::kAddIceCandidateFailClosed:
2541 // Note that the spec says to just abort without resolving the
2542 // promise in this case, but this layer must return an RTCError.
2543 callback(RTCError(
2544 RTCErrorType::INVALID_STATE,
2545 "AddIceCandidate failed because the session was shut down"));
2546 break;
2547 case AddIceCandidateResult::kAddIceCandidateFailNoRemoteDescription:
2548 // Spec: "If remoteDescription is null return a promise rejected
2549 // with a newly created InvalidStateError."
2550 callback(RTCError(RTCErrorType::INVALID_STATE,
2551 "The remote description was null"));
2552 break;
2553 case AddIceCandidateResult::kAddIceCandidateFailNullCandidate:
2554 // TODO(https://crbug.com/935898): Handle end-of-candidates instead
2555 // of treating null candidate as an error.
2556 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2557 "Error processing ICE candidate"));
2558 break;
2559 case AddIceCandidateResult::kAddIceCandidateFailNotValid:
2560 case AddIceCandidateResult::kAddIceCandidateFailInAddition:
2561 case AddIceCandidateResult::kAddIceCandidateFailNotUsable:
2562 // Spec: "If candidate could not be successfully added [...] Reject
2563 // p with a newly created OperationError and abort these steps."
2564 // UNSUPPORTED_OPERATION maps to OperationError.
2565 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2566 "Error processing ICE candidate"));
2567 break;
2568 default:
2569 RTC_DCHECK_NOTREACHED();
Harald Alvestrandcdcfab02020-09-28 13:02:072570 }
Harald Alvestrandcdcfab02020-09-28 13:02:072571 });
2572}
2573
2574bool SdpOfferAnswerHandler::RemoveIceCandidates(
2575 const std::vector<cricket::Candidate>& candidates) {
2576 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveIceCandidates");
2577 RTC_DCHECK_RUN_ON(signaling_thread());
2578 if (pc_->IsClosed()) {
2579 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: PeerConnection is closed.";
2580 return false;
2581 }
2582
2583 if (!remote_description()) {
2584 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed "
2585 "without any remote session description.";
2586 return false;
2587 }
2588
2589 if (candidates.empty()) {
2590 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty.";
2591 return false;
2592 }
2593
2594 size_t number_removed =
2595 mutable_remote_description()->RemoveCandidates(candidates);
2596 if (number_removed != candidates.size()) {
2597 RTC_LOG(LS_ERROR)
2598 << "RemoveIceCandidates: Failed to remove candidates. Requested "
2599 << candidates.size() << " but only " << number_removed
2600 << " are removed.";
2601 }
2602
2603 // Remove the candidates from the transport controller.
Harald Alvestrandbc32c562022-02-09 12:08:472604 RTCError error = transport_controller_s()->RemoveRemoteCandidates(candidates);
Harald Alvestrandcdcfab02020-09-28 13:02:072605 if (!error.ok()) {
2606 RTC_LOG(LS_ERROR)
2607 << "RemoveIceCandidates: Error when removing remote candidates: "
2608 << error.message();
2609 }
2610 return true;
2611}
2612
2613void SdpOfferAnswerHandler::AddLocalIceCandidate(
2614 const JsepIceCandidate* candidate) {
2615 RTC_DCHECK_RUN_ON(signaling_thread());
2616 if (local_description()) {
2617 mutable_local_description()->AddCandidate(candidate);
2618 }
2619}
2620
2621void SdpOfferAnswerHandler::RemoveLocalIceCandidates(
2622 const std::vector<cricket::Candidate>& candidates) {
2623 RTC_DCHECK_RUN_ON(signaling_thread());
2624 if (local_description()) {
2625 mutable_local_description()->RemoveCandidates(candidates);
2626 }
2627}
2628
2629const SessionDescriptionInterface* SdpOfferAnswerHandler::local_description()
2630 const {
2631 RTC_DCHECK_RUN_ON(signaling_thread());
2632 return pending_local_description_ ? pending_local_description_.get()
2633 : current_local_description_.get();
2634}
2635
2636const SessionDescriptionInterface* SdpOfferAnswerHandler::remote_description()
2637 const {
2638 RTC_DCHECK_RUN_ON(signaling_thread());
2639 return pending_remote_description_ ? pending_remote_description_.get()
2640 : current_remote_description_.get();
2641}
2642
2643const SessionDescriptionInterface*
2644SdpOfferAnswerHandler::current_local_description() const {
2645 RTC_DCHECK_RUN_ON(signaling_thread());
2646 return current_local_description_.get();
2647}
2648
2649const SessionDescriptionInterface*
2650SdpOfferAnswerHandler::current_remote_description() const {
2651 RTC_DCHECK_RUN_ON(signaling_thread());
2652 return current_remote_description_.get();
2653}
2654
2655const SessionDescriptionInterface*
2656SdpOfferAnswerHandler::pending_local_description() const {
2657 RTC_DCHECK_RUN_ON(signaling_thread());
2658 return pending_local_description_.get();
2659}
2660
2661const SessionDescriptionInterface*
2662SdpOfferAnswerHandler::pending_remote_description() const {
2663 RTC_DCHECK_RUN_ON(signaling_thread());
2664 return pending_remote_description_.get();
2665}
2666
2667PeerConnectionInterface::SignalingState SdpOfferAnswerHandler::signaling_state()
2668 const {
2669 RTC_DCHECK_RUN_ON(signaling_thread());
2670 return signaling_state_;
2671}
2672
2673void SdpOfferAnswerHandler::ChangeSignalingState(
2674 PeerConnectionInterface::SignalingState signaling_state) {
Markus Handell518669d2021-06-07 11:30:462675 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ChangeSignalingState");
Harald Alvestrandcdcfab02020-09-28 13:02:072676 RTC_DCHECK_RUN_ON(signaling_thread());
2677 if (signaling_state_ == signaling_state) {
2678 return;
2679 }
2680 RTC_LOG(LS_INFO) << "Session: " << pc_->session_id() << " Old state: "
Harald Alvestrand31b03e92021-11-02 10:54:382681 << PeerConnectionInterface::AsString(signaling_state_)
Harald Alvestrandcdcfab02020-09-28 13:02:072682 << " New state: "
Harald Alvestrand31b03e92021-11-02 10:54:382683 << PeerConnectionInterface::AsString(signaling_state);
Harald Alvestrandcdcfab02020-09-28 13:02:072684 signaling_state_ = signaling_state;
2685 pc_->Observer()->OnSignalingChange(signaling_state_);
2686}
2687
2688RTCError SdpOfferAnswerHandler::UpdateSessionState(
2689 SdpType type,
2690 cricket::ContentSource source,
Henrik Boströmf8187e02021-04-26 19:04:262691 const cricket::SessionDescription* description,
2692 const std::map<std::string, const cricket::ContentGroup*>&
2693 bundle_groups_by_mid) {
Harald Alvestrandcdcfab02020-09-28 13:02:072694 RTC_DCHECK_RUN_ON(signaling_thread());
2695
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042696 // If there's already a pending error then no state transition should
2697 // happen. But all call-sites should be verifying this before calling us!
Harald Alvestranda474fbf2020-10-01 16:47:232698 RTC_DCHECK(session_error() == SessionError::kNone);
Harald Alvestrandcdcfab02020-09-28 13:02:072699
Taylor Brandstetterd0acbd82021-01-25 21:44:552700 // If this is answer-ish we're ready to let media flow.
2701 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
2702 EnableSending();
2703 }
2704
Harald Alvestrandcdcfab02020-09-28 13:02:072705 // Update the signaling state according to the specified state machine (see
2706 // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum).
2707 if (type == SdpType::kOffer) {
2708 ChangeSignalingState(source == cricket::CS_LOCAL
2709 ? PeerConnectionInterface::kHaveLocalOffer
2710 : PeerConnectionInterface::kHaveRemoteOffer);
2711 } else if (type == SdpType::kPrAnswer) {
2712 ChangeSignalingState(source == cricket::CS_LOCAL
2713 ? PeerConnectionInterface::kHaveLocalPrAnswer
2714 : PeerConnectionInterface::kHaveRemotePrAnswer);
2715 } else {
Harald Alvestrandcdcfab02020-09-28 13:02:072716 RTC_DCHECK(type == SdpType::kAnswer);
2717 ChangeSignalingState(PeerConnectionInterface::kStable);
Harald Alvestrand8101e7b2022-05-23 14:57:472718 if (ConfiguredForMedia()) {
2719 transceivers()->DiscardStableStates();
2720 }
Harald Alvestrandcdcfab02020-09-28 13:02:072721 }
2722
2723 // Update internal objects according to the session description's media
2724 // descriptions.
Tommicc7a3682021-05-04 12:59:382725 return PushdownMediaDescription(type, source, bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:072726}
2727
2728bool SdpOfferAnswerHandler::ShouldFireNegotiationNeededEvent(
2729 uint32_t event_id) {
2730 RTC_DCHECK_RUN_ON(signaling_thread());
2731 // Plan B? Always fire to conform with useless legacy behavior.
2732 if (!IsUnifiedPlan()) {
2733 return true;
2734 }
2735 // The event ID has been invalidated. Either negotiation is no longer needed
2736 // or a newer negotiation needed event has been generated.
2737 if (event_id != negotiation_needed_event_id_) {
2738 return false;
2739 }
2740 // The chain is no longer empty, update negotiation needed when it becomes
2741 // empty. This should generate a newer negotiation needed event, making this
2742 // one obsolete.
2743 if (!operations_chain_->IsEmpty()) {
2744 // Since we just suppressed an event that would have been fired, if
Tomas Gunnarsson1c7c09b2022-01-12 12:11:042745 // negotiation is still needed by the time the chain becomes empty again,
2746 // we must make sure to generate another event if negotiation is needed
2747 // then. This happens when `is_negotiation_needed_` goes from false to
2748 // true, so we set it to false until UpdateNegotiationNeeded() is called.
Harald Alvestrandcdcfab02020-09-28 13:02:072749 is_negotiation_needed_ = false;
2750 update_negotiation_needed_on_empty_chain_ = true;
2751 return false;
2752 }
2753 // We must not fire if the signaling state is no longer "stable". If
2754 // negotiation is still needed when we return to "stable", a new negotiation
2755 // needed event will be generated, so this one can safely be suppressed.
2756 if (signaling_state_ != PeerConnectionInterface::kStable) {
2757 return false;
2758 }
2759 // All checks have passed - please fire "negotiationneeded" now!
2760 return true;
2761}
2762
Harald Alvestrand6f04b6532020-10-09 11:42:172763rtc::scoped_refptr<StreamCollectionInterface>
2764SdpOfferAnswerHandler::local_streams() {
2765 RTC_DCHECK_RUN_ON(signaling_thread());
2766 RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified "
2767 "Plan SdpSemantics. Please use GetSenders "
2768 "instead.";
2769 return local_streams_;
2770}
2771
2772rtc::scoped_refptr<StreamCollectionInterface>
2773SdpOfferAnswerHandler::remote_streams() {
2774 RTC_DCHECK_RUN_ON(signaling_thread());
2775 RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified "
2776 "Plan SdpSemantics. Please use GetReceivers "
2777 "instead.";
2778 return remote_streams_;
2779}
2780
2781bool SdpOfferAnswerHandler::AddStream(MediaStreamInterface* local_stream) {
2782 RTC_DCHECK_RUN_ON(signaling_thread());
2783 RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan "
2784 "SdpSemantics. Please use AddTrack instead.";
2785 if (pc_->IsClosed()) {
2786 return false;
2787 }
Niels Möllerafb246b2022-04-20 12:26:502788 if (!CanAddLocalMediaStream(local_streams_.get(), local_stream)) {
Harald Alvestrand6f04b6532020-10-09 11:42:172789 return false;
2790 }
2791
Niels Möllere7cc8832022-01-04 14:20:032792 local_streams_->AddStream(
2793 rtc::scoped_refptr<MediaStreamInterface>(local_stream));
Mirko Bonadei9ff450d2021-08-02 08:56:332794 auto observer = std::make_unique<MediaStreamObserver>(
2795 local_stream,
2796 [this](AudioTrackInterface* audio_track,
2797 MediaStreamInterface* media_stream) {
2798 RTC_DCHECK_RUN_ON(signaling_thread());
2799 OnAudioTrackAdded(audio_track, media_stream);
2800 },
2801 [this](AudioTrackInterface* audio_track,
2802 MediaStreamInterface* media_stream) {
2803 RTC_DCHECK_RUN_ON(signaling_thread());
2804 OnAudioTrackRemoved(audio_track, media_stream);
2805 },
2806 [this](VideoTrackInterface* video_track,
2807 MediaStreamInterface* media_stream) {
2808 RTC_DCHECK_RUN_ON(signaling_thread());
2809 OnVideoTrackAdded(video_track, media_stream);
2810 },
2811 [this](VideoTrackInterface* video_track,
2812 MediaStreamInterface* media_stream) {
2813 RTC_DCHECK_RUN_ON(signaling_thread());
2814 OnVideoTrackRemoved(video_track, media_stream);
2815 });
2816 stream_observers_.push_back(std::move(observer));
Harald Alvestrand6f04b6532020-10-09 11:42:172817
2818 for (const auto& track : local_stream->GetAudioTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:052819 rtp_manager()->AddAudioTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:172820 }
2821 for (const auto& track : local_stream->GetVideoTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:052822 rtp_manager()->AddVideoTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:172823 }
2824
Henrik Boströmf7859892022-07-04 12:36:372825 pc_->legacy_stats()->AddStream(local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:172826 UpdateNegotiationNeeded();
2827 return true;
2828}
2829
2830void SdpOfferAnswerHandler::RemoveStream(MediaStreamInterface* local_stream) {
2831 RTC_DCHECK_RUN_ON(signaling_thread());
2832 RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified "
2833 "Plan SdpSemantics. Please use RemoveTrack "
2834 "instead.";
2835 TRACE_EVENT0("webrtc", "PeerConnection::RemoveStream");
2836 if (!pc_->IsClosed()) {
2837 for (const auto& track : local_stream->GetAudioTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:052838 rtp_manager()->RemoveAudioTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:172839 }
2840 for (const auto& track : local_stream->GetVideoTracks()) {
Harald Alvestrande15fb152020-10-19 13:28:052841 rtp_manager()->RemoveVideoTrack(track.get(), local_stream);
Harald Alvestrand6f04b6532020-10-09 11:42:172842 }
2843 }
2844 local_streams_->RemoveStream(local_stream);
2845 stream_observers_.erase(
2846 std::remove_if(
2847 stream_observers_.begin(), stream_observers_.end(),
2848 [local_stream](const std::unique_ptr<MediaStreamObserver>& observer) {
2849 return observer->stream()->id().compare(local_stream->id()) == 0;
2850 }),
2851 stream_observers_.end());
2852
2853 if (pc_->IsClosed()) {
2854 return;
2855 }
2856 UpdateNegotiationNeeded();
2857}
2858
Harald Alvestrand763f5a92020-10-22 10:39:402859void SdpOfferAnswerHandler::OnAudioTrackAdded(AudioTrackInterface* track,
2860 MediaStreamInterface* stream) {
2861 if (pc_->IsClosed()) {
2862 return;
2863 }
2864 rtp_manager()->AddAudioTrack(track, stream);
2865 UpdateNegotiationNeeded();
2866}
2867
2868void SdpOfferAnswerHandler::OnAudioTrackRemoved(AudioTrackInterface* track,
2869 MediaStreamInterface* stream) {
2870 if (pc_->IsClosed()) {
2871 return;
2872 }
2873 rtp_manager()->RemoveAudioTrack(track, stream);
2874 UpdateNegotiationNeeded();
2875}
2876
2877void SdpOfferAnswerHandler::OnVideoTrackAdded(VideoTrackInterface* track,
2878 MediaStreamInterface* stream) {
2879 if (pc_->IsClosed()) {
2880 return;
2881 }
2882 rtp_manager()->AddVideoTrack(track, stream);
2883 UpdateNegotiationNeeded();
2884}
2885
2886void SdpOfferAnswerHandler::OnVideoTrackRemoved(VideoTrackInterface* track,
2887 MediaStreamInterface* stream) {
2888 if (pc_->IsClosed()) {
2889 return;
2890 }
2891 rtp_manager()->RemoveVideoTrack(track, stream);
2892 UpdateNegotiationNeeded();
2893}
2894
Harald Alvestrandcdcfab02020-09-28 13:02:072895RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) {
Markus Handell518669d2021-06-07 11:30:462896 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::Rollback");
Harald Alvestrandcdcfab02020-09-28 13:02:072897 auto state = signaling_state();
2898 if (state != PeerConnectionInterface::kHaveLocalOffer &&
2899 state != PeerConnectionInterface::kHaveRemoteOffer) {
2900 return RTCError(RTCErrorType::INVALID_STATE,
Harald Alvestrand31b03e92021-11-02 10:54:382901 (rtc::StringBuilder("Called in wrong signalingState: ")
2902 << (PeerConnectionInterface::AsString(signaling_state())))
2903 .Release());
Harald Alvestrandcdcfab02020-09-28 13:02:072904 }
2905 RTC_DCHECK_RUN_ON(signaling_thread());
2906 RTC_DCHECK(IsUnifiedPlan());
Henrik Boström0a162762022-05-02 13:47:522907 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
2908 now_receiving_transceivers;
Harald Alvestrandcdcfab02020-09-28 13:02:072909 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_added_streams;
2910 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_removed_streams;
2911 std::vector<rtc::scoped_refptr<RtpReceiverInterface>> removed_receivers;
2912
Harald Alvestrande15fb152020-10-19 13:28:052913 for (auto&& transceivers_stable_state_pair : transceivers()->StableStates()) {
Harald Alvestrandcdcfab02020-09-28 13:02:072914 auto transceiver = transceivers_stable_state_pair.first;
2915 auto state = transceivers_stable_state_pair.second;
2916
Henrik Boström0a162762022-05-02 13:47:522917 if (state.did_set_fired_direction()) {
2918 // If this rollback triggers going from not receiving to receving again,
2919 // we need to fire "ontrack".
2920 bool previously_fired_direction_is_recv =
2921 transceiver->fired_direction().has_value() &&
2922 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction());
2923 bool currently_fired_direction_is_recv =
2924 state.fired_direction().has_value() &&
2925 RtpTransceiverDirectionHasRecv(state.fired_direction().value());
2926 if (!previously_fired_direction_is_recv &&
2927 currently_fired_direction_is_recv) {
2928 now_receiving_transceivers.push_back(transceiver);
2929 }
2930 transceiver->internal()->set_fired_direction(state.fired_direction());
2931 }
2932
Harald Alvestrandcdcfab02020-09-28 13:02:072933 if (state.remote_stream_ids()) {
2934 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
2935 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
2936 SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(),
2937 state.remote_stream_ids().value(),
2938 &added_streams, &removed_streams);
2939 all_added_streams.insert(all_added_streams.end(), added_streams.begin(),
2940 added_streams.end());
2941 all_removed_streams.insert(all_removed_streams.end(),
2942 removed_streams.begin(),
2943 removed_streams.end());
2944 if (!state.has_m_section() && !state.newly_created()) {
2945 continue;
2946 }
2947 }
2948
Henrik Boström0a162762022-05-02 13:47:522949 // Due to the above `continue` statement, the below code only runs if there
2950 // is a change in mid association (has_m_section), if the transceiver was
2951 // newly created (newly_created) or if remote streams were not set.
2952
Harald Alvestrandcdcfab02020-09-28 13:02:072953 RTC_DCHECK(transceiver->internal()->mid().has_value());
Harald Alvestrand19ebabc2022-04-28 13:31:172954 transceiver->internal()->ClearChannel();
Harald Alvestrandcdcfab02020-09-28 13:02:072955
2956 if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer &&
2957 transceiver->receiver()) {
2958 removed_receivers.push_back(transceiver->receiver());
2959 }
2960 if (state.newly_created()) {
2961 if (transceiver->internal()->reused_for_addtrack()) {
2962 transceiver->internal()->set_created_by_addtrack(true);
2963 } else {
Henrik Boströma8ad11d2022-04-27 09:54:012964 transceiver->internal()->StopTransceiverProcedure();
Harald Alvestrande15fb152020-10-19 13:28:052965 transceivers()->Remove(transceiver);
Harald Alvestrandcdcfab02020-09-28 13:02:072966 }
2967 }
Eldar Rello950d6b92021-04-06 19:38:002968 if (state.init_send_encodings()) {
2969 transceiver->internal()->sender_internal()->set_init_send_encodings(
2970 state.init_send_encodings().value());
2971 }
Harald Alvestrandcdcfab02020-09-28 13:02:072972 transceiver->internal()->sender_internal()->set_transport(nullptr);
2973 transceiver->internal()->receiver_internal()->set_transport(nullptr);
2974 transceiver->internal()->set_mid(state.mid());
2975 transceiver->internal()->set_mline_index(state.mline_index());
2976 }
Harald Alvestrandbc32c562022-02-09 12:08:472977 RTCError e = transport_controller_s()->RollbackTransports();
Taylor Brandstetter8591eff2021-08-11 21:56:382978 if (!e.ok()) {
2979 return e;
2980 }
Harald Alvestrande15fb152020-10-19 13:28:052981 transceivers()->DiscardStableStates();
Harald Alvestrandcdcfab02020-09-28 13:02:072982 pending_local_description_.reset();
2983 pending_remote_description_.reset();
2984 ChangeSignalingState(PeerConnectionInterface::kStable);
2985
2986 // Once all processing has finished, fire off callbacks.
Henrik Boström0a162762022-05-02 13:47:522987 for (const auto& transceiver : now_receiving_transceivers) {
2988 pc_->Observer()->OnTrack(transceiver);
2989 pc_->Observer()->OnAddTrack(transceiver->receiver(),
2990 transceiver->receiver()->streams());
2991 }
Harald Alvestrandcdcfab02020-09-28 13:02:072992 for (const auto& receiver : removed_receivers) {
2993 pc_->Observer()->OnRemoveTrack(receiver);
2994 }
2995 for (const auto& stream : all_added_streams) {
2996 pc_->Observer()->OnAddStream(stream);
2997 }
2998 for (const auto& stream : all_removed_streams) {
2999 pc_->Observer()->OnRemoveStream(stream);
3000 }
3001
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043002 // The assumption is that in case of implicit rollback
3003 // UpdateNegotiationNeeded gets called in SetRemoteDescription.
Harald Alvestrandcdcfab02020-09-28 13:02:073004 if (desc_type == SdpType::kRollback) {
3005 UpdateNegotiationNeeded();
3006 if (is_negotiation_needed_) {
3007 // Legacy version.
3008 pc_->Observer()->OnRenegotiationNeeded();
3009 // Spec-compliant version; the event may get invalidated before firing.
3010 GenerateNegotiationNeededEvent();
3011 }
3012 }
3013 return RTCError::OK();
3014}
3015
3016bool SdpOfferAnswerHandler::IsUnifiedPlan() const {
Harald Alvestrandcdcfab02020-09-28 13:02:073017 return pc_->IsUnifiedPlan();
3018}
3019
3020void SdpOfferAnswerHandler::OnOperationsChainEmpty() {
3021 RTC_DCHECK_RUN_ON(signaling_thread());
3022 if (pc_->IsClosed() || !update_negotiation_needed_on_empty_chain_)
3023 return;
3024 update_negotiation_needed_on_empty_chain_ = false;
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043025 // Firing when chain is empty is only supported in Unified Plan to avoid
3026 // Plan B regressions. (In Plan B, onnegotiationneeded is already broken
3027 // anyway, so firing it even more might just be confusing.)
Harald Alvestrandcdcfab02020-09-28 13:02:073028 if (IsUnifiedPlan()) {
3029 UpdateNegotiationNeeded();
3030 }
3031}
3032
3033absl::optional<bool> SdpOfferAnswerHandler::is_caller() {
3034 RTC_DCHECK_RUN_ON(signaling_thread());
3035 return is_caller_;
3036}
3037
3038bool SdpOfferAnswerHandler::HasNewIceCredentials() {
3039 RTC_DCHECK_RUN_ON(signaling_thread());
3040 return local_ice_credentials_to_replace_->HasIceCredentials();
3041}
3042
3043bool SdpOfferAnswerHandler::IceRestartPending(
3044 const std::string& content_name) const {
3045 RTC_DCHECK_RUN_ON(signaling_thread());
3046 return pending_ice_restarts_.find(content_name) !=
3047 pending_ice_restarts_.end();
3048}
3049
Harald Alvestrandf01bd6c2020-10-23 13:30:463050bool SdpOfferAnswerHandler::NeedsIceRestart(
3051 const std::string& content_name) const {
Tommic3257d02021-02-10 17:40:083052 return pc_->NeedsIceRestart(content_name);
Harald Alvestrandf01bd6c2020-10-23 13:30:463053}
3054
3055absl::optional<rtc::SSLRole> SdpOfferAnswerHandler::GetDtlsRole(
3056 const std::string& mid) const {
Harald Alvestrandbc32c562022-02-09 12:08:473057 RTC_DCHECK_RUN_ON(signaling_thread());
3058 return transport_controller_s()->GetDtlsRole(mid);
Harald Alvestrandf01bd6c2020-10-23 13:30:463059}
3060
Harald Alvestrandcdcfab02020-09-28 13:02:073061void SdpOfferAnswerHandler::UpdateNegotiationNeeded() {
3062 RTC_DCHECK_RUN_ON(signaling_thread());
3063 if (!IsUnifiedPlan()) {
3064 pc_->Observer()->OnRenegotiationNeeded();
3065 GenerateNegotiationNeededEvent();
3066 return;
3067 }
3068
3069 // In the spec, a task is queued here to run the following steps - this is
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043070 // meant to ensure we do not fire onnegotiationneeded prematurely if
3071 // multiple changes are being made at once. In order to support Chromium's
Harald Alvestrandcdcfab02020-09-28 13:02:073072 // implementation where the JavaScript representation of the PeerConnection
3073 // lives on a separate thread though, the queuing of a task is instead
3074 // performed by the PeerConnectionObserver posting from the signaling thread
3075 // to the JavaScript main thread that negotiation is needed. And because the
3076 // Operations Chain lives on the WebRTC signaling thread,
3077 // ShouldFireNegotiationNeededEvent() must be called before firing the event
3078 // to ensure the Operations Chain is still empty and the event has not been
3079 // invalidated.
3080
3081 // If connection's [[IsClosed]] slot is true, abort these steps.
3082 if (pc_->IsClosed())
3083 return;
3084
3085 // If connection's signaling state is not "stable", abort these steps.
3086 if (signaling_state() != PeerConnectionInterface::kStable)
3087 return;
3088
3089 // NOTE
3090 // The negotiation-needed flag will be updated once the state transitions to
3091 // "stable", as part of the steps for setting an RTCSessionDescription.
3092
3093 // If the result of checking if negotiation is needed is false, clear the
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043094 // negotiation-needed flag by setting connection's [[NegotiationNeeded]]
3095 // slot to false, and abort these steps.
Harald Alvestrandcdcfab02020-09-28 13:02:073096 bool is_negotiation_needed = CheckIfNegotiationIsNeeded();
3097 if (!is_negotiation_needed) {
3098 is_negotiation_needed_ = false;
3099 // Invalidate any negotiation needed event that may previosuly have been
3100 // generated.
3101 ++negotiation_needed_event_id_;
3102 return;
3103 }
3104
3105 // If connection's [[NegotiationNeeded]] slot is already true, abort these
3106 // steps.
3107 if (is_negotiation_needed_)
3108 return;
3109
3110 // Set connection's [[NegotiationNeeded]] slot to true.
3111 is_negotiation_needed_ = true;
3112
3113 // Queue a task that runs the following steps:
3114 // If connection's [[IsClosed]] slot is true, abort these steps.
3115 // If connection's [[NegotiationNeeded]] slot is false, abort these steps.
3116 // Fire an event named negotiationneeded at connection.
3117 pc_->Observer()->OnRenegotiationNeeded();
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043118 // Fire the spec-compliant version; when ShouldFireNegotiationNeededEvent()
3119 // is used in the task queued by the observer, this event will only fire
3120 // when the chain is empty.
Harald Alvestrandcdcfab02020-09-28 13:02:073121 GenerateNegotiationNeededEvent();
3122}
3123
3124bool SdpOfferAnswerHandler::CheckIfNegotiationIsNeeded() {
3125 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043126 // 1. If any implementation-specific negotiation is required, as described
3127 // at the start of this section, return true.
Harald Alvestrandcdcfab02020-09-28 13:02:073128
3129 // 2. If connection.[[LocalIceCredentialsToReplace]] is not empty, return
3130 // true.
3131 if (local_ice_credentials_to_replace_->HasIceCredentials()) {
3132 return true;
3133 }
3134
3135 // 3. Let description be connection.[[CurrentLocalDescription]].
3136 const SessionDescriptionInterface* description = current_local_description();
3137 if (!description)
3138 return true;
3139
3140 // 4. If connection has created any RTCDataChannels, and no m= section in
3141 // description has been negotiated yet for data, return true.
Harald Alvestrandbc9ca252020-10-05 13:08:413142 if (data_channel_controller()->HasSctpDataChannels()) {
Harald Alvestrandcdcfab02020-09-28 13:02:073143 if (!cricket::GetFirstDataContent(description->description()->contents()))
3144 return true;
3145 }
Harald Alvestrand8101e7b2022-05-23 14:57:473146 if (!ConfiguredForMedia()) {
3147 return false;
3148 }
Harald Alvestrandcdcfab02020-09-28 13:02:073149
3150 // 5. For each transceiver in connection's set of transceivers, perform the
3151 // following checks:
Harald Alvestrand85466662021-04-19 21:21:363152 for (const auto& transceiver : transceivers()->ListInternal()) {
Harald Alvestrandcdcfab02020-09-28 13:02:073153 const ContentInfo* current_local_msection =
Harald Alvestrand85466662021-04-19 21:21:363154 FindTransceiverMSection(transceiver, description);
Harald Alvestrandcdcfab02020-09-28 13:02:073155
Harald Alvestrand85466662021-04-19 21:21:363156 const ContentInfo* current_remote_msection =
3157 FindTransceiverMSection(transceiver, current_remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:073158
3159 // 5.4 If transceiver is stopped and is associated with an m= section,
3160 // but the associated m= section is not yet rejected in
3161 // connection.[[CurrentLocalDescription]] or
3162 // connection.[[CurrentRemoteDescription]], return true.
3163 if (transceiver->stopped()) {
3164 RTC_DCHECK(transceiver->stopping());
3165 if (current_local_msection && !current_local_msection->rejected &&
3166 ((current_remote_msection && !current_remote_msection->rejected) ||
3167 !current_remote_msection)) {
3168 return true;
3169 }
3170 continue;
3171 }
3172
3173 // 5.1 If transceiver.[[Stopping]] is true and transceiver.[[Stopped]] is
3174 // false, return true.
3175 if (transceiver->stopping() && !transceiver->stopped())
3176 return true;
3177
3178 // 5.2 If transceiver isn't stopped and isn't yet associated with an m=
3179 // section in description, return true.
3180 if (!current_local_msection)
3181 return true;
3182
3183 const MediaContentDescription* current_local_media_description =
3184 current_local_msection->media_description();
3185 // 5.3 If transceiver isn't stopped and is associated with an m= section
3186 // in description then perform the following checks:
3187
3188 // 5.3.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the
3189 // associated m= section in description either doesn't contain a single
3190 // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this
3191 // m= section, or the MSID values themselves, differ from what is in
3192 // transceiver.sender.[[AssociatedMediaStreamIds]], return true.
3193 if (RtpTransceiverDirectionHasSend(transceiver->direction())) {
3194 if (current_local_media_description->streams().size() == 0)
3195 return true;
3196
3197 std::vector<std::string> msection_msids;
3198 for (const auto& stream : current_local_media_description->streams()) {
3199 for (const std::string& msid : stream.stream_ids())
3200 msection_msids.push_back(msid);
3201 }
3202
3203 std::vector<std::string> transceiver_msids =
3204 transceiver->sender()->stream_ids();
3205 if (msection_msids.size() != transceiver_msids.size())
3206 return true;
3207
3208 absl::c_sort(transceiver_msids);
3209 absl::c_sort(msection_msids);
3210 if (transceiver_msids != msection_msids)
3211 return true;
3212 }
3213
3214 // 5.3.2 If description is of type "offer", and the direction of the
3215 // associated m= section in neither connection.[[CurrentLocalDescription]]
3216 // nor connection.[[CurrentRemoteDescription]] matches
3217 // transceiver.[[Direction]], return true.
3218 if (description->GetType() == SdpType::kOffer) {
3219 if (!current_remote_description())
3220 return true;
3221
3222 if (!current_remote_msection)
3223 return true;
3224
3225 RtpTransceiverDirection current_local_direction =
3226 current_local_media_description->direction();
3227 RtpTransceiverDirection current_remote_direction =
3228 current_remote_msection->media_description()->direction();
3229 if (transceiver->direction() != current_local_direction &&
3230 transceiver->direction() !=
3231 RtpTransceiverDirectionReversed(current_remote_direction)) {
3232 return true;
3233 }
3234 }
3235
3236 // 5.3.3 If description is of type "answer", and the direction of the
3237 // associated m= section in the description does not match
3238 // transceiver.[[Direction]] intersected with the offered direction (as
3239 // described in [JSEP] (section 5.3.1.)), return true.
3240 if (description->GetType() == SdpType::kAnswer) {
3241 if (!remote_description())
3242 return true;
3243
3244 const ContentInfo* offered_remote_msection =
Harald Alvestrand85466662021-04-19 21:21:363245 FindTransceiverMSection(transceiver, remote_description());
Harald Alvestrandcdcfab02020-09-28 13:02:073246
3247 RtpTransceiverDirection offered_direction =
3248 offered_remote_msection
3249 ? offered_remote_msection->media_description()->direction()
3250 : RtpTransceiverDirection::kInactive;
3251
3252 if (current_local_media_description->direction() !=
3253 (RtpTransceiverDirectionIntersection(
3254 transceiver->direction(),
3255 RtpTransceiverDirectionReversed(offered_direction)))) {
3256 return true;
3257 }
3258 }
3259 }
Harald Alvestrandcdcfab02020-09-28 13:02:073260 // If all the preceding checks were performed and true was not returned,
3261 // nothing remains to be negotiated; return false.
3262 return false;
3263}
3264
3265void SdpOfferAnswerHandler::GenerateNegotiationNeededEvent() {
3266 RTC_DCHECK_RUN_ON(signaling_thread());
3267 ++negotiation_needed_event_id_;
3268 pc_->Observer()->OnNegotiationNeededEvent(negotiation_needed_event_id_);
3269}
3270
3271RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
3272 const SessionDescriptionInterface* sdesc,
Henrik Boströmf8187e02021-04-26 19:04:263273 cricket::ContentSource source,
3274 const std::map<std::string, const cricket::ContentGroup*>&
3275 bundle_groups_by_mid) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043276 // An assumption is that a check for session error is done at a higher level.
3277 RTC_DCHECK_EQ(SessionError::kNone, session_error());
Harald Alvestrandcdcfab02020-09-28 13:02:073278
3279 if (!sdesc || !sdesc->description()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563280 return RTCError(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
Harald Alvestrandcdcfab02020-09-28 13:02:073281 }
3282
3283 SdpType type = sdesc->GetType();
Harald Alvestrandc06e3742020-10-01 10:23:333284 if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) ||
3285 (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563286 return RTCError(RTCErrorType::INVALID_STATE,
3287 (rtc::StringBuilder("Called in wrong state: ")
3288 << PeerConnectionInterface::AsString(signaling_state()))
3289 .Release());
Harald Alvestrandcdcfab02020-09-28 13:02:073290 }
3291
3292 RTCError error = ValidateMids(*sdesc->description());
3293 if (!error.ok()) {
3294 return error;
3295 }
3296
3297 // Verify crypto settings.
3298 std::string crypto_error;
Harald Alvestrand0d018412021-11-04 13:52:313299 if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
3300 pc_->dtls_enabled()) {
3301 RTCError crypto_error = VerifyCrypto(
3302 sdesc->description(), pc_->dtls_enabled(), bundle_groups_by_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:073303 if (!crypto_error.ok()) {
3304 return crypto_error;
3305 }
3306 }
3307
3308 // Verify ice-ufrag and ice-pwd.
Henrik Boströmf8187e02021-04-26 19:04:263309 if (!VerifyIceUfragPwdPresent(sdesc->description(), bundle_groups_by_mid)) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563310 return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutIceUfragPwd);
Harald Alvestrandcdcfab02020-09-28 13:02:073311 }
3312
Henrik Boströmf8187e02021-04-26 19:04:263313 if (!pc_->ValidateBundleSettings(sdesc->description(),
3314 bundle_groups_by_mid)) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563315 return RTCError(RTCErrorType::INVALID_PARAMETER, kBundleWithoutRtcpMux);
Harald Alvestrandcdcfab02020-09-28 13:02:073316 }
3317
3318 // TODO(skvlad): When the local rtcp-mux policy is Require, reject any
3319 // m-lines that do not rtcp-mux enabled.
3320
3321 // Verify m-lines in Answer when compared against Offer.
3322 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043323 // With an answer we want to compare the new answer session description
3324 // with the offer's session description from the current negotiation.
Harald Alvestrandcdcfab02020-09-28 13:02:073325 const cricket::SessionDescription* offer_desc =
3326 (source == cricket::CS_LOCAL) ? remote_description()->description()
3327 : local_description()->description();
3328 if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) ||
3329 !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(),
3330 type)) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563331 return RTCError(RTCErrorType::INVALID_PARAMETER, kMlineMismatchInAnswer);
Harald Alvestrandcdcfab02020-09-28 13:02:073332 }
3333 } else {
3334 // The re-offers should respect the order of m= sections in current
3335 // description. See RFC3264 Section 8 paragraph 4 for more details.
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043336 // With a re-offer, either the current local or current remote
3337 // descriptions could be the most up to date, so we would like to check
3338 // against both of them if they exist. It could be the case that one of
3339 // them has a 0 port for a media section, but the other does not. This is
3340 // important to check against in the case that we are recycling an m=
3341 // section.
Harald Alvestrandcdcfab02020-09-28 13:02:073342 const cricket::SessionDescription* current_desc = nullptr;
3343 const cricket::SessionDescription* secondary_current_desc = nullptr;
3344 if (local_description()) {
3345 current_desc = local_description()->description();
3346 if (remote_description()) {
3347 secondary_current_desc = remote_description()->description();
3348 }
3349 } else if (remote_description()) {
3350 current_desc = remote_description()->description();
3351 }
3352 if (current_desc &&
3353 !MediaSectionsInSameOrder(*current_desc, secondary_current_desc,
3354 *sdesc->description(), type)) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563355 return RTCError(RTCErrorType::INVALID_PARAMETER,
3356 kMlineMismatchInSubsequentOffer);
Harald Alvestrandcdcfab02020-09-28 13:02:073357 }
3358 }
3359
3360 if (IsUnifiedPlan()) {
3361 // Ensure that each audio and video media section has at most one
3362 // "StreamParams". This will return an error if receiving a session
3363 // description from a "Plan B" endpoint which adds multiple tracks of the
3364 // same type. With Unified Plan, there can only be at most one track per
3365 // media section.
3366 for (const ContentInfo& content : sdesc->description()->contents()) {
3367 const MediaContentDescription& desc = *content.media_description();
3368 if ((desc.type() == cricket::MEDIA_TYPE_AUDIO ||
3369 desc.type() == cricket::MEDIA_TYPE_VIDEO) &&
3370 desc.streams().size() > 1u) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563371 return RTCError(
3372 RTCErrorType::INVALID_PARAMETER,
3373 "Media section has more than one track specified with a=ssrc lines "
3374 "which is not supported with Unified Plan.");
Harald Alvestrandcdcfab02020-09-28 13:02:073375 }
3376 }
3377 }
3378
3379 return RTCError::OK();
3380}
3381
3382RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels(
3383 cricket::ContentSource source,
3384 const SessionDescriptionInterface& new_session,
3385 const SessionDescriptionInterface* old_local_description,
Henrik Boströmf8187e02021-04-26 19:04:263386 const SessionDescriptionInterface* old_remote_description,
3387 const std::map<std::string, const cricket::ContentGroup*>&
3388 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:463389 TRACE_EVENT0("webrtc",
3390 "SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels");
Harald Alvestrandcdcfab02020-09-28 13:02:073391 RTC_DCHECK_RUN_ON(signaling_thread());
3392 RTC_DCHECK(IsUnifiedPlan());
3393
Harald Alvestrandcdcfab02020-09-28 13:02:073394 if (new_session.GetType() == SdpType::kOffer) {
Henrik Boströmf8187e02021-04-26 19:04:263395 // If the BUNDLE policy is max-bundle, then we know for sure that all
Tomas Gunnarsson1c7c09b2022-01-12 12:11:043396 // transports will be bundled from the start. Return an error if
3397 // max-bundle is specified but the session description does not have a
3398 // BUNDLE group.
Henrik Boströmf8187e02021-04-26 19:04:263399 if (pc_->configuration()->bundle_policy ==
3400 PeerConnectionInterface::kBundlePolicyMaxBundle &&
3401 bundle_groups_by_mid.empty()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563402 return RTCError(
3403 RTCErrorType::INVALID_PARAMETER,
3404 "max-bundle configured but session description has no BUNDLE group");
Harald Alvestrandcdcfab02020-09-28 13:02:073405 }
Harald Alvestrandcdcfab02020-09-28 13:02:073406 }
3407
3408 const ContentInfos& new_contents = new_session.description()->contents();
3409 for (size_t i = 0; i < new_contents.size(); ++i) {
3410 const cricket::ContentInfo& new_content = new_contents[i];
3411 cricket::MediaType media_type = new_content.media_description()->type();
Harald Alvestrandbc9ca252020-10-05 13:08:413412 mid_generator_.AddKnownId(new_content.name);
Henrik Boströmf8187e02021-04-26 19:04:263413 auto it = bundle_groups_by_mid.find(new_content.name);
3414 const cricket::ContentGroup* bundle_group =
3415 it != bundle_groups_by_mid.end() ? it->second : nullptr;
Harald Alvestrandcdcfab02020-09-28 13:02:073416 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
3417 media_type == cricket::MEDIA_TYPE_VIDEO) {
3418 const cricket::ContentInfo* old_local_content = nullptr;
3419 if (old_local_description &&
3420 i < old_local_description->description()->contents().size()) {
3421 old_local_content =
3422 &old_local_description->description()->contents()[i];
3423 }
3424 const cricket::ContentInfo* old_remote_content = nullptr;
3425 if (old_remote_description &&
3426 i < old_remote_description->description()->contents().size()) {
3427 old_remote_content =
3428 &old_remote_description->description()->contents()[i];
3429 }
Harald Alvestrandcdcfab02020-09-28 13:02:073430 auto transceiver_or_error =
3431 AssociateTransceiver(source, new_session.GetType(), i, new_content,
3432 old_local_content, old_remote_content);
3433 if (!transceiver_or_error.ok()) {
Henrik Boströma8ad11d2022-04-27 09:54:013434 // In the case where a transceiver is rejected locally prior to being
3435 // associated, we don't expect to find a transceiver, but might find it
3436 // in the case where state is still "stopping", not "stopped".
Harald Alvestrand09bd9ba2020-10-09 08:13:303437 if (new_content.rejected) {
3438 continue;
3439 }
Harald Alvestrandcdcfab02020-09-28 13:02:073440 return transceiver_or_error.MoveError();
3441 }
3442 auto transceiver = transceiver_or_error.MoveValue();
3443 RTCError error =
3444 UpdateTransceiverChannel(transceiver, new_content, bundle_group);
Henrik Boströma8ad11d2022-04-27 09:54:013445 // Handle locally rejected content. This code path is only needed for apps
3446 // that SDP munge. Remote rejected content is handled in
3447 // ApplyRemoteDescriptionUpdateTransceiverState().
3448 if (source == cricket::ContentSource::CS_LOCAL && new_content.rejected) {
3449 // Local offer.
3450 if (new_session.GetType() == SdpType::kOffer) {
3451 // If the RtpTransceiver API was used, it would already have made the
3452 // transceiver stopping. But if the rejection was caused by SDP
3453 // munging then we need to ensure the transceiver is stopping here.
3454 if (!transceiver->internal()->stopping()) {
3455 transceiver->internal()->StopStandard();
3456 }
3457 RTC_DCHECK(transceiver->internal()->stopping());
3458 } else {
3459 // Local answer.
3460 RTC_DCHECK(new_session.GetType() == SdpType::kAnswer ||
3461 new_session.GetType() == SdpType::kPrAnswer);
3462 // When RtpTransceiver API is used, rejection happens in the offer and
3463 // the transceiver will already be stopped at local answer time
3464 // (calling stop between SRD(offer) and SLD(answer) would not reject
3465 // the content in the answer - instead this would trigger a follow-up
3466 // O/A exchange). So if the content was rejected but the transceiver
3467 // is not already stopped, SDP munging has happened and we need to
3468 // ensure the transceiver is stopped.
3469 if (!transceiver->internal()->stopped()) {
3470 transceiver->internal()->StopTransceiverProcedure();
3471 }
3472 RTC_DCHECK(transceiver->internal()->stopped());
3473 }
3474 }
Harald Alvestrandcdcfab02020-09-28 13:02:073475 if (!error.ok()) {
3476 return error;
3477 }
3478 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3479 if (pc_->GetDataMid() && new_content.name != *(pc_->GetDataMid())) {
3480 // Ignore all but the first data section.
3481 RTC_LOG(LS_INFO) << "Ignoring data media section with MID="
3482 << new_content.name;
3483 continue;
3484 }
3485 RTCError error = UpdateDataChannel(source, new_content, bundle_group);
3486 if (!error.ok()) {
3487 return error;
3488 }
Philipp Hancke4e8c1152020-10-13 10:43:153489 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
3490 RTC_LOG(LS_INFO) << "Ignoring unsupported media type";
Harald Alvestrandcdcfab02020-09-28 13:02:073491 } else {
Tomas Gunnarsson0dd75392022-01-17 18:19:563492 return RTCError(RTCErrorType::INTERNAL_ERROR, "Unknown section type.");
Harald Alvestrandcdcfab02020-09-28 13:02:073493 }
3494 }
3495
3496 return RTCError::OK();
3497}
3498
3499RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
3500SdpOfferAnswerHandler::AssociateTransceiver(
3501 cricket::ContentSource source,
3502 SdpType type,
3503 size_t mline_index,
3504 const ContentInfo& content,
3505 const ContentInfo* old_local_content,
3506 const ContentInfo* old_remote_content) {
Markus Handell518669d2021-06-07 11:30:463507 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AssociateTransceiver");
Harald Alvestrandcdcfab02020-09-28 13:02:073508 RTC_DCHECK(IsUnifiedPlan());
Tomas Gunnarsson36992362020-10-05 19:41:363509#if RTC_DCHECK_IS_ON
Harald Alvestrandcdcfab02020-09-28 13:02:073510 // If this is an offer then the m= section might be recycled. If the m=
3511 // section is being recycled (defined as: rejected in the current local or
3512 // remote description and not rejected in new description), the transceiver
Harald Alvestrande15fb152020-10-19 13:28:053513 // should have been removed by RemoveStoppedtransceivers()->
Harald Alvestrandcdcfab02020-09-28 13:02:073514 if (IsMediaSectionBeingRecycled(type, content, old_local_content,
3515 old_remote_content)) {
3516 const std::string& old_mid =
3517 (old_local_content && old_local_content->rejected)
3518 ? old_local_content->name
3519 : old_remote_content->name;
Harald Alvestrande15fb152020-10-19 13:28:053520 auto old_transceiver = transceivers()->FindByMid(old_mid);
Harald Alvestrandcdcfab02020-09-28 13:02:073521 // The transceiver should be disassociated in RemoveStoppedTransceivers()
3522 RTC_DCHECK(!old_transceiver);
3523 }
Tomas Gunnarsson36992362020-10-05 19:41:363524#endif
3525
Harald Alvestrandcdcfab02020-09-28 13:02:073526 const MediaContentDescription* media_desc = content.media_description();
Harald Alvestrande15fb152020-10-19 13:28:053527 auto transceiver = transceivers()->FindByMid(content.name);
Harald Alvestrandcdcfab02020-09-28 13:02:073528 if (source == cricket::CS_LOCAL) {
3529 // Find the RtpTransceiver that corresponds to this m= section, using the
3530 // mapping between transceivers and m= section indices established when
3531 // creating the offer.
3532 if (!transceiver) {
Harald Alvestrande15fb152020-10-19 13:28:053533 transceiver = transceivers()->FindByMLineIndex(mline_index);
Harald Alvestrandcdcfab02020-09-28 13:02:073534 }
3535 if (!transceiver) {
Harald Alvestrand09bd9ba2020-10-09 08:13:303536 // This may happen normally when media sections are rejected.
Tomas Gunnarsson0dd75392022-01-17 18:19:563537 return RTCError(RTCErrorType::INVALID_PARAMETER,
3538 "Transceiver not found based on m-line index");
Harald Alvestrandcdcfab02020-09-28 13:02:073539 }
3540 } else {
3541 RTC_DCHECK_EQ(source, cricket::CS_REMOTE);
3542 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers
3543 // of the same type...
3544 // When simulcast is requested, a transceiver cannot be associated because
3545 // AddTrack cannot be called to initialize it.
3546 if (!transceiver &&
3547 RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
3548 !media_desc->HasSimulcast()) {
Harald Alvestrandc06e3742020-10-01 10:23:333549 transceiver = FindAvailableTransceiverToReceive(media_desc->type());
Harald Alvestrandcdcfab02020-09-28 13:02:073550 }
3551 // If no RtpTransceiver was found in the previous step, create one with a
3552 // recvonly direction.
3553 if (!transceiver) {
3554 RTC_LOG(LS_INFO) << "Adding "
3555 << cricket::MediaTypeToString(media_desc->type())
3556 << " transceiver for MID=" << content.name
3557 << " at i=" << mline_index
3558 << " in response to the remote description.";
3559 std::string sender_id = rtc::CreateRandomUuid();
3560 std::vector<RtpEncodingParameters> send_encodings =
3561 GetSendEncodingsFromRemoteDescription(*media_desc);
Harald Alvestrande15fb152020-10-19 13:28:053562 auto sender = rtp_manager()->CreateSender(media_desc->type(), sender_id,
3563 nullptr, {}, send_encodings);
Harald Alvestrandcdcfab02020-09-28 13:02:073564 std::string receiver_id;
3565 if (!media_desc->streams().empty()) {
3566 receiver_id = media_desc->streams()[0].id;
3567 } else {
3568 receiver_id = rtc::CreateRandomUuid();
3569 }
Harald Alvestrande15fb152020-10-19 13:28:053570 auto receiver =
3571 rtp_manager()->CreateReceiver(media_desc->type(), receiver_id);
3572 transceiver = rtp_manager()->CreateAndAddTransceiver(sender, receiver);
Harald Alvestrandcdcfab02020-09-28 13:02:073573 transceiver->internal()->set_direction(
3574 RtpTransceiverDirection::kRecvOnly);
3575 if (type == SdpType::kOffer) {
Harald Alvestrande15fb152020-10-19 13:28:053576 transceivers()->StableState(transceiver)->set_newly_created();
Harald Alvestrandcdcfab02020-09-28 13:02:073577 }
3578 }
Tomas Gunnarsson36992362020-10-05 19:41:363579
3580 RTC_DCHECK(transceiver);
3581
Harald Alvestrandcdcfab02020-09-28 13:02:073582 // Check if the offer indicated simulcast but the answer rejected it.
3583 // This can happen when simulcast is not supported on the remote party.
Lennart Grahl0d0ed762021-05-17 14:06:373584 if (SimulcastIsRejected(old_local_content, *media_desc,
3585 pc_->GetCryptoOptions()
3586 .srtp.enable_encrypted_rtp_header_extensions)) {
Harald Alvestrandcdcfab02020-09-28 13:02:073587 RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true);
3588 RTCError error =
3589 DisableSimulcastInSender(transceiver->internal()->sender_internal());
3590 if (!error.ok()) {
3591 RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast.";
3592 return std::move(error);
3593 }
3594 }
3595 }
Tomas Gunnarsson36992362020-10-05 19:41:363596
Harald Alvestrandcdcfab02020-09-28 13:02:073597 if (transceiver->media_type() != media_desc->type()) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563598 return RTCError(RTCErrorType::INVALID_PARAMETER,
3599 "Transceiver type does not match media description type.");
Harald Alvestrandcdcfab02020-09-28 13:02:073600 }
Tomas Gunnarsson36992362020-10-05 19:41:363601
Harald Alvestrandcdcfab02020-09-28 13:02:073602 if (media_desc->HasSimulcast()) {
3603 std::vector<SimulcastLayer> layers =
3604 source == cricket::CS_LOCAL
3605 ? media_desc->simulcast_description().send_layers().GetAllLayers()
3606 : media_desc->simulcast_description()
3607 .receive_layers()
3608 .GetAllLayers();
3609 RTCError error = UpdateSimulcastLayerStatusInSender(
3610 layers, transceiver->internal()->sender_internal());
3611 if (!error.ok()) {
3612 RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers.";
3613 return std::move(error);
3614 }
3615 }
3616 if (type == SdpType::kOffer) {
3617 bool state_changes = transceiver->internal()->mid() != content.name ||
3618 transceiver->internal()->mline_index() != mline_index;
3619 if (state_changes) {
Harald Alvestrandbc9ca252020-10-05 13:08:413620 transceivers()
Harald Alvestrande15fb152020-10-19 13:28:053621 ->StableState(transceiver)
Harald Alvestrand38b768c2020-09-29 11:54:053622 ->SetMSectionIfUnset(transceiver->internal()->mid(),
3623 transceiver->internal()->mline_index());
Harald Alvestrandcdcfab02020-09-28 13:02:073624 }
3625 }
3626 // Associate the found or created RtpTransceiver with the m= section by
3627 // setting the value of the RtpTransceiver's mid property to the MID of the m=
3628 // section, and establish a mapping between the transceiver and the index of
3629 // the m= section.
3630 transceiver->internal()->set_mid(content.name);
3631 transceiver->internal()->set_mline_index(mline_index);
3632 return std::move(transceiver);
3633}
3634
Harald Alvestrandcdcfab02020-09-28 13:02:073635RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel(
3636 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
3637 transceiver,
3638 const cricket::ContentInfo& content,
3639 const cricket::ContentGroup* bundle_group) {
Markus Handell518669d2021-06-07 11:30:463640 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateTransceiverChannel");
Harald Alvestrandcdcfab02020-09-28 13:02:073641 RTC_DCHECK(IsUnifiedPlan());
3642 RTC_DCHECK(transceiver);
3643 cricket::ChannelInterface* channel = transceiver->internal()->channel();
3644 if (content.rejected) {
3645 if (channel) {
Harald Alvestrand19ebabc2022-04-28 13:31:173646 transceiver->internal()->ClearChannel();
Harald Alvestrandcdcfab02020-09-28 13:02:073647 }
3648 } else {
3649 if (!channel) {
Harald Alvestrand8f429922022-05-04 10:32:303650 auto error = transceiver->internal()->CreateChannel(
3651 content.name, pc_->call_ptr(), pc_->configuration()->media_config,
3652 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
3653 video_options(), video_bitrate_allocator_factory_.get(),
3654 [&](absl::string_view mid) {
Harald Alvestrand3af79d12022-04-29 15:04:583655 RTC_DCHECK_RUN_ON(network_thread());
3656 return transport_controller_n()->GetRtpTransport(mid);
3657 });
Harald Alvestrand8f429922022-05-04 10:32:303658 if (!error.ok()) {
3659 return error;
3660 }
Harald Alvestrandcdcfab02020-09-28 13:02:073661 }
3662 }
3663 return RTCError::OK();
3664}
3665
3666RTCError SdpOfferAnswerHandler::UpdateDataChannel(
3667 cricket::ContentSource source,
3668 const cricket::ContentInfo& content,
3669 const cricket::ContentGroup* bundle_group) {
Harald Alvestrandcdcfab02020-09-28 13:02:073670 if (content.rejected) {
Florent Castellidcb9ffc2021-06-29 12:58:233671 RTC_LOG(LS_INFO) << "Rejected data channel transport with mid="
3672 << content.mid();
3673
3674 rtc::StringBuilder sb;
3675 sb << "Rejected data channel transport with mid=" << content.mid();
3676 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
3677 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
3678 DestroyDataChannelTransport(error);
Harald Alvestrandcdcfab02020-09-28 13:02:073679 } else {
Harald Alvestrand7af57c62021-04-16 11:12:143680 if (!data_channel_controller()->data_channel_transport()) {
Harald Alvestrandcdcfab02020-09-28 13:02:073681 RTC_LOG(LS_INFO) << "Creating data channel, mid=" << content.mid();
Harald Alvestrandbc9ca252020-10-05 13:08:413682 if (!CreateDataChannel(content.name)) {
Tomas Gunnarsson0dd75392022-01-17 18:19:563683 return RTCError(RTCErrorType::INTERNAL_ERROR,
3684 "Failed to create data channel.");
Harald Alvestrandcdcfab02020-09-28 13:02:073685 }
3686 }
Harald Alvestrandcdcfab02020-09-28 13:02:073687 }
3688 return RTCError::OK();
3689}
3690
Harald Alvestrandc06e3742020-10-01 10:23:333691bool SdpOfferAnswerHandler::ExpectSetLocalDescription(SdpType type) {
3692 PeerConnectionInterface::SignalingState state = signaling_state();
3693 if (type == SdpType::kOffer) {
3694 return (state == PeerConnectionInterface::kStable) ||
3695 (state == PeerConnectionInterface::kHaveLocalOffer);
3696 } else {
3697 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3698 return (state == PeerConnectionInterface::kHaveRemoteOffer) ||
3699 (state == PeerConnectionInterface::kHaveLocalPrAnswer);
3700 }
3701}
3702
3703bool SdpOfferAnswerHandler::ExpectSetRemoteDescription(SdpType type) {
3704 PeerConnectionInterface::SignalingState state = signaling_state();
3705 if (type == SdpType::kOffer) {
3706 return (state == PeerConnectionInterface::kStable) ||
3707 (state == PeerConnectionInterface::kHaveRemoteOffer);
3708 } else {
3709 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3710 return (state == PeerConnectionInterface::kHaveLocalOffer) ||
3711 (state == PeerConnectionInterface::kHaveRemotePrAnswer);
3712 }
3713}
3714
3715void SdpOfferAnswerHandler::FillInMissingRemoteMids(
3716 cricket::SessionDescription* new_remote_description) {
3717 RTC_DCHECK_RUN_ON(signaling_thread());
3718 RTC_DCHECK(new_remote_description);
3719 const cricket::ContentInfos no_infos;
3720 const cricket::ContentInfos& local_contents =
3721 (local_description() ? local_description()->description()->contents()
3722 : no_infos);
3723 const cricket::ContentInfos& remote_contents =
3724 (remote_description() ? remote_description()->description()->contents()
3725 : no_infos);
3726 for (size_t i = 0; i < new_remote_description->contents().size(); ++i) {
3727 cricket::ContentInfo& content = new_remote_description->contents()[i];
3728 if (!content.name.empty()) {
3729 continue;
3730 }
3731 std::string new_mid;
3732 absl::string_view source_explanation;
3733 if (IsUnifiedPlan()) {
3734 if (i < local_contents.size()) {
3735 new_mid = local_contents[i].name;
3736 source_explanation = "from the matching local media section";
3737 } else if (i < remote_contents.size()) {
3738 new_mid = remote_contents[i].name;
3739 source_explanation = "from the matching previous remote media section";
3740 } else {
Harald Alvestrandbc9ca252020-10-05 13:08:413741 new_mid = mid_generator_.GenerateString();
Harald Alvestrandc06e3742020-10-01 10:23:333742 source_explanation = "generated just now";
3743 }
3744 } else {
3745 new_mid = std::string(
3746 GetDefaultMidForPlanB(content.media_description()->type()));
3747 source_explanation = "to match pre-existing behavior";
3748 }
3749 RTC_DCHECK(!new_mid.empty());
3750 content.name = new_mid;
3751 new_remote_description->transport_infos()[i].content_name = new_mid;
3752 RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i
3753 << " is missing an a=mid line. Filling in the value '"
3754 << new_mid << "' " << source_explanation << ".";
3755 }
3756}
3757
3758rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
3759SdpOfferAnswerHandler::FindAvailableTransceiverToReceive(
3760 cricket::MediaType media_type) const {
3761 RTC_DCHECK_RUN_ON(signaling_thread());
3762 RTC_DCHECK(IsUnifiedPlan());
3763 // From JSEP section 5.10 (Applying a Remote Description):
3764 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of
3765 // the same type that were added to the PeerConnection by addTrack and are not
3766 // associated with any m= section and are not stopped, find the first such
3767 // RtpTransceiver.
Harald Alvestrande15fb152020-10-19 13:28:053768 for (auto transceiver : transceivers()->List()) {
Harald Alvestrandc06e3742020-10-01 10:23:333769 if (transceiver->media_type() == media_type &&
3770 transceiver->internal()->created_by_addtrack() && !transceiver->mid() &&
3771 !transceiver->stopped()) {
3772 return transceiver;
3773 }
3774 }
3775 return nullptr;
3776}
3777
3778const cricket::ContentInfo*
3779SdpOfferAnswerHandler::FindMediaSectionForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:363780 const RtpTransceiver* transceiver,
Harald Alvestrandc06e3742020-10-01 10:23:333781 const SessionDescriptionInterface* sdesc) const {
3782 RTC_DCHECK_RUN_ON(signaling_thread());
3783 RTC_DCHECK(transceiver);
3784 RTC_DCHECK(sdesc);
3785 if (IsUnifiedPlan()) {
Harald Alvestrand85466662021-04-19 21:21:363786 if (!transceiver->mid()) {
Harald Alvestrandc06e3742020-10-01 10:23:333787 // This transceiver is not associated with a media section yet.
3788 return nullptr;
3789 }
Harald Alvestrand85466662021-04-19 21:21:363790 return sdesc->description()->GetContentByName(*transceiver->mid());
Harald Alvestrandc06e3742020-10-01 10:23:333791 } else {
3792 // Plan B only allows at most one audio and one video section, so use the
3793 // first media section of that type.
3794 return cricket::GetFirstMediaContent(sdesc->description()->contents(),
3795 transceiver->media_type());
3796 }
3797}
3798
3799void SdpOfferAnswerHandler::GetOptionsForOffer(
3800 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
3801 cricket::MediaSessionOptions* session_options) {
3802 RTC_DCHECK_RUN_ON(signaling_thread());
3803 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
3804
3805 if (IsUnifiedPlan()) {
3806 GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options);
3807 } else {
3808 GetOptionsForPlanBOffer(offer_answer_options, session_options);
3809 }
3810
Harald Alvestrandc06e3742020-10-01 10:23:333811 // Apply ICE restart flag and renomination flag.
3812 bool ice_restart = offer_answer_options.ice_restart || HasNewIceCredentials();
3813 for (auto& options : session_options->media_description_options) {
3814 options.transport_options.ice_restart = ice_restart;
3815 options.transport_options.enable_ice_renomination =
3816 pc_->configuration()->enable_ice_renomination;
3817 }
3818
Harald Alvestrandbc9ca252020-10-05 13:08:413819 session_options->rtcp_cname = rtcp_cname_;
Harald Alvestrandc06e3742020-10-01 10:23:333820 session_options->crypto_options = pc_->GetCryptoOptions();
3821 session_options->pooled_ice_credentials =
Danil Chapovalov9e09a1f2022-09-08 16:38:103822 context_->network_thread()->BlockingCall(
Niels Möller4bab23f2021-01-18 08:24:333823 [this] { return port_allocator()->GetPooledIceCredentials(); });
Harald Alvestrandc06e3742020-10-01 10:23:333824 session_options->offer_extmap_allow_mixed =
3825 pc_->configuration()->offer_extmap_allow_mixed;
3826
3827 // Allow fallback for using obsolete SCTP syntax.
Artem Titov880fa812021-07-30 20:30:233828 // Note that the default in `session_options` is true, while
3829 // the default in `options` is false.
Harald Alvestrandc06e3742020-10-01 10:23:333830 session_options->use_obsolete_sctp_sdp =
3831 offer_answer_options.use_obsolete_sctp_sdp;
3832}
3833
3834void SdpOfferAnswerHandler::GetOptionsForPlanBOffer(
3835 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
3836 cricket::MediaSessionOptions* session_options) {
Harald Alvestrand8101e7b2022-05-23 14:57:473837 bool offer_new_data_description =
3838 data_channel_controller()->HasDataChannels();
3839 bool send_audio = false;
3840 bool send_video = false;
3841 bool recv_audio = false;
3842 bool recv_video = false;
3843 if (ConfiguredForMedia()) {
3844 // Figure out transceiver directional preferences.
3845 send_audio =
3846 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
3847 send_video =
3848 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
Harald Alvestrandc06e3742020-10-01 10:23:333849
Harald Alvestrand8101e7b2022-05-23 14:57:473850 // By default, generate sendrecv/recvonly m= sections.
3851 recv_audio = true;
3852 recv_video = true;
3853 }
Harald Alvestrandc06e3742020-10-01 10:23:333854 // By default, only offer a new m= section if we have media to send with it.
3855 bool offer_new_audio_description = send_audio;
3856 bool offer_new_video_description = send_video;
Harald Alvestrand8101e7b2022-05-23 14:57:473857 if (ConfiguredForMedia()) {
3858 // The "offer_to_receive_X" options allow those defaults to be overridden.
3859 if (offer_answer_options.offer_to_receive_audio !=
3860 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
3861 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
3862 offer_new_audio_description =
3863 offer_new_audio_description ||
3864 (offer_answer_options.offer_to_receive_audio > 0);
3865 }
3866 if (offer_answer_options.offer_to_receive_video !=
3867 RTCOfferAnswerOptions::kUndefined) {
3868 recv_video = (offer_answer_options.offer_to_receive_video > 0);
3869 offer_new_video_description =
3870 offer_new_video_description ||
3871 (offer_answer_options.offer_to_receive_video > 0);
3872 }
Harald Alvestrandc06e3742020-10-01 10:23:333873 }
Harald Alvestrandc06e3742020-10-01 10:23:333874 absl::optional<size_t> audio_index;
3875 absl::optional<size_t> video_index;
3876 absl::optional<size_t> data_index;
3877 // If a current description exists, generate m= sections in the same order,
3878 // using the first audio/video/data section that appears and rejecting
3879 // extraneous ones.
3880 if (local_description()) {
Harald Alvestrandbc9ca252020-10-05 13:08:413881 GenerateMediaDescriptionOptions(
Harald Alvestrandc06e3742020-10-01 10:23:333882 local_description(),
3883 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
3884 RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
3885 &audio_index, &video_index, &data_index, session_options);
3886 }
3887
Harald Alvestrand8101e7b2022-05-23 14:57:473888 if (ConfiguredForMedia()) {
3889 // Add audio/video/data m= sections to the end if needed.
3890 if (!audio_index && offer_new_audio_description) {
3891 cricket::MediaDescriptionOptions options(
3892 cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
3893 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false);
3894 options.header_extensions =
3895 media_engine()->voice().GetRtpHeaderExtensions();
3896 session_options->media_description_options.push_back(options);
3897 audio_index = session_options->media_description_options.size() - 1;
3898 }
3899 if (!video_index && offer_new_video_description) {
3900 cricket::MediaDescriptionOptions options(
3901 cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
3902 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false);
3903 options.header_extensions =
3904 media_engine()->video().GetRtpHeaderExtensions();
3905 session_options->media_description_options.push_back(options);
3906 video_index = session_options->media_description_options.size() - 1;
3907 }
3908 cricket::MediaDescriptionOptions* audio_media_description_options =
3909 !audio_index
3910 ? nullptr
3911 : &session_options->media_description_options[*audio_index];
3912 cricket::MediaDescriptionOptions* video_media_description_options =
3913 !video_index
3914 ? nullptr
3915 : &session_options->media_description_options[*video_index];
3916
3917 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
3918 audio_media_description_options,
3919 video_media_description_options,
3920 offer_answer_options.num_simulcast_layers);
Harald Alvestrandc06e3742020-10-01 10:23:333921 }
3922 if (!data_index && offer_new_data_description) {
3923 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:413924 GetMediaDescriptionOptionsForActiveData(cricket::CN_DATA));
Harald Alvestrandc06e3742020-10-01 10:23:333925 }
Harald Alvestrandc06e3742020-10-01 10:23:333926}
3927
3928void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanOffer(
3929 const RTCOfferAnswerOptions& offer_answer_options,
3930 cricket::MediaSessionOptions* session_options) {
3931 // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial
3932 // Offers) and 5.2.2 (Subsequent Offers).
3933 RTC_DCHECK_EQ(session_options->media_description_options.size(), 0);
3934 const ContentInfos no_infos;
3935 const ContentInfos& local_contents =
3936 (local_description() ? local_description()->description()->contents()
3937 : no_infos);
3938 const ContentInfos& remote_contents =
3939 (remote_description() ? remote_description()->description()->contents()
3940 : no_infos);
3941 // The mline indices that can be recycled. New transceivers should reuse these
3942 // slots first.
3943 std::queue<size_t> recycleable_mline_indices;
3944 // First, go through each media section that exists in either the local or
3945 // remote description and generate a media section in this offer for the
3946 // associated transceiver. If a media section can be recycled, generate a
3947 // default, rejected media section here that can be later overwritten.
3948 for (size_t i = 0;
3949 i < std::max(local_contents.size(), remote_contents.size()); ++i) {
Artem Titov880fa812021-07-30 20:30:233950 // Either `local_content` or `remote_content` is non-null.
Harald Alvestrandc06e3742020-10-01 10:23:333951 const ContentInfo* local_content =
3952 (i < local_contents.size() ? &local_contents[i] : nullptr);
3953 const ContentInfo* current_local_content =
3954 GetContentByIndex(current_local_description(), i);
3955 const ContentInfo* remote_content =
3956 (i < remote_contents.size() ? &remote_contents[i] : nullptr);
3957 const ContentInfo* current_remote_content =
3958 GetContentByIndex(current_remote_description(), i);
3959 bool had_been_rejected =
3960 (current_local_content && current_local_content->rejected) ||
3961 (current_remote_content && current_remote_content->rejected);
3962 const std::string& mid =
3963 (local_content ? local_content->name : remote_content->name);
3964 cricket::MediaType media_type =
3965 (local_content ? local_content->media_description()->type()
3966 : remote_content->media_description()->type());
3967 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
3968 media_type == cricket::MEDIA_TYPE_VIDEO) {
3969 // A media section is considered eligible for recycling if it is marked as
3970 // rejected in either the current local or current remote description.
Harald Alvestrande15fb152020-10-19 13:28:053971 auto transceiver = transceivers()->FindByMid(mid);
Harald Alvestrandc06e3742020-10-01 10:23:333972 if (!transceiver) {
3973 // No associated transceiver. The media section has been stopped.
3974 recycleable_mline_indices.push(i);
3975 session_options->media_description_options.push_back(
3976 cricket::MediaDescriptionOptions(media_type, mid,
3977 RtpTransceiverDirection::kInactive,
3978 /*stopped=*/true));
3979 } else {
3980 // NOTE: a stopping transceiver should be treated as a stopped one in
3981 // createOffer as specified in
3982 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
3983 if (had_been_rejected && transceiver->stopping()) {
3984 session_options->media_description_options.push_back(
3985 cricket::MediaDescriptionOptions(
3986 transceiver->media_type(), mid,
3987 RtpTransceiverDirection::kInactive,
3988 /*stopped=*/true));
3989 recycleable_mline_indices.push(i);
3990 } else {
3991 session_options->media_description_options.push_back(
3992 GetMediaDescriptionOptionsForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:363993 transceiver->internal(), mid,
Harald Alvestrandc06e3742020-10-01 10:23:333994 /*is_create_offer=*/true));
3995 // CreateOffer shouldn't really cause any state changes in
3996 // PeerConnection, but we need a way to match new transceivers to new
3997 // media sections in SetLocalDescription and JSEP specifies this is
3998 // done by recording the index of the media section generated for the
3999 // transceiver in the offer.
4000 transceiver->internal()->set_mline_index(i);
4001 }
4002 }
Philipp Hancke4e8c1152020-10-13 10:43:154003 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4004 RTC_DCHECK(local_content->rejected);
4005 session_options->media_description_options.push_back(
4006 cricket::MediaDescriptionOptions(media_type, mid,
4007 RtpTransceiverDirection::kInactive,
4008 /*stopped=*/true));
Harald Alvestrandc06e3742020-10-01 10:23:334009 } else {
4010 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4011 if (had_been_rejected) {
4012 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414013 GetMediaDescriptionOptionsForRejectedData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:334014 } else {
4015 RTC_CHECK(pc_->GetDataMid());
4016 if (mid == *(pc_->GetDataMid())) {
4017 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414018 GetMediaDescriptionOptionsForActiveData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:334019 } else {
4020 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414021 GetMediaDescriptionOptionsForRejectedData(mid));
Harald Alvestrandc06e3742020-10-01 10:23:334022 }
4023 }
4024 }
4025 }
4026
4027 // Next, look for transceivers that are newly added (that is, are not stopped
4028 // and not associated). Reuse media sections marked as recyclable first,
4029 // otherwise append to the end of the offer. New media sections should be
4030 // added in the order they were added to the PeerConnection.
Harald Alvestrand8101e7b2022-05-23 14:57:474031 if (ConfiguredForMedia()) {
4032 for (const auto& transceiver : transceivers()->ListInternal()) {
4033 if (transceiver->mid() || transceiver->stopping()) {
4034 continue;
4035 }
4036 size_t mline_index;
4037 if (!recycleable_mline_indices.empty()) {
4038 mline_index = recycleable_mline_indices.front();
4039 recycleable_mline_indices.pop();
4040 session_options->media_description_options[mline_index] =
4041 GetMediaDescriptionOptionsForTransceiver(
4042 transceiver, mid_generator_.GenerateString(),
4043 /*is_create_offer=*/true);
4044 } else {
4045 mline_index = session_options->media_description_options.size();
4046 session_options->media_description_options.push_back(
4047 GetMediaDescriptionOptionsForTransceiver(
4048 transceiver, mid_generator_.GenerateString(),
4049 /*is_create_offer=*/true));
4050 }
4051 // See comment above for why CreateOffer changes the transceiver's state.
4052 transceiver->set_mline_index(mline_index);
Harald Alvestrandc06e3742020-10-01 10:23:334053 }
Harald Alvestrandc06e3742020-10-01 10:23:334054 }
4055 // Lastly, add a m-section if we have local data channels and an m section
4056 // does not already exist.
Harald Alvestrandbc9ca252020-10-05 13:08:414057 if (!pc_->GetDataMid() && data_channel_controller()->HasDataChannels()) {
Harald Alvestrandc06e3742020-10-01 10:23:334058 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414059 GetMediaDescriptionOptionsForActiveData(
4060 mid_generator_.GenerateString()));
Harald Alvestrandc06e3742020-10-01 10:23:334061 }
4062}
4063
4064void SdpOfferAnswerHandler::GetOptionsForAnswer(
4065 const RTCOfferAnswerOptions& offer_answer_options,
4066 cricket::MediaSessionOptions* session_options) {
4067 RTC_DCHECK_RUN_ON(signaling_thread());
4068 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
4069
4070 if (IsUnifiedPlan()) {
4071 GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options);
4072 } else {
4073 GetOptionsForPlanBAnswer(offer_answer_options, session_options);
4074 }
4075
Harald Alvestrandc06e3742020-10-01 10:23:334076 // Apply ICE renomination flag.
4077 for (auto& options : session_options->media_description_options) {
4078 options.transport_options.enable_ice_renomination =
4079 pc_->configuration()->enable_ice_renomination;
4080 }
4081
Harald Alvestrandbc9ca252020-10-05 13:08:414082 session_options->rtcp_cname = rtcp_cname_;
Harald Alvestrandc06e3742020-10-01 10:23:334083 session_options->crypto_options = pc_->GetCryptoOptions();
4084 session_options->pooled_ice_credentials =
Danil Chapovalov9e09a1f2022-09-08 16:38:104085 context_->network_thread()->BlockingCall(
Niels Möller4bab23f2021-01-18 08:24:334086 [this] { return port_allocator()->GetPooledIceCredentials(); });
Harald Alvestrandc06e3742020-10-01 10:23:334087}
4088
4089void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer(
4090 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4091 cricket::MediaSessionOptions* session_options) {
Harald Alvestrand8101e7b2022-05-23 14:57:474092 bool send_audio = false;
4093 bool recv_audio = false;
4094 bool send_video = false;
4095 bool recv_video = false;
Harald Alvestrandc06e3742020-10-01 10:23:334096
Harald Alvestrand8101e7b2022-05-23 14:57:474097 if (ConfiguredForMedia()) {
4098 // Figure out transceiver directional preferences.
4099 send_audio =
4100 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
4101 send_video =
4102 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
Harald Alvestrandc06e3742020-10-01 10:23:334103
Harald Alvestrand8101e7b2022-05-23 14:57:474104 // By default, generate sendrecv/recvonly m= sections. The direction is also
4105 // restricted by the direction in the offer.
4106 recv_audio = true;
4107 recv_video = true;
4108
4109 // The "offer_to_receive_X" options allow those defaults to be overridden.
4110 if (offer_answer_options.offer_to_receive_audio !=
4111 RTCOfferAnswerOptions::kUndefined) {
4112 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
4113 }
4114 if (offer_answer_options.offer_to_receive_video !=
4115 RTCOfferAnswerOptions::kUndefined) {
4116 recv_video = (offer_answer_options.offer_to_receive_video > 0);
4117 }
Harald Alvestrandc06e3742020-10-01 10:23:334118 }
4119
4120 absl::optional<size_t> audio_index;
4121 absl::optional<size_t> video_index;
4122 absl::optional<size_t> data_index;
4123
4124 // Generate m= sections that match those in the offer.
4125 // Note that mediasession.cc will handle intersection our preferred
4126 // direction with the offered direction.
Harald Alvestrandbc9ca252020-10-05 13:08:414127 GenerateMediaDescriptionOptions(
Harald Alvestrandc06e3742020-10-01 10:23:334128 remote_description(),
4129 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
4130 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index,
4131 &video_index, &data_index, session_options);
4132
4133 cricket::MediaDescriptionOptions* audio_media_description_options =
4134 !audio_index ? nullptr
4135 : &session_options->media_description_options[*audio_index];
4136 cricket::MediaDescriptionOptions* video_media_description_options =
4137 !video_index ? nullptr
4138 : &session_options->media_description_options[*video_index];
4139
Harald Alvestrand8101e7b2022-05-23 14:57:474140 if (ConfiguredForMedia()) {
4141 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
4142 audio_media_description_options,
4143 video_media_description_options,
4144 offer_answer_options.num_simulcast_layers);
4145 }
Harald Alvestrandc06e3742020-10-01 10:23:334146}
4147
4148void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanAnswer(
4149 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4150 cricket::MediaSessionOptions* session_options) {
4151 // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial
4152 // Answers) and 5.3.2 (Subsequent Answers).
4153 RTC_DCHECK(remote_description());
4154 RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer);
4155 for (const ContentInfo& content :
4156 remote_description()->description()->contents()) {
4157 cricket::MediaType media_type = content.media_description()->type();
4158 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
4159 media_type == cricket::MEDIA_TYPE_VIDEO) {
Harald Alvestrande15fb152020-10-19 13:28:054160 auto transceiver = transceivers()->FindByMid(content.name);
Harald Alvestrandc06e3742020-10-01 10:23:334161 if (transceiver) {
4162 session_options->media_description_options.push_back(
4163 GetMediaDescriptionOptionsForTransceiver(
Harald Alvestrand85466662021-04-19 21:21:364164 transceiver->internal(), content.name,
Harald Alvestrandc06e3742020-10-01 10:23:334165 /*is_create_offer=*/false));
4166 } else {
4167 // This should only happen with rejected transceivers.
4168 RTC_DCHECK(content.rejected);
4169 session_options->media_description_options.push_back(
4170 cricket::MediaDescriptionOptions(media_type, content.name,
4171 RtpTransceiverDirection::kInactive,
4172 /*stopped=*/true));
4173 }
Philipp Hancke4e8c1152020-10-13 10:43:154174 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4175 RTC_DCHECK(content.rejected);
4176 session_options->media_description_options.push_back(
4177 cricket::MediaDescriptionOptions(media_type, content.name,
4178 RtpTransceiverDirection::kInactive,
4179 /*stopped=*/true));
Harald Alvestrandc06e3742020-10-01 10:23:334180 } else {
4181 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4182 // Reject all data sections if data channels are disabled.
4183 // Reject a data section if it has already been rejected.
4184 // Reject all data sections except for the first one.
Florent Castelli516e2842021-04-19 13:29:504185 if (content.rejected || content.name != *(pc_->GetDataMid())) {
Harald Alvestrandc06e3742020-10-01 10:23:334186 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414187 GetMediaDescriptionOptionsForRejectedData(content.name));
Harald Alvestrandc06e3742020-10-01 10:23:334188 } else {
4189 session_options->media_description_options.push_back(
Harald Alvestrandbc9ca252020-10-05 13:08:414190 GetMediaDescriptionOptionsForActiveData(content.name));
Harald Alvestrandc06e3742020-10-01 10:23:334191 }
4192 }
4193 }
4194}
4195
Harald Alvestranda474fbf2020-10-01 16:47:234196const char* SdpOfferAnswerHandler::SessionErrorToString(
4197 SessionError error) const {
4198 switch (error) {
4199 case SessionError::kNone:
4200 return "ERROR_NONE";
4201 case SessionError::kContent:
4202 return "ERROR_CONTENT";
4203 case SessionError::kTransport:
4204 return "ERROR_TRANSPORT";
4205 }
Artem Titovd3251962021-11-15 15:57:074206 RTC_DCHECK_NOTREACHED();
Harald Alvestranda474fbf2020-10-01 16:47:234207 return "";
4208}
4209
4210std::string SdpOfferAnswerHandler::GetSessionErrorMsg() {
4211 RTC_DCHECK_RUN_ON(signaling_thread());
4212 rtc::StringBuilder desc;
4213 desc << kSessionError << SessionErrorToString(session_error()) << ". ";
4214 desc << kSessionErrorDesc << session_error_desc() << ".";
4215 return desc.Release();
4216}
4217
4218void SdpOfferAnswerHandler::SetSessionError(SessionError error,
4219 const std::string& error_desc) {
4220 RTC_DCHECK_RUN_ON(signaling_thread());
4221 if (error != session_error_) {
4222 session_error_ = error;
4223 session_error_desc_ = error_desc;
4224 }
4225}
4226
4227RTCError SdpOfferAnswerHandler::HandleLegacyOfferOptions(
4228 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
4229 RTC_DCHECK_RUN_ON(signaling_thread());
4230 RTC_DCHECK(IsUnifiedPlan());
4231
4232 if (options.offer_to_receive_audio == 0) {
4233 RemoveRecvDirectionFromReceivingTransceiversOfType(
4234 cricket::MEDIA_TYPE_AUDIO);
4235 } else if (options.offer_to_receive_audio == 1) {
4236 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO);
4237 } else if (options.offer_to_receive_audio > 1) {
4238 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4239 "offer_to_receive_audio > 1 is not supported.");
4240 }
4241
4242 if (options.offer_to_receive_video == 0) {
4243 RemoveRecvDirectionFromReceivingTransceiversOfType(
4244 cricket::MEDIA_TYPE_VIDEO);
4245 } else if (options.offer_to_receive_video == 1) {
4246 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO);
4247 } else if (options.offer_to_receive_video > 1) {
4248 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4249 "offer_to_receive_video > 1 is not supported.");
4250 }
4251
4252 return RTCError::OK();
4253}
4254
4255void SdpOfferAnswerHandler::RemoveRecvDirectionFromReceivingTransceiversOfType(
4256 cricket::MediaType media_type) {
4257 for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) {
4258 RtpTransceiverDirection new_direction =
4259 RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false);
4260 if (new_direction != transceiver->direction()) {
4261 RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type)
4262 << " transceiver (MID="
4263 << transceiver->mid().value_or("<not set>") << ") from "
4264 << RtpTransceiverDirectionToString(
4265 transceiver->direction())
4266 << " to "
4267 << RtpTransceiverDirectionToString(new_direction)
4268 << " since CreateOffer specified offer_to_receive=0";
4269 transceiver->internal()->set_direction(new_direction);
4270 }
4271 }
4272}
4273
4274void SdpOfferAnswerHandler::AddUpToOneReceivingTransceiverOfType(
4275 cricket::MediaType media_type) {
4276 RTC_DCHECK_RUN_ON(signaling_thread());
4277 if (GetReceivingTransceiversOfType(media_type).empty()) {
4278 RTC_LOG(LS_INFO)
4279 << "Adding one recvonly " << cricket::MediaTypeToString(media_type)
4280 << " transceiver since CreateOffer specified offer_to_receive=1";
4281 RtpTransceiverInit init;
4282 init.direction = RtpTransceiverDirection::kRecvOnly;
4283 pc_->AddTransceiver(media_type, nullptr, init,
4284 /*update_negotiation_needed=*/false);
4285 }
4286}
4287
4288std::vector<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
4289SdpOfferAnswerHandler::GetReceivingTransceiversOfType(
4290 cricket::MediaType media_type) {
4291 std::vector<
4292 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
4293 receiving_transceivers;
Harald Alvestrande15fb152020-10-19 13:28:054294 for (const auto& transceiver : transceivers()->List()) {
Harald Alvestranda474fbf2020-10-01 16:47:234295 if (!transceiver->stopped() && transceiver->media_type() == media_type &&
4296 RtpTransceiverDirectionHasRecv(transceiver->direction())) {
4297 receiving_transceivers.push_back(transceiver);
4298 }
4299 }
4300 return receiving_transceivers;
4301}
4302
4303void SdpOfferAnswerHandler::ProcessRemovalOfRemoteTrack(
4304 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
4305 transceiver,
4306 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>* remove_list,
4307 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4308 RTC_DCHECK(transceiver->mid());
4309 RTC_LOG(LS_INFO) << "Processing the removal of a track for MID="
4310 << *transceiver->mid();
4311 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
4312 transceiver->internal()->receiver_internal()->streams();
4313 // This will remove the remote track from the streams.
4314 transceiver->internal()->receiver_internal()->set_stream_ids({});
4315 remove_list->push_back(transceiver);
4316 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
4317}
4318
4319void SdpOfferAnswerHandler::RemoveRemoteStreamsIfEmpty(
4320 const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& remote_streams,
4321 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4322 RTC_DCHECK_RUN_ON(signaling_thread());
4323 // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of
4324 // streams, see if the stream was removed by checking if this was the last
4325 // receiver with that stream ID.
4326 for (const auto& remote_stream : remote_streams) {
4327 if (remote_stream->GetAudioTracks().empty() &&
4328 remote_stream->GetVideoTracks().empty()) {
Niels Möllerafb246b2022-04-20 12:26:504329 remote_streams_->RemoveStream(remote_stream.get());
Harald Alvestranda474fbf2020-10-01 16:47:234330 removed_streams->push_back(remote_stream);
4331 }
4332 }
4333}
4334
4335void SdpOfferAnswerHandler::RemoveSenders(cricket::MediaType media_type) {
4336 RTC_DCHECK_RUN_ON(signaling_thread());
4337 UpdateLocalSenders(std::vector<cricket::StreamParams>(), media_type);
4338 UpdateRemoteSendersList(std::vector<cricket::StreamParams>(), false,
4339 media_type, nullptr);
4340}
4341
4342void SdpOfferAnswerHandler::UpdateLocalSenders(
4343 const std::vector<cricket::StreamParams>& streams,
4344 cricket::MediaType media_type) {
Markus Handell518669d2021-06-07 11:30:464345 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateLocalSenders");
Harald Alvestranda474fbf2020-10-01 16:47:234346 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrande15fb152020-10-19 13:28:054347 std::vector<RtpSenderInfo>* current_senders =
4348 rtp_manager()->GetLocalSenderInfos(media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234349
4350 // Find removed tracks. I.e., tracks where the track id, stream id or ssrc
4351 // don't match the new StreamParam.
4352 for (auto sender_it = current_senders->begin();
4353 sender_it != current_senders->end();
4354 /* incremented manually */) {
Harald Alvestrande15fb152020-10-19 13:28:054355 const RtpSenderInfo& info = *sender_it;
Harald Alvestranda474fbf2020-10-01 16:47:234356 const cricket::StreamParams* params =
4357 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4358 if (!params || params->id != info.sender_id ||
4359 params->first_stream_id() != info.stream_id) {
Harald Alvestrande15fb152020-10-19 13:28:054360 rtp_manager()->OnLocalSenderRemoved(info, media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234361 sender_it = current_senders->erase(sender_it);
4362 } else {
4363 ++sender_it;
4364 }
4365 }
4366
4367 // Find new and active senders.
4368 for (const cricket::StreamParams& params : streams) {
Artem Titovcfea2182021-08-09 23:22:314369 // The sync_label is the MediaStream label and the `stream.id` is the
Harald Alvestranda474fbf2020-10-01 16:47:234370 // sender id.
4371 const std::string& stream_id = params.first_stream_id();
4372 const std::string& sender_id = params.id;
4373 uint32_t ssrc = params.first_ssrc();
Harald Alvestrande15fb152020-10-19 13:28:054374 const RtpSenderInfo* sender_info =
4375 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:234376 if (!sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:054377 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
4378 rtp_manager()->OnLocalSenderAdded(current_senders->back(), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234379 }
4380 }
4381}
4382
4383void SdpOfferAnswerHandler::UpdateRemoteSendersList(
4384 const cricket::StreamParamsVec& streams,
4385 bool default_sender_needed,
4386 cricket::MediaType media_type,
4387 StreamCollection* new_streams) {
Markus Handell518669d2021-06-07 11:30:464388 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateRemoteSendersList");
Harald Alvestranda474fbf2020-10-01 16:47:234389 RTC_DCHECK_RUN_ON(signaling_thread());
4390 RTC_DCHECK(!IsUnifiedPlan());
4391
Harald Alvestrande15fb152020-10-19 13:28:054392 std::vector<RtpSenderInfo>* current_senders =
4393 rtp_manager()->GetRemoteSenderInfos(media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234394
4395 // Find removed senders. I.e., senders where the sender id or ssrc don't match
4396 // the new StreamParam.
4397 for (auto sender_it = current_senders->begin();
4398 sender_it != current_senders->end();
4399 /* incremented manually */) {
Harald Alvestrande15fb152020-10-19 13:28:054400 const RtpSenderInfo& info = *sender_it;
Harald Alvestranda474fbf2020-10-01 16:47:234401 const cricket::StreamParams* params =
4402 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4403 std::string params_stream_id;
4404 if (params) {
4405 params_stream_id =
4406 (!params->first_stream_id().empty() ? params->first_stream_id()
4407 : kDefaultStreamId);
4408 }
4409 bool sender_exists = params && params->id == info.sender_id &&
4410 params_stream_id == info.stream_id;
4411 // If this is a default track, and we still need it, don't remove it.
4412 if ((info.stream_id == kDefaultStreamId && default_sender_needed) ||
4413 sender_exists) {
4414 ++sender_it;
4415 } else {
Harald Alvestrande15fb152020-10-19 13:28:054416 rtp_manager()->OnRemoteSenderRemoved(
4417 info, remote_streams_->find(info.stream_id), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234418 sender_it = current_senders->erase(sender_it);
4419 }
4420 }
4421
4422 // Find new and active senders.
4423 for (const cricket::StreamParams& params : streams) {
4424 if (!params.has_ssrcs()) {
4425 // The remote endpoint has streams, but didn't signal ssrcs. For an active
4426 // sender, this means it is coming from a Unified Plan endpoint,so we just
4427 // create a default.
4428 default_sender_needed = true;
4429 break;
4430 }
4431
Artem Titovcfea2182021-08-09 23:22:314432 // `params.id` is the sender id and the stream id uses the first of
4433 // `params.stream_ids`. The remote description could come from a Unified
Harald Alvestranda474fbf2020-10-01 16:47:234434 // Plan endpoint, with multiple or no stream_ids() signaled. Since this is
4435 // not supported in Plan B, we just take the first here and create the
4436 // default stream ID if none is specified.
4437 const std::string& stream_id =
4438 (!params.first_stream_id().empty() ? params.first_stream_id()
4439 : kDefaultStreamId);
4440 const std::string& sender_id = params.id;
4441 uint32_t ssrc = params.first_ssrc();
4442
Niels Möllere7cc8832022-01-04 14:20:034443 rtc::scoped_refptr<MediaStreamInterface> stream(
4444 remote_streams_->find(stream_id));
Harald Alvestranda474fbf2020-10-01 16:47:234445 if (!stream) {
4446 // This is a new MediaStream. Create a new remote MediaStream.
4447 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
4448 MediaStream::Create(stream_id));
Harald Alvestrand6f04b6532020-10-09 11:42:174449 remote_streams_->AddStream(stream);
Harald Alvestranda474fbf2020-10-01 16:47:234450 new_streams->AddStream(stream);
4451 }
4452
Harald Alvestrande15fb152020-10-19 13:28:054453 const RtpSenderInfo* sender_info =
4454 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:234455 if (!sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:054456 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
Niels Möllerafb246b2022-04-20 12:26:504457 rtp_manager()->OnRemoteSenderAdded(current_senders->back(), stream.get(),
Harald Alvestrande15fb152020-10-19 13:28:054458 media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234459 }
4460 }
4461
4462 // Add default sender if necessary.
4463 if (default_sender_needed) {
Niels Möllere7cc8832022-01-04 14:20:034464 rtc::scoped_refptr<MediaStreamInterface> default_stream(
4465 remote_streams_->find(kDefaultStreamId));
Harald Alvestranda474fbf2020-10-01 16:47:234466 if (!default_stream) {
4467 // Create the new default MediaStream.
4468 default_stream = MediaStreamProxy::Create(
4469 rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId));
Harald Alvestrand6f04b6532020-10-09 11:42:174470 remote_streams_->AddStream(default_stream);
Harald Alvestranda474fbf2020-10-01 16:47:234471 new_streams->AddStream(default_stream);
4472 }
4473 std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO)
4474 ? kDefaultAudioSenderId
4475 : kDefaultVideoSenderId;
Harald Alvestrande15fb152020-10-19 13:28:054476 const RtpSenderInfo* default_sender_info = rtp_manager()->FindSenderInfo(
4477 *current_senders, kDefaultStreamId, default_sender_id);
Harald Alvestranda474fbf2020-10-01 16:47:234478 if (!default_sender_info) {
Harald Alvestrande15fb152020-10-19 13:28:054479 current_senders->push_back(
4480 RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0));
4481 rtp_manager()->OnRemoteSenderAdded(current_senders->back(),
Niels Möllerafb246b2022-04-20 12:26:504482 default_stream.get(), media_type);
Harald Alvestranda474fbf2020-10-01 16:47:234483 }
4484 }
4485}
4486
Taylor Brandstetterd0acbd82021-01-25 21:44:554487void SdpOfferAnswerHandler::EnableSending() {
Markus Handell518669d2021-06-07 11:30:464488 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::EnableSending");
Taylor Brandstetterd0acbd82021-01-25 21:44:554489 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrand8101e7b2022-05-23 14:57:474490 if (!ConfiguredForMedia()) {
4491 return;
4492 }
Harald Alvestrand85466662021-04-19 21:21:364493 for (const auto& transceiver : transceivers()->ListInternal()) {
4494 cricket::ChannelInterface* channel = transceiver->channel();
Tommi1959f8f2021-04-26 08:20:194495 if (channel) {
Taylor Brandstetterd0acbd82021-01-25 21:44:554496 channel->Enable(true);
4497 }
4498 }
Taylor Brandstetterd0acbd82021-01-25 21:44:554499}
4500
Harald Alvestranda474fbf2020-10-01 16:47:234501RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
4502 SdpType type,
Henrik Boströmf8187e02021-04-26 19:04:264503 cricket::ContentSource source,
4504 const std::map<std::string, const cricket::ContentGroup*>&
4505 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:464506 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownMediaDescription");
Harald Alvestranda474fbf2020-10-01 16:47:234507 const SessionDescriptionInterface* sdesc =
4508 (source == cricket::CS_LOCAL ? local_description()
4509 : remote_description());
4510 RTC_DCHECK_RUN_ON(signaling_thread());
4511 RTC_DCHECK(sdesc);
4512
Harald Alvestrand8101e7b2022-05-23 14:57:474513 if (ConfiguredForMedia()) {
Danil Chapovalov9e09a1f2022-09-08 16:38:104514 // Note: This will perform a BlockingCall over to the worker thread, which
4515 // we'll also do in a loop below.
Harald Alvestrand8101e7b2022-05-23 14:57:474516 if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) {
4517 // Note that this is never expected to fail, since RtpDemuxer doesn't
4518 // return an error when changing payload type demux criteria, which is all
4519 // this does.
4520 return RTCError(RTCErrorType::INTERNAL_ERROR,
4521 "Failed to update payload type demuxing state.");
Taylor Brandstetterd0acbd82021-01-25 21:44:554522 }
Tommicc7a3682021-05-04 12:59:384523
Harald Alvestrand8101e7b2022-05-23 14:57:474524 // Push down the new SDP media section for each audio/video transceiver.
4525 auto rtp_transceivers = transceivers()->ListInternal();
4526 std::vector<
4527 std::pair<cricket::ChannelInterface*, const MediaContentDescription*>>
4528 channels;
4529 for (const auto& transceiver : rtp_transceivers) {
4530 const ContentInfo* content_info =
4531 FindMediaSectionForTransceiver(transceiver, sdesc);
4532 cricket::ChannelInterface* channel = transceiver->channel();
4533 if (!channel || !content_info || content_info->rejected) {
4534 continue;
4535 }
4536 const MediaContentDescription* content_desc =
4537 content_info->media_description();
4538 if (!content_desc) {
4539 continue;
4540 }
Tommicc7a3682021-05-04 12:59:384541
Harald Alvestrand8101e7b2022-05-23 14:57:474542 transceiver->OnNegotiationUpdate(type, content_desc);
4543 channels.push_back(std::make_pair(channel, content_desc));
4544 }
4545
4546 // This for-loop of invokes helps audio impairment during re-negotiations.
4547 // One of the causes is that downstairs decoder creation is synchronous at
4548 // the moment, and that a decoder is created for each codec listed in the
4549 // SDP.
4550 //
4551 // TODO(bugs.webrtc.org/12840): consider merging the invokes again after
4552 // these projects have shipped:
4553 // - bugs.webrtc.org/12462
4554 // - crbug.com/1157227
4555 // - crbug.com/1187289
4556 for (const auto& entry : channels) {
4557 std::string error;
4558 bool success =
Danil Chapovalov9e09a1f2022-09-08 16:38:104559 context_->worker_thread()->BlockingCall([&]() {
Harald Alvestrand8101e7b2022-05-23 14:57:474560 return (source == cricket::CS_LOCAL)
4561 ? entry.first->SetLocalContent(entry.second, type, error)
4562 : entry.first->SetRemoteContent(entry.second, type,
4563 error);
4564 });
4565 if (!success) {
4566 return RTCError(RTCErrorType::INVALID_PARAMETER, error);
4567 }
Harald Alvestranda474fbf2020-10-01 16:47:234568 }
4569 }
Harald Alvestranda474fbf2020-10-01 16:47:234570 // Need complete offer/answer with an SCTP m= section before starting SCTP,
4571 // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
4572 if (pc_->sctp_mid() && local_description() && remote_description()) {
Harald Alvestranda474fbf2020-10-01 16:47:234573 auto local_sctp_description = cricket::GetFirstSctpDataContentDescription(
4574 local_description()->description());
4575 auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription(
4576 remote_description()->description());
Tomas Gunnarsson92eebef2021-02-10 12:05:444577 if (local_sctp_description && remote_sctp_description) {
Harald Alvestranda474fbf2020-10-01 16:47:234578 int max_message_size;
4579 // A remote max message size of zero means "any size supported".
4580 // We configure the connection with our own max message size.
4581 if (remote_sctp_description->max_message_size() == 0) {
4582 max_message_size = local_sctp_description->max_message_size();
4583 } else {
4584 max_message_size =
4585 std::min(local_sctp_description->max_message_size(),
4586 remote_sctp_description->max_message_size());
4587 }
Tomas Gunnarsson92eebef2021-02-10 12:05:444588 pc_->StartSctpTransport(local_sctp_description->port(),
4589 remote_sctp_description->port(),
4590 max_message_size);
Harald Alvestranda474fbf2020-10-01 16:47:234591 }
4592 }
4593
4594 return RTCError::OK();
4595}
4596
4597RTCError SdpOfferAnswerHandler::PushdownTransportDescription(
4598 cricket::ContentSource source,
4599 SdpType type) {
Markus Handell518669d2021-06-07 11:30:464600 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownTransportDescription");
Harald Alvestranda474fbf2020-10-01 16:47:234601 RTC_DCHECK_RUN_ON(signaling_thread());
4602
4603 if (source == cricket::CS_LOCAL) {
4604 const SessionDescriptionInterface* sdesc = local_description();
4605 RTC_DCHECK(sdesc);
Harald Alvestrandbc32c562022-02-09 12:08:474606 return transport_controller_s()->SetLocalDescription(type,
4607 sdesc->description());
Harald Alvestranda474fbf2020-10-01 16:47:234608 } else {
4609 const SessionDescriptionInterface* sdesc = remote_description();
4610 RTC_DCHECK(sdesc);
Harald Alvestrandbc32c562022-02-09 12:08:474611 return transport_controller_s()->SetRemoteDescription(type,
4612 sdesc->description());
Harald Alvestranda474fbf2020-10-01 16:47:234613 }
4614}
4615
4616void SdpOfferAnswerHandler::RemoveStoppedTransceivers() {
Markus Handell518669d2021-06-07 11:30:464617 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveStoppedTransceivers");
Harald Alvestranda474fbf2020-10-01 16:47:234618 RTC_DCHECK_RUN_ON(signaling_thread());
4619 // 3.2.10.1: For each transceiver in the connection's set of transceivers
4620 // run the following steps:
4621 if (!IsUnifiedPlan())
4622 return;
Harald Alvestrand8101e7b2022-05-23 14:57:474623 if (!ConfiguredForMedia()) {
4624 return;
4625 }
Harald Alvestranda474fbf2020-10-01 16:47:234626 // Traverse a copy of the transceiver list.
Harald Alvestrande15fb152020-10-19 13:28:054627 auto transceiver_list = transceivers()->List();
Harald Alvestranda474fbf2020-10-01 16:47:234628 for (auto transceiver : transceiver_list) {
4629 // 3.2.10.1.1: If transceiver is stopped, associated with an m= section
4630 // and the associated m= section is rejected in
4631 // connection.[[CurrentLocalDescription]] or
4632 // connection.[[CurrentRemoteDescription]], remove the
4633 // transceiver from the connection's set of transceivers.
4634 if (!transceiver->stopped()) {
4635 continue;
4636 }
Harald Alvestrand85466662021-04-19 21:21:364637 const ContentInfo* local_content = FindMediaSectionForTransceiver(
4638 transceiver->internal(), local_description());
4639 const ContentInfo* remote_content = FindMediaSectionForTransceiver(
4640 transceiver->internal(), remote_description());
Harald Alvestranda474fbf2020-10-01 16:47:234641 if ((local_content && local_content->rejected) ||
4642 (remote_content && remote_content->rejected)) {
4643 RTC_LOG(LS_INFO) << "Dissociating transceiver"
Tomas Gunnarsson7fa8d462021-04-16 12:28:264644 " since the media section is being recycled.";
Harald Alvestranda474fbf2020-10-01 16:47:234645 transceiver->internal()->set_mid(absl::nullopt);
4646 transceiver->internal()->set_mline_index(absl::nullopt);
Tomas Gunnarsson7fa8d462021-04-16 12:28:264647 } else if (!local_content && !remote_content) {
Harald Alvestranda474fbf2020-10-01 16:47:234648 // TODO(bugs.webrtc.org/11973): Consider if this should be removed already
4649 // See https://github.com/w3c/webrtc-pc/issues/2576
4650 RTC_LOG(LS_INFO)
4651 << "Dropping stopped transceiver that was never associated";
Harald Alvestranda474fbf2020-10-01 16:47:234652 }
Tomas Gunnarsson7fa8d462021-04-16 12:28:264653 transceivers()->Remove(transceiver);
Harald Alvestranda474fbf2020-10-01 16:47:234654 }
4655}
4656
4657void SdpOfferAnswerHandler::RemoveUnusedChannels(
4658 const SessionDescription* desc) {
4659 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrand8101e7b2022-05-23 14:57:474660 if (ConfiguredForMedia()) {
4661 // Destroy video channel first since it may have a pointer to the
4662 // voice channel.
4663 const cricket::ContentInfo* video_info =
4664 cricket::GetFirstVideoContent(desc);
4665 if (!video_info || video_info->rejected) {
4666 rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel();
4667 }
Artem Titovc6c02ef2022-05-09 08:30:094668
Harald Alvestrand8101e7b2022-05-23 14:57:474669 const cricket::ContentInfo* audio_info =
4670 cricket::GetFirstAudioContent(desc);
4671 if (!audio_info || audio_info->rejected) {
4672 rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel();
4673 }
Artem Titovc6c02ef2022-05-09 08:30:094674 }
Harald Alvestranda474fbf2020-10-01 16:47:234675 const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc);
Florent Castellidcb9ffc2021-06-29 12:58:234676 if (!data_info) {
4677 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA,
4678 "No data channel section in the description.");
4679 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
4680 DestroyDataChannelTransport(error);
4681 } else if (data_info->rejected) {
4682 rtc::StringBuilder sb;
4683 sb << "Rejected data channel with mid=" << data_info->name << ".";
4684
4685 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
4686 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
4687 DestroyDataChannelTransport(error);
Harald Alvestranda474fbf2020-10-01 16:47:234688 }
4689}
4690
Harald Alvestrandbc9ca252020-10-05 13:08:414691void SdpOfferAnswerHandler::UpdateEndedRemoteMediaStreams() {
4692 RTC_DCHECK_RUN_ON(signaling_thread());
4693 std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_to_remove;
Harald Alvestrand6f04b6532020-10-09 11:42:174694 for (size_t i = 0; i < remote_streams_->count(); ++i) {
4695 MediaStreamInterface* stream = remote_streams_->at(i);
Harald Alvestrandbc9ca252020-10-05 13:08:414696 if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) {
Niels Möllere7cc8832022-01-04 14:20:034697 streams_to_remove.push_back(
4698 rtc::scoped_refptr<MediaStreamInterface>(stream));
Harald Alvestrandbc9ca252020-10-05 13:08:414699 }
4700 }
4701
4702 for (auto& stream : streams_to_remove) {
Niels Möllerafb246b2022-04-20 12:26:504703 remote_streams_->RemoveStream(stream.get());
Harald Alvestrandbc9ca252020-10-05 13:08:414704 pc_->Observer()->OnRemoveStream(std::move(stream));
4705 }
4706}
4707
Tomas Gunnarsson0dd75392022-01-17 18:19:564708bool SdpOfferAnswerHandler::UseCandidatesInRemoteDescription() {
Harald Alvestrandbc9ca252020-10-05 13:08:414709 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson0dd75392022-01-17 18:19:564710 auto* remote_desc = remote_description();
Harald Alvestrandbc9ca252020-10-05 13:08:414711 if (!remote_desc) {
4712 return true;
4713 }
4714 bool ret = true;
4715
4716 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
4717 const IceCandidateCollection* candidates = remote_desc->candidates(m);
4718 for (size_t n = 0; n < candidates->count(); ++n) {
4719 const IceCandidateInterface* candidate = candidates->at(n);
4720 bool valid = false;
4721 if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) {
4722 if (valid) {
4723 RTC_LOG(LS_INFO)
Tomas Gunnarsson0dd75392022-01-17 18:19:564724 << "UseCandidatesInRemoteDescription: Not ready to use "
Harald Alvestrandbc9ca252020-10-05 13:08:414725 "candidate.";
4726 }
4727 continue;
4728 }
4729 ret = UseCandidate(candidate);
4730 if (!ret) {
4731 break;
4732 }
4733 }
4734 }
4735 return ret;
4736}
4737
4738bool SdpOfferAnswerHandler::UseCandidate(
4739 const IceCandidateInterface* candidate) {
4740 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarsson8cb97062021-02-08 17:57:044741
4742 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
4743
Harald Alvestrandbc9ca252020-10-05 13:08:414744 RTCErrorOr<const cricket::ContentInfo*> result =
4745 FindContentInfo(remote_description(), candidate);
Tomas Gunnarsson8cb97062021-02-08 17:57:044746 if (!result.ok())
Harald Alvestrandbc9ca252020-10-05 13:08:414747 return false;
Tomas Gunnarsson8cb97062021-02-08 17:57:044748
4749 const cricket::Candidate& c = candidate->candidate();
4750 RTCError error = cricket::VerifyCandidate(c);
Tomas Gunnarsson27bc6e22021-02-12 12:16:264751 if (!error.ok()) {
4752 RTC_LOG(LS_WARNING) << "Invalid candidate: " << c.ToString();
4753 return true;
4754 }
Tomas Gunnarsson8cb97062021-02-08 17:57:044755
4756 pc_->AddRemoteCandidate(result.value()->name, c);
4757
Harald Alvestrandbc9ca252020-10-05 13:08:414758 return true;
4759}
4760
4761// We need to check the local/remote description for the Transport instead of
4762// the session, because a new Transport added during renegotiation may have
4763// them unset while the session has them set from the previous negotiation.
4764// Not doing so may trigger the auto generation of transport description and
4765// mess up DTLS identity information, ICE credential, etc.
4766bool SdpOfferAnswerHandler::ReadyToUseRemoteCandidate(
4767 const IceCandidateInterface* candidate,
4768 const SessionDescriptionInterface* remote_desc,
4769 bool* valid) {
4770 RTC_DCHECK_RUN_ON(signaling_thread());
4771 *valid = true;
4772
4773 const SessionDescriptionInterface* current_remote_desc =
4774 remote_desc ? remote_desc : remote_description();
4775
4776 if (!current_remote_desc) {
4777 return false;
4778 }
4779
4780 RTCErrorOr<const cricket::ContentInfo*> result =
4781 FindContentInfo(current_remote_desc, candidate);
4782 if (!result.ok()) {
4783 RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. "
4784 << result.error().message();
4785
4786 *valid = false;
4787 return false;
4788 }
4789
Tomas Gunnarsson45de8b32021-04-02 10:33:394790 return true;
Harald Alvestrandbc9ca252020-10-05 13:08:414791}
4792
Harald Alvestrandbc9ca252020-10-05 13:08:414793RTCErrorOr<const cricket::ContentInfo*> SdpOfferAnswerHandler::FindContentInfo(
4794 const SessionDescriptionInterface* description,
4795 const IceCandidateInterface* candidate) {
Philipp Hancke31e06cb2021-02-26 08:23:534796 if (!candidate->sdp_mid().empty()) {
Harald Alvestrandbc9ca252020-10-05 13:08:414797 auto& contents = description->description()->contents();
4798 auto it = absl::c_find_if(
4799 contents, [candidate](const cricket::ContentInfo& content_info) {
4800 return content_info.mid() == candidate->sdp_mid();
4801 });
4802 if (it == contents.end()) {
4803 return RTCError(
4804 RTCErrorType::INVALID_PARAMETER,
4805 "Mid " + candidate->sdp_mid() +
4806 " specified but no media section with that mid found.");
4807 } else {
4808 return &*it;
4809 }
Philipp Hancke31e06cb2021-02-26 08:23:534810 } else if (candidate->sdp_mline_index() >= 0) {
4811 size_t mediacontent_index =
4812 static_cast<size_t>(candidate->sdp_mline_index());
4813 size_t content_size = description->description()->contents().size();
4814 if (mediacontent_index < content_size) {
4815 return &description->description()->contents()[mediacontent_index];
4816 } else {
4817 return RTCError(RTCErrorType::INVALID_RANGE,
4818 "Media line index (" +
4819 rtc::ToString(candidate->sdp_mline_index()) +
4820 ") out of range (number of mlines: " +
4821 rtc::ToString(content_size) + ").");
4822 }
Harald Alvestrandbc9ca252020-10-05 13:08:414823 }
4824
4825 return RTCError(RTCErrorType::INVALID_PARAMETER,
4826 "Neither sdp_mline_index nor sdp_mid specified.");
4827}
4828
4829RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) {
Markus Handell518669d2021-06-07 11:30:464830 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateChannels");
Harald Alvestrandbc9ca252020-10-05 13:08:414831 // Creating the media channels. Transports should already have been created
4832 // at this point.
4833 RTC_DCHECK_RUN_ON(signaling_thread());
4834 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc);
4835 if (voice && !voice->rejected &&
Harald Alvestrande15fb152020-10-19 13:28:054836 !rtp_manager()->GetAudioTransceiver()->internal()->channel()) {
Harald Alvestrand8f429922022-05-04 10:32:304837 auto error =
4838 rtp_manager()->GetAudioTransceiver()->internal()->CreateChannel(
4839 voice->name, pc_->call_ptr(), pc_->configuration()->media_config,
4840 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
4841 video_options(), video_bitrate_allocator_factory_.get(),
4842 [&](absl::string_view mid) {
4843 RTC_DCHECK_RUN_ON(network_thread());
4844 return transport_controller_n()->GetRtpTransport(mid);
4845 });
4846 if (!error.ok()) {
4847 return error;
Harald Alvestrandbc9ca252020-10-05 13:08:414848 }
Harald Alvestrandbc9ca252020-10-05 13:08:414849 }
4850
4851 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc);
4852 if (video && !video->rejected &&
Harald Alvestrande15fb152020-10-19 13:28:054853 !rtp_manager()->GetVideoTransceiver()->internal()->channel()) {
Harald Alvestrand8f429922022-05-04 10:32:304854 auto error =
4855 rtp_manager()->GetVideoTransceiver()->internal()->CreateChannel(
4856 video->name, pc_->call_ptr(), pc_->configuration()->media_config,
4857 pc_->SrtpRequired(), pc_->GetCryptoOptions(),
4858
4859 audio_options(), video_options(),
4860 video_bitrate_allocator_factory_.get(), [&](absl::string_view mid) {
4861 RTC_DCHECK_RUN_ON(network_thread());
4862 return transport_controller_n()->GetRtpTransport(mid);
4863 });
4864 if (!error.ok()) {
4865 return error;
Harald Alvestrandbc9ca252020-10-05 13:08:414866 }
Harald Alvestrandbc9ca252020-10-05 13:08:414867 }
4868
4869 const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc);
Florent Castelli516e2842021-04-19 13:29:504870 if (data && !data->rejected &&
4871 !data_channel_controller()->data_channel_transport()) {
Harald Alvestrandbc9ca252020-10-05 13:08:414872 if (!CreateDataChannel(data->name)) {
Tomas Gunnarsson0dd75392022-01-17 18:19:564873 return RTCError(RTCErrorType::INTERNAL_ERROR,
4874 "Failed to create data channel.");
Harald Alvestrandbc9ca252020-10-05 13:08:414875 }
4876 }
4877
4878 return RTCError::OK();
4879}
4880
Harald Alvestrandbc9ca252020-10-05 13:08:414881bool SdpOfferAnswerHandler::CreateDataChannel(const std::string& mid) {
4882 RTC_DCHECK_RUN_ON(signaling_thread());
Danil Chapovalov9e09a1f2022-09-08 16:38:104883 if (!context_->network_thread()->BlockingCall([this, &mid] {
Harald Alvestrand66c40362022-01-28 17:41:304884 RTC_DCHECK_RUN_ON(context_->network_thread());
Florent Castelli516e2842021-04-19 13:29:504885 return pc_->SetupDataChannelTransport_n(mid);
4886 })) {
4887 return false;
Harald Alvestrandbc9ca252020-10-05 13:08:414888 }
Florent Castelli516e2842021-04-19 13:29:504889 // TODO(tommi): Is this necessary? SetupDataChannelTransport_n() above
4890 // will have queued up updating the transport name on the signaling thread
4891 // and could update the mid at the same time. This here is synchronous
4892 // though, but it changes the state of PeerConnection and makes it be
4893 // out of sync (transport name not set while the mid is set).
4894 pc_->SetSctpDataMid(mid);
Tomas Gunnarsson00f4fd92021-04-08 12:39:474895 return true;
Harald Alvestrandbc9ca252020-10-05 13:08:414896}
4897
Florent Castellidcb9ffc2021-06-29 12:58:234898void SdpOfferAnswerHandler::DestroyDataChannelTransport(RTCError error) {
Harald Alvestrandbc9ca252020-10-05 13:08:414899 RTC_DCHECK_RUN_ON(signaling_thread());
Tomas Gunnarssond69e0702021-04-07 13:14:434900 const bool has_sctp = pc_->sctp_mid().has_value();
Harald Alvestrandbc9ca252020-10-05 13:08:414901
Harald Alvestrand7af57c62021-04-16 11:12:144902 if (has_sctp)
Florent Castellidcb9ffc2021-06-29 12:58:234903 data_channel_controller()->OnTransportChannelClosed(error);
Tomas Gunnarssond69e0702021-04-07 13:14:434904
Danil Chapovalov9e09a1f2022-09-08 16:38:104905 context_->network_thread()->BlockingCall([this] {
Harald Alvestrand66c40362022-01-28 17:41:304906 RTC_DCHECK_RUN_ON(context_->network_thread());
Tomas Gunnarssond69e0702021-04-07 13:14:434907 pc_->TeardownDataChannelTransport_n();
4908 });
4909
4910 if (has_sctp)
Harald Alvestrand653429c2020-10-19 16:05:204911 pc_->ResetSctpDataMid();
Harald Alvestrandbc9ca252020-10-05 13:08:414912}
4913
Harald Alvestrandbc9ca252020-10-05 13:08:414914void SdpOfferAnswerHandler::DestroyAllChannels() {
4915 RTC_DCHECK_RUN_ON(signaling_thread());
Harald Alvestrande15fb152020-10-19 13:28:054916 if (!transceivers()) {
4917 return;
4918 }
Tommife041642021-04-07 08:08:284919
4920 RTC_LOG_THREAD_BLOCK_COUNT();
4921
Harald Alvestrandbc9ca252020-10-05 13:08:414922 // Destroy video channels first since they may have a pointer to a voice
4923 // channel.
Tommife041642021-04-07 08:08:284924 auto list = transceivers()->List();
4925 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
4926
4927 for (const auto& transceiver : list) {
Harald Alvestrandbc9ca252020-10-05 13:08:414928 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Harald Alvestrand19ebabc2022-04-28 13:31:174929 transceiver->internal()->ClearChannel();
Harald Alvestrandbc9ca252020-10-05 13:08:414930 }
4931 }
Tommife041642021-04-07 08:08:284932 for (const auto& transceiver : list) {
Harald Alvestrandbc9ca252020-10-05 13:08:414933 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Harald Alvestrand19ebabc2022-04-28 13:31:174934 transceiver->internal()->ClearChannel();
Harald Alvestrandbc9ca252020-10-05 13:08:414935 }
4936 }
Tommife041642021-04-07 08:08:284937
Florent Castellidcb9ffc2021-06-29 12:58:234938 DestroyDataChannelTransport({});
Harald Alvestrandbc9ca252020-10-05 13:08:414939}
4940
4941void SdpOfferAnswerHandler::GenerateMediaDescriptionOptions(
4942 const SessionDescriptionInterface* session_desc,
4943 RtpTransceiverDirection audio_direction,
4944 RtpTransceiverDirection video_direction,
4945 absl::optional<size_t>* audio_index,
4946 absl::optional<size_t>* video_index,
4947 absl::optional<size_t>* data_index,
4948 cricket::MediaSessionOptions* session_options) {
4949 RTC_DCHECK_RUN_ON(signaling_thread());
4950 for (const cricket::ContentInfo& content :
4951 session_desc->description()->contents()) {
4952 if (IsAudioContent(&content)) {
4953 // If we already have an audio m= section, reject this extra one.
4954 if (*audio_index) {
4955 session_options->media_description_options.push_back(
4956 cricket::MediaDescriptionOptions(
4957 cricket::MEDIA_TYPE_AUDIO, content.name,
4958 RtpTransceiverDirection::kInactive, /*stopped=*/true));
4959 } else {
4960 bool stopped = (audio_direction == RtpTransceiverDirection::kInactive);
4961 session_options->media_description_options.push_back(
4962 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO,
4963 content.name, audio_direction,
4964 stopped));
4965 *audio_index = session_options->media_description_options.size() - 1;
4966 }
4967 session_options->media_description_options.back().header_extensions =
Harald Alvestrand35f4b4c2022-05-16 10:36:434968 media_engine()->voice().GetRtpHeaderExtensions();
Harald Alvestrandbc9ca252020-10-05 13:08:414969 } else if (IsVideoContent(&content)) {
4970 // If we already have an video m= section, reject this extra one.
4971 if (*video_index) {
4972 session_options->media_description_options.push_back(
4973 cricket::MediaDescriptionOptions(
4974 cricket::MEDIA_TYPE_VIDEO, content.name,
4975 RtpTransceiverDirection::kInactive, /*stopped=*/true));
4976 } else {
4977 bool stopped = (video_direction == RtpTransceiverDirection::kInactive);
4978 session_options->media_description_options.push_back(
4979 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO,
4980 content.name, video_direction,
4981 stopped));
4982 *video_index = session_options->media_description_options.size() - 1;
4983 }
4984 session_options->media_description_options.back().header_extensions =
Harald Alvestrand35f4b4c2022-05-16 10:36:434985 media_engine()->video().GetRtpHeaderExtensions();
Philipp Hancke4e8c1152020-10-13 10:43:154986 } else if (IsUnsupportedContent(&content)) {
4987 session_options->media_description_options.push_back(
4988 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_UNSUPPORTED,
4989 content.name,
4990 RtpTransceiverDirection::kInactive,
4991 /*stopped=*/true));
Harald Alvestrandbc9ca252020-10-05 13:08:414992 } else {
4993 RTC_DCHECK(IsDataContent(&content));
4994 // If we already have an data m= section, reject this extra one.
4995 if (*data_index) {
4996 session_options->media_description_options.push_back(
4997 GetMediaDescriptionOptionsForRejectedData(content.name));
4998 } else {
4999 session_options->media_description_options.push_back(
5000 GetMediaDescriptionOptionsForActiveData(content.name));
5001 *data_index = session_options->media_description_options.size() - 1;
5002 }
5003 }
5004 }
5005}
5006
5007cricket::MediaDescriptionOptions
5008SdpOfferAnswerHandler::GetMediaDescriptionOptionsForActiveData(
5009 const std::string& mid) const {
5010 RTC_DCHECK_RUN_ON(signaling_thread());
5011 // Direction for data sections is meaningless, but legacy endpoints might
5012 // expect sendrecv.
5013 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5014 RtpTransceiverDirection::kSendRecv,
5015 /*stopped=*/false);
Harald Alvestrandbc9ca252020-10-05 13:08:415016 return options;
5017}
5018
5019cricket::MediaDescriptionOptions
5020SdpOfferAnswerHandler::GetMediaDescriptionOptionsForRejectedData(
5021 const std::string& mid) const {
5022 RTC_DCHECK_RUN_ON(signaling_thread());
5023 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5024 RtpTransceiverDirection::kInactive,
5025 /*stopped=*/true);
Harald Alvestrandbc9ca252020-10-05 13:08:415026 return options;
5027}
5028
Taylor Brandstetterd0acbd82021-01-25 21:44:555029bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
Henrik Boströmf8187e02021-04-26 19:04:265030 cricket::ContentSource source,
5031 const std::map<std::string, const cricket::ContentGroup*>&
5032 bundle_groups_by_mid) {
Markus Handell518669d2021-06-07 11:30:465033 TRACE_EVENT0("webrtc",
5034 "SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState");
Harald Alvestrandbc9ca252020-10-05 13:08:415035 RTC_DCHECK_RUN_ON(signaling_thread());
5036 // We may need to delete any created default streams and disable creation of
5037 // new ones on the basis of payload type. This is needed to avoid SSRC
5038 // collisions in Call's RtpDemuxer, in the case that a transceiver has
5039 // created a default stream, and then some other channel gets the SSRC
Taylor Brandstetterd3ef4992020-10-16 01:22:575040 // signaled in the corresponding Unified Plan "m=" section. Specifically, we
5041 // need to disable payload type based demuxing when two bundled "m=" sections
5042 // are using the same payload type(s). For more context
Harald Alvestrandbc9ca252020-10-05 13:08:415043 // see https://bugs.chromium.org/p/webrtc/issues/detail?id=11477
5044 const SessionDescriptionInterface* sdesc =
5045 (source == cricket::CS_LOCAL ? local_description()
5046 : remote_description());
Henrik Boströmf8187e02021-04-26 19:04:265047 struct PayloadTypes {
5048 std::set<int> audio_payload_types;
5049 std::set<int> video_payload_types;
Henrik Boström4ea80f32021-06-09 08:29:505050 bool pt_demuxing_possible_audio = true;
5051 bool pt_demuxing_possible_video = true;
Henrik Boströmf8187e02021-04-26 19:04:265052 };
5053 std::map<const cricket::ContentGroup*, PayloadTypes> payload_types_by_bundle;
Henrik Boström4ea80f32021-06-09 08:29:505054 // If the MID is missing from *any* receiving m= section, this is set to true.
5055 bool mid_header_extension_missing_audio = false;
5056 bool mid_header_extension_missing_video = false;
Harald Alvestrandbc9ca252020-10-05 13:08:415057 for (auto& content_info : sdesc->description()->contents()) {
Henrik Boströmf8187e02021-04-26 19:04:265058 auto it = bundle_groups_by_mid.find(content_info.name);
5059 const cricket::ContentGroup* bundle_group =
5060 it != bundle_groups_by_mid.end() ? it->second : nullptr;
Taylor Brandstetterd3ef4992020-10-16 01:22:575061 // If this m= section isn't bundled, it's safe to demux by payload type
5062 // since other m= sections using the same payload type will also be using
5063 // different transports.
Henrik Boströmf8187e02021-04-26 19:04:265064 if (!bundle_group) {
Taylor Brandstetterd3ef4992020-10-16 01:22:575065 continue;
5066 }
Henrik Boströmf8187e02021-04-26 19:04:265067 PayloadTypes* payload_types = &payload_types_by_bundle[bundle_group];
Harald Alvestrandbc9ca252020-10-05 13:08:415068 if (content_info.rejected ||
5069 (source == cricket::ContentSource::CS_LOCAL &&
5070 !RtpTransceiverDirectionHasRecv(
5071 content_info.media_description()->direction())) ||
5072 (source == cricket::ContentSource::CS_REMOTE &&
5073 !RtpTransceiverDirectionHasSend(
5074 content_info.media_description()->direction()))) {
5075 // Ignore transceivers that are not receiving.
5076 continue;
5077 }
5078 switch (content_info.media_description()->type()) {
Taylor Brandstetterd3ef4992020-10-16 01:22:575079 case cricket::MediaType::MEDIA_TYPE_AUDIO: {
Henrik Boström4ea80f32021-06-09 08:29:505080 if (!mid_header_extension_missing_audio) {
5081 mid_header_extension_missing_audio =
5082 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5083 }
Taylor Brandstetterd3ef4992020-10-16 01:22:575084 const cricket::AudioContentDescription* audio_desc =
5085 content_info.media_description()->as_audio();
5086 for (const cricket::AudioCodec& audio : audio_desc->codecs()) {
Henrik Boströmf8187e02021-04-26 19:04:265087 if (payload_types->audio_payload_types.count(audio.id)) {
Taylor Brandstetterd3ef4992020-10-16 01:22:575088 // Two m= sections are using the same payload type, thus demuxing
5089 // by payload type is not possible.
Henrik Boström4ea80f32021-06-09 08:29:505090 payload_types->pt_demuxing_possible_audio = false;
Taylor Brandstetterd3ef4992020-10-16 01:22:575091 }
Henrik Boströmf8187e02021-04-26 19:04:265092 payload_types->audio_payload_types.insert(audio.id);
Taylor Brandstetterd3ef4992020-10-16 01:22:575093 }
Harald Alvestrandbc9ca252020-10-05 13:08:415094 break;
Taylor Brandstetterd3ef4992020-10-16 01:22:575095 }
5096 case cricket::MediaType::MEDIA_TYPE_VIDEO: {
Henrik Boström4ea80f32021-06-09 08:29:505097 if (!mid_header_extension_missing_video) {
5098 mid_header_extension_missing_video =
5099 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5100 }
Taylor Brandstetterd3ef4992020-10-16 01:22:575101 const cricket::VideoContentDescription* video_desc =
5102 content_info.media_description()->as_video();
5103 for (const cricket::VideoCodec& video : video_desc->codecs()) {
Henrik Boströmf8187e02021-04-26 19:04:265104 if (payload_types->video_payload_types.count(video.id)) {
Taylor Brandstetterd3ef4992020-10-16 01:22:575105 // Two m= sections are using the same payload type, thus demuxing
5106 // by payload type is not possible.
Henrik Boström4ea80f32021-06-09 08:29:505107 payload_types->pt_demuxing_possible_video = false;
Taylor Brandstetterd3ef4992020-10-16 01:22:575108 }
Henrik Boströmf8187e02021-04-26 19:04:265109 payload_types->video_payload_types.insert(video.id);
Taylor Brandstetterd3ef4992020-10-16 01:22:575110 }
Harald Alvestrandbc9ca252020-10-05 13:08:415111 break;
Taylor Brandstetterd3ef4992020-10-16 01:22:575112 }
Harald Alvestrandbc9ca252020-10-05 13:08:415113 default:
5114 // Ignore data channels.
5115 continue;
5116 }
5117 }
Harald Alvestrandbc9ca252020-10-05 13:08:415118
Henrik Boström4ea80f32021-06-09 08:29:505119 // In Unified Plan, payload type demuxing is useful for legacy endpoints that
5120 // don't support the MID header extension, but it can also cause incorrrect
5121 // forwarding of packets when going from one m= section to multiple m=
5122 // sections in the same BUNDLE. This only happens if media arrives prior to
5123 // negotiation, but this can cause missing video and unsignalled ssrc bugs
5124 // severe enough to warrant disabling PT demuxing in such cases. Therefore, if
5125 // a MID header extension is present on all m= sections for a given kind
5126 // (audio/video) then we use that as an OK to disable payload type demuxing in
5127 // BUNDLEs of that kind. However if PT demuxing was ever turned on (e.g. MID
5128 // was ever removed on ANY m= section of that kind) then we continue to allow
5129 // PT demuxing in order to prevent disabling it in follow-up O/A exchanges and
5130 // allowing early media by PT.
5131 bool bundled_pt_demux_allowed_audio = !IsUnifiedPlan() ||
5132 mid_header_extension_missing_audio ||
5133 pt_demuxing_has_been_used_audio_;
5134 bool bundled_pt_demux_allowed_video = !IsUnifiedPlan() ||
5135 mid_header_extension_missing_video ||
5136 pt_demuxing_has_been_used_video_;
Henrik Boström4ea80f32021-06-09 08:29:505137
Tomas Gunnarsson92f9b742022-01-02 20:40:225138 // Gather all updates ahead of time so that all channels can be updated in a
Danil Chapovalov9e09a1f2022-09-08 16:38:105139 // single BlockingCall; necessary due to thread guards.
Tomas Gunnarsson92f9b742022-01-02 20:40:225140 std::vector<std::pair<bool, cricket::ChannelInterface*>> channels_to_update;
5141 for (const auto& transceiver : transceivers()->ListInternal()) {
5142 cricket::ChannelInterface* channel = transceiver->channel();
5143 const ContentInfo* content =
5144 FindMediaSectionForTransceiver(transceiver, sdesc);
5145 if (!channel || !content) {
5146 continue;
5147 }
5148
5149 const cricket::MediaType media_type = channel->media_type();
5150 if (media_type != cricket::MediaType::MEDIA_TYPE_AUDIO &&
5151 media_type != cricket::MediaType::MEDIA_TYPE_VIDEO) {
5152 continue;
5153 }
5154
5155 RtpTransceiverDirection local_direction =
5156 content->media_description()->direction();
5157 if (source == cricket::CS_REMOTE) {
5158 local_direction = RtpTransceiverDirectionReversed(local_direction);
5159 }
5160
Tomas Gunnarsson5411b172022-01-24 07:45:265161 auto bundle_it = bundle_groups_by_mid.find(channel->mid());
Tomas Gunnarsson92f9b742022-01-02 20:40:225162 const cricket::ContentGroup* bundle_group =
5163 bundle_it != bundle_groups_by_mid.end() ? bundle_it->second : nullptr;
5164 bool pt_demux_enabled = RtpTransceiverDirectionHasRecv(local_direction);
5165 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
5166 pt_demux_enabled &=
5167 !bundle_group ||
5168 (bundled_pt_demux_allowed_audio &&
5169 payload_types_by_bundle[bundle_group].pt_demuxing_possible_audio);
5170 if (pt_demux_enabled) {
5171 pt_demuxing_has_been_used_audio_ = true;
5172 }
5173 } else {
5174 RTC_DCHECK_EQ(media_type, cricket::MediaType::MEDIA_TYPE_VIDEO);
5175 pt_demux_enabled &=
5176 !bundle_group ||
5177 (bundled_pt_demux_allowed_video &&
5178 payload_types_by_bundle[bundle_group].pt_demuxing_possible_video);
5179 if (pt_demux_enabled) {
5180 pt_demuxing_has_been_used_video_ = true;
5181 }
5182 }
5183
5184 channels_to_update.emplace_back(pt_demux_enabled, transceiver->channel());
5185 }
5186
5187 if (channels_to_update.empty()) {
5188 return true;
5189 }
5190
Danil Chapovalov9e09a1f2022-09-08 16:38:105191 // TODO(bugs.webrtc.org/11993): This BlockingCall() will also block on the
5192 // network thread for every demuxer sink that needs to be updated. The demuxer
5193 // state needs to be fully (and only) managed on the network thread and once
5194 // that's the case, there's no need to stop by on the worker. Ideally we could
5195 // also do this without blocking.
5196 return context_->worker_thread()->BlockingCall([&channels_to_update]() {
5197 for (const auto& it : channels_to_update) {
5198 if (!it.second->SetPayloadTypeDemuxingEnabled(it.first)) {
5199 // Note that the state has already been irrevocably changed at this
5200 // point. Is it useful to stop the loop?
5201 return false;
5202 }
5203 }
5204 return true;
5205 });
Harald Alvestrandbc9ca252020-10-05 13:08:415206}
5207
Harald Alvestrand8101e7b2022-05-23 14:57:475208bool SdpOfferAnswerHandler::ConfiguredForMedia() const {
5209 return context_->media_engine();
5210}
5211
Harald Alvestrandcdcfab02020-09-28 13:02:075212} // namespace webrtc