1 /*
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
13 #include <algorithm>
14 #include <cstddef>
15 #include <iterator>
16 #include <map>
17 #include <memory>
18 #include <queue>
19 #include <string>
20 #include <utility>
21
22 #include "absl/algorithm/container.h"
23 #include "absl/memory/memory.h"
24 #include "absl/strings/match.h"
25 #include "absl/strings/string_view.h"
26 #include "api/array_view.h"
27 #include "api/crypto/crypto_options.h"
28 #include "api/dtls_transport_interface.h"
29 #include "api/field_trials_view.h"
30 #include "api/rtp_parameters.h"
31 #include "api/rtp_receiver_interface.h"
32 #include "api/rtp_sender_interface.h"
33 #include "api/video/builtin_video_bitrate_allocator_factory.h"
34 #include "media/base/codec.h"
35 #include "media/base/rid_description.h"
36 #include "p2p/base/ice_transport_internal.h"
37 #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"
43 #include "pc/channel_interface.h"
44 #include "pc/dtls_transport.h"
45 #include "pc/legacy_stats_collector.h"
46 #include "pc/media_stream.h"
47 #include "pc/media_stream_proxy.h"
48 #include "pc/peer_connection_internal.h"
49 #include "pc/peer_connection_message_handler.h"
50 #include "pc/rtp_media_utils.h"
51 #include "pc/rtp_receiver_proxy.h"
52 #include "pc/rtp_sender.h"
53 #include "pc/rtp_sender_proxy.h"
54 #include "pc/simulcast_description.h"
55 #include "pc/usage_pattern.h"
56 #include "pc/webrtc_session_description_factory.h"
57 #include "rtc_base/helpers.h"
58 #include "rtc_base/logging.h"
59 #include "rtc_base/rtc_certificate.h"
60 #include "rtc_base/ssl_stream_adapter.h"
61 #include "rtc_base/string_encode.h"
62 #include "rtc_base/strings/string_builder.h"
63 #include "rtc_base/trace_event.h"
64 #include "system_wrappers/include/metrics.h"
65
66 using cricket::ContentInfo;
67 using cricket::ContentInfos;
68 using cricket::MediaContentDescription;
69 using cricket::MediaProtocolType;
70 using cricket::RidDescription;
71 using cricket::RidDirection;
72 using cricket::SessionDescription;
73 using cricket::SimulcastDescription;
74 using cricket::SimulcastLayer;
75 using cricket::SimulcastLayerList;
76 using cricket::StreamParams;
77 using cricket::TransportInfo;
78
79 using cricket::LOCAL_PORT_TYPE;
80 using cricket::PRFLX_PORT_TYPE;
81 using cricket::RELAY_PORT_TYPE;
82 using cricket::STUN_PORT_TYPE;
83
84 namespace webrtc {
85
86 namespace {
87
88 typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions
89 RTCOfferAnswerOptions;
90
91 // Error messages
92 const char kInvalidSdp[] = "Invalid session description.";
93 const char kInvalidCandidates[] = "Description contains invalid candidates.";
94 const char kBundleWithoutRtcpMux[] =
95 "rtcp-mux must be enabled when BUNDLE "
96 "is enabled.";
97 const char kMlineMismatchInAnswer[] =
98 "The order of m-lines in answer doesn't match order in offer. Rejecting "
99 "answer.";
100 const char kMlineMismatchInSubsequentOffer[] =
101 "The order of m-lines in subsequent offer doesn't match order from "
102 "previous offer/answer.";
103 const char kSdpWithoutIceUfragPwd[] =
104 "Called with SDP without ice-ufrag and ice-pwd.";
105 const char kSdpWithoutDtlsFingerprint[] =
106 "Called with SDP without DTLS fingerprint.";
107 const char kSdpWithoutSdesCrypto[] = "Called with SDP without SDES crypto.";
108
109 const char kSessionError[] = "Session error code: ";
110 const char kSessionErrorDesc[] = "Session error description: ";
111
112 // UMA metric names.
113 const char kSimulcastVersionApplyLocalDescription[] =
114 "WebRTC.PeerConnection.Simulcast.ApplyLocalDescription";
115 const char kSimulcastVersionApplyRemoteDescription[] =
116 "WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription";
117 const char kSimulcastDisabled[] = "WebRTC.PeerConnection.Simulcast.Disabled";
118
119 // The length of RTCP CNAMEs.
120 static const int kRtcpCnameLength = 16;
121
122 // The maximum length of the MID attribute.
123 // TODO(bugs.webrtc.org/12517) - reduce to 16 again.
124 static constexpr size_t kMidMaxSize = 32;
125
126 const char kDefaultStreamId[] = "default";
127 // NOTE: Duplicated in peer_connection.cc:
128 static const char kDefaultAudioSenderId[] = "defaulta0";
129 static const char kDefaultVideoSenderId[] = "defaultv0";
130
NoteAddIceCandidateResult(int result)131 void NoteAddIceCandidateResult(int result) {
132 RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result,
133 kAddIceCandidateMax);
134 }
135
GetBundleGroupsByMid(const SessionDescription * desc)136 std::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
149 // Returns true if `new_desc` requests an ICE restart (i.e., new ufrag/pwd).
CheckForRemoteIceRestart(const SessionDescriptionInterface * old_desc,const SessionDescriptionInterface * new_desc,const std::string & content_name)150 bool 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.
GetSetDescriptionErrorMessage(cricket::ContentSource source,SdpType type,const RTCError & error)183 std::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")
188 << " " << SdpTypeToString(type) << " sdp: ";
189 RTC_DCHECK(!absl::StartsWith(error.message(), oss.str())) << error.message();
190 oss << error.message();
191 return oss.Release();
192 }
193
GetStreamIdsString(rtc::ArrayView<const std::string> stream_ids)194 std::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
ReportSimulcastApiVersion(const char * name,const SessionDescription & session)205 void 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
FindTransceiverMSection(RtpTransceiver * transceiver,const SessionDescriptionInterface * session_description)233 const ContentInfo* FindTransceiverMSection(
234 RtpTransceiver* transceiver,
235 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
GetActiveStreams(const cricket::MediaContentDescription * desc)245 std::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
254 // rejected. `old_content_one` and `old_content_two` refer to the m= section
255 // 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.
IsMediaSectionBeingRecycled(SdpType type,const ContentInfo & content,const ContentInfo * old_content_one,const ContentInfo * old_content_two)258 bool 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
267 // 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
272 // 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
274 // possible rejected m sections. These are the `current_desc` and
275 // `secondary_current_desc`.
MediaSectionsInSameOrder(const SessionDescription & current_desc,const SessionDescription * secondary_current_desc,const SessionDescription & new_desc,const SdpType type)276 bool 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 ¤t_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
MediaSectionsHaveSameCount(const SessionDescription & desc1,const SessionDescription & desc2)311 bool MediaSectionsHaveSameCount(const SessionDescription& desc1,
312 const SessionDescription& desc2) {
313 return desc1.contents().size() == desc2.contents().size();
314 }
315 // Checks that each non-rejected content has SDES crypto keys or a DTLS
316 // 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
320 // by Channel's `srtp_required` check.
VerifyCrypto(const SessionDescription * desc,bool dtls_enabled,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)321 RTCError VerifyCrypto(const SessionDescription* desc,
322 bool dtls_enabled,
323 const std::map<std::string, const cricket::ContentGroup*>&
324 bundle_groups_by_mid) {
325 for (const cricket::ContentInfo& content_info : desc->contents()) {
326 if (content_info.rejected) {
327 continue;
328 }
329 #if !defined(WEBRTC_FUCHSIA)
330 RTC_CHECK(dtls_enabled) << "SDES protocol is only allowed in Fuchsia";
331 #endif
332 const std::string& mid = content_info.name;
333 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())) {
337 // 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 }
351 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 }
365 }
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.
VerifyIceUfragPwdPresent(const SessionDescription * desc,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)373 bool VerifyIceUfragPwdPresent(
374 const SessionDescription* desc,
375 const std::map<std::string, const cricket::ContentGroup*>&
376 bundle_groups_by_mid) {
377 for (const cricket::ContentInfo& content_info : desc->contents()) {
378 if (content_info.rejected) {
379 continue;
380 }
381 const std::string& mid = content_info.name;
382 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())) {
386 // 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
ValidateMids(const cricket::SessionDescription & description)409 RTCError ValidateMids(const cricket::SessionDescription& description) {
410 std::set<std::string> mids;
411 size_t max_length = 0;
412 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 }
417 max_length = std::max(max_length, content.name.size());
418 if (content.name.size() > kMidMaxSize) {
419 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
420 "The MID attribute exceeds the maximum supported "
421 "length of 32 characters.");
422 }
423 if (!mids.insert(content.name).second) {
424 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
425 "Duplicate a=mid value '" + content.name + "'.");
426 }
427 }
428 RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.PeerConnection.Mid.Size", max_length, 0,
429 31, 32);
430 return RTCError::OK();
431 }
432
FindDuplicateCodecParameters(const RtpCodecParameters codec_parameters,std::map<int,RtpCodecParameters> & payload_to_codec_parameters)433 RTCError FindDuplicateCodecParameters(
434 const RtpCodecParameters codec_parameters,
435 std::map<int, RtpCodecParameters>& payload_to_codec_parameters) {
436 auto existing_codec_parameters =
437 payload_to_codec_parameters.find(codec_parameters.payload_type);
438 if (existing_codec_parameters != payload_to_codec_parameters.end() &&
439 codec_parameters != existing_codec_parameters->second) {
440 return RTCError(RTCErrorType::INVALID_PARAMETER,
441 "A BUNDLE group contains a codec collision for "
442 "payload_type='" +
443 rtc::ToString(codec_parameters.payload_type) +
444 ". All codecs must share the same type, "
445 "encoding name, clock rate and parameters.");
446 }
447 payload_to_codec_parameters.insert(
448 std::make_pair(codec_parameters.payload_type, codec_parameters));
449 return RTCError::OK();
450 }
451
ValidateBundledPayloadTypes(const cricket::SessionDescription & description)452 RTCError ValidateBundledPayloadTypes(
453 const cricket::SessionDescription& description) {
454 // https://www.rfc-editor.org/rfc/rfc8843#name-payload-type-pt-value-reuse
455 // ... all codecs associated with the payload type number MUST share an
456 // identical codec configuration. This means that the codecs MUST share
457 // the same media type, encoding name, clock rate, and any parameter
458 // that can affect the codec configuration and packetization.
459 std::map<int, RtpCodecParameters> payload_to_codec_parameters;
460 std::vector<const cricket::ContentGroup*> bundle_groups =
461 description.GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
462 for (const cricket::ContentGroup* bundle_group : bundle_groups) {
463 std::map<int, RtpCodecParameters> payload_to_codec_parameters;
464 for (const std::string& content_name : bundle_group->content_names()) {
465 const cricket::MediaContentDescription* media_description =
466 description.GetContentDescriptionByName(content_name);
467 if (!media_description) {
468 return RTCError(RTCErrorType::INVALID_PARAMETER,
469 "A BUNDLE group contains a MID='" + content_name +
470 "' matching no m= section.");
471 }
472 if (!media_description->has_codecs()) {
473 continue;
474 }
475 const auto type = media_description->type();
476 if (type == cricket::MEDIA_TYPE_AUDIO) {
477 RTC_DCHECK(media_description->as_audio());
478 for (const auto& c : media_description->as_audio()->codecs()) {
479 auto error = FindDuplicateCodecParameters(
480 c.ToCodecParameters(), payload_to_codec_parameters);
481 if (!error.ok()) {
482 return error;
483 }
484 }
485 } else if (type == cricket::MEDIA_TYPE_VIDEO) {
486 RTC_DCHECK(media_description->as_video());
487 for (const auto& c : media_description->as_video()->codecs()) {
488 auto error = FindDuplicateCodecParameters(
489 c.ToCodecParameters(), payload_to_codec_parameters);
490 if (!error.ok()) {
491 return error;
492 }
493 }
494 }
495 }
496 }
497 return RTCError::OK();
498 }
499
IsValidOfferToReceiveMedia(int value)500 bool IsValidOfferToReceiveMedia(int value) {
501 typedef PeerConnectionInterface::RTCOfferAnswerOptions Options;
502 return (value >= Options::kUndefined) &&
503 (value <= Options::kMaxOfferToReceiveMedia);
504 }
505
ValidateOfferAnswerOptions(const PeerConnectionInterface::RTCOfferAnswerOptions & rtc_options)506 bool ValidateOfferAnswerOptions(
507 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) {
508 return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) &&
509 IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video);
510 }
511
512 // This method will extract any send encodings that were sent by the remote
513 // connection. This is currently only relevant for Simulcast scenario (where
514 // the number of layers may be communicated by the server).
GetSendEncodingsFromRemoteDescription(const MediaContentDescription & desc)515 std::vector<RtpEncodingParameters> GetSendEncodingsFromRemoteDescription(
516 const MediaContentDescription& desc) {
517 if (!desc.HasSimulcast()) {
518 return {};
519 }
520 std::vector<RtpEncodingParameters> result;
521 const SimulcastDescription& simulcast = desc.simulcast_description();
522
523 // This is a remote description, the parameters we are after should appear
524 // as receive streams.
525 for (const auto& alternatives : simulcast.receive_layers()) {
526 RTC_DCHECK(!alternatives.empty());
527 // There is currently no way to specify or choose from alternatives.
528 // We will always use the first alternative, which is the most preferred.
529 const SimulcastLayer& layer = alternatives[0];
530 RtpEncodingParameters parameters;
531 parameters.rid = layer.rid;
532 parameters.active = !layer.is_paused;
533 result.push_back(parameters);
534 }
535
536 return result;
537 }
538
UpdateSimulcastLayerStatusInSender(const std::vector<SimulcastLayer> & layers,rtc::scoped_refptr<RtpSenderInternal> sender)539 RTCError UpdateSimulcastLayerStatusInSender(
540 const std::vector<SimulcastLayer>& layers,
541 rtc::scoped_refptr<RtpSenderInternal> sender) {
542 RTC_DCHECK(sender);
543 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
544 std::vector<std::string> disabled_layers;
545
546 // The simulcast envelope cannot be changed, only the status of the streams.
547 // So we will iterate over the send encodings rather than the layers.
548 for (RtpEncodingParameters& encoding : parameters.encodings) {
549 auto iter = std::find_if(layers.begin(), layers.end(),
550 [&encoding](const SimulcastLayer& layer) {
551 return layer.rid == encoding.rid;
552 });
553 // A layer that cannot be found may have been removed by the remote party.
554 if (iter == layers.end()) {
555 disabled_layers.push_back(encoding.rid);
556 continue;
557 }
558
559 encoding.active = !iter->is_paused;
560 }
561
562 RTCError result = sender->SetParametersInternalWithAllLayers(parameters);
563 if (result.ok()) {
564 result = sender->DisableEncodingLayers(disabled_layers);
565 }
566
567 return result;
568 }
569
SimulcastIsRejected(const ContentInfo * local_content,const MediaContentDescription & answer_media_desc,bool enable_encrypted_rtp_header_extensions)570 bool SimulcastIsRejected(const ContentInfo* local_content,
571 const MediaContentDescription& answer_media_desc,
572 bool enable_encrypted_rtp_header_extensions) {
573 bool simulcast_offered = local_content &&
574 local_content->media_description() &&
575 local_content->media_description()->HasSimulcast();
576 bool simulcast_answered = answer_media_desc.HasSimulcast();
577 bool rids_supported = RtpExtension::FindHeaderExtensionByUri(
578 answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri,
579 enable_encrypted_rtp_header_extensions
580 ? RtpExtension::Filter::kPreferEncryptedExtension
581 : RtpExtension::Filter::kDiscardEncryptedExtension);
582 return simulcast_offered && (!simulcast_answered || !rids_supported);
583 }
584
DisableSimulcastInSender(rtc::scoped_refptr<RtpSenderInternal> sender)585 RTCError DisableSimulcastInSender(
586 rtc::scoped_refptr<RtpSenderInternal> sender) {
587 RTC_DCHECK(sender);
588 RtpParameters parameters = sender->GetParametersInternalWithAllLayers();
589 if (parameters.encodings.size() <= 1) {
590 return RTCError::OK();
591 }
592
593 std::vector<std::string> disabled_layers;
594 std::transform(
595 parameters.encodings.begin() + 1, parameters.encodings.end(),
596 std::back_inserter(disabled_layers),
597 [](const RtpEncodingParameters& encoding) { return encoding.rid; });
598 return sender->DisableEncodingLayers(disabled_layers);
599 }
600
601 // The SDP parser used to populate these values by default for the 'content
602 // name' if an a=mid line was absent.
GetDefaultMidForPlanB(cricket::MediaType media_type)603 absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) {
604 switch (media_type) {
605 case cricket::MEDIA_TYPE_AUDIO:
606 return cricket::CN_AUDIO;
607 case cricket::MEDIA_TYPE_VIDEO:
608 return cricket::CN_VIDEO;
609 case cricket::MEDIA_TYPE_DATA:
610 return cricket::CN_DATA;
611 case cricket::MEDIA_TYPE_UNSUPPORTED:
612 return "not supported";
613 }
614 RTC_DCHECK_NOTREACHED();
615 return "";
616 }
617
618 // Add options to |[audio/video]_media_description_options| from `senders`.
AddPlanBRtpSenderOptions(const std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>> & senders,cricket::MediaDescriptionOptions * audio_media_description_options,cricket::MediaDescriptionOptions * video_media_description_options,int num_sim_layers)619 void AddPlanBRtpSenderOptions(
620 const std::vector<rtc::scoped_refptr<
621 RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
622 cricket::MediaDescriptionOptions* audio_media_description_options,
623 cricket::MediaDescriptionOptions* video_media_description_options,
624 int num_sim_layers) {
625 for (const auto& sender : senders) {
626 if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
627 if (audio_media_description_options) {
628 audio_media_description_options->AddAudioSender(
629 sender->id(), sender->internal()->stream_ids());
630 }
631 } else {
632 RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO);
633 if (video_media_description_options) {
634 video_media_description_options->AddVideoSender(
635 sender->id(), sender->internal()->stream_ids(), {},
636 SimulcastLayerList(), num_sim_layers);
637 }
638 }
639 }
640 }
641
GetMediaDescriptionOptionsForTransceiver(RtpTransceiver * transceiver,const std::string & mid,bool is_create_offer)642 cricket::MediaDescriptionOptions GetMediaDescriptionOptionsForTransceiver(
643 RtpTransceiver* transceiver,
644 const std::string& mid,
645 bool is_create_offer) {
646 // NOTE: a stopping transceiver should be treated as a stopped one in
647 // createOffer as specified in
648 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
649 bool stopped =
650 is_create_offer ? transceiver->stopping() : transceiver->stopped();
651 cricket::MediaDescriptionOptions media_description_options(
652 transceiver->media_type(), mid, transceiver->direction(), stopped);
653 media_description_options.codec_preferences =
654 transceiver->codec_preferences();
655 media_description_options.header_extensions =
656 transceiver->HeaderExtensionsToOffer();
657 // This behavior is specified in JSEP. The gist is that:
658 // 1. The MSID is included if the RtpTransceiver's direction is sendonly or
659 // sendrecv.
660 // 2. If the MSID is included, then it must be included in any subsequent
661 // offer/answer exactly the same until the RtpTransceiver is stopped.
662 if (stopped || (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
663 !transceiver->has_ever_been_used_to_send())) {
664 return media_description_options;
665 }
666
667 cricket::SenderOptions sender_options;
668 sender_options.track_id = transceiver->sender()->id();
669 sender_options.stream_ids = transceiver->sender()->stream_ids();
670
671 // The following sets up RIDs and Simulcast.
672 // RIDs are included if Simulcast is requested or if any RID was specified.
673 RtpParameters send_parameters =
674 transceiver->sender_internal()->GetParametersInternalWithAllLayers();
675 bool has_rids = std::any_of(send_parameters.encodings.begin(),
676 send_parameters.encodings.end(),
677 [](const RtpEncodingParameters& encoding) {
678 return !encoding.rid.empty();
679 });
680
681 std::vector<RidDescription> send_rids;
682 SimulcastLayerList send_layers;
683 for (const RtpEncodingParameters& encoding : send_parameters.encodings) {
684 if (encoding.rid.empty()) {
685 continue;
686 }
687 send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend));
688 send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
689 }
690
691 if (has_rids) {
692 sender_options.rids = send_rids;
693 }
694
695 sender_options.simulcast_layers = send_layers;
696 // When RIDs are configured, we must set num_sim_layers to 0 to.
697 // Otherwise, num_sim_layers must be 1 because either there is no
698 // simulcast, or simulcast is acheived by munging the SDP.
699 sender_options.num_sim_layers = has_rids ? 0 : 1;
700 media_description_options.sender_options.push_back(sender_options);
701
702 return media_description_options;
703 }
704
705 // Returns the ContentInfo at mline index `i`, or null if none exists.
GetContentByIndex(const SessionDescriptionInterface * sdesc,size_t i)706 const ContentInfo* GetContentByIndex(const SessionDescriptionInterface* sdesc,
707 size_t i) {
708 if (!sdesc) {
709 return nullptr;
710 }
711 const ContentInfos& contents = sdesc->description()->contents();
712 return (i < contents.size() ? &contents[i] : nullptr);
713 }
714
715 // From `rtc_options`, fill parts of `session_options` shared by all generated
716 // m= sectionss (in other words, nothing that involves a map/array).
ExtractSharedMediaSessionOptions(const PeerConnectionInterface::RTCOfferAnswerOptions & rtc_options,cricket::MediaSessionOptions * session_options)717 void ExtractSharedMediaSessionOptions(
718 const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
719 cricket::MediaSessionOptions* session_options) {
720 session_options->vad_enabled = rtc_options.voice_activity_detection;
721 session_options->bundle_enabled = rtc_options.use_rtp_mux;
722 session_options->raw_packetization_for_video =
723 rtc_options.raw_packetization_for_video;
724 }
725
726 // Generate a RTCP CNAME when a PeerConnection is created.
GenerateRtcpCname()727 std::string GenerateRtcpCname() {
728 std::string cname;
729 if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) {
730 RTC_LOG(LS_ERROR) << "Failed to generate CNAME.";
731 RTC_DCHECK_NOTREACHED();
732 }
733 return cname;
734 }
735
736 // Check if we can send `new_stream` on a PeerConnection.
CanAddLocalMediaStream(webrtc::StreamCollectionInterface * current_streams,webrtc::MediaStreamInterface * new_stream)737 bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams,
738 webrtc::MediaStreamInterface* new_stream) {
739 if (!new_stream || !current_streams) {
740 return false;
741 }
742 if (current_streams->find(new_stream->id()) != nullptr) {
743 RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id()
744 << " is already added.";
745 return false;
746 }
747 return true;
748 }
749
LookupDtlsTransportByMid(rtc::Thread * network_thread,JsepTransportController * controller,const std::string & mid)750 rtc::scoped_refptr<webrtc::DtlsTransport> LookupDtlsTransportByMid(
751 rtc::Thread* network_thread,
752 JsepTransportController* controller,
753 const std::string& mid) {
754 // TODO(tommi): Can we post this (and associated operations where this
755 // function is called) to the network thread and avoid this BlockingCall?
756 // We might be able to simplify a few things if we set the transport on
757 // the network thread and then update the implementation to check that
758 // the set_ and relevant get methods are always called on the network
759 // thread (we'll need to update proxy maps).
760 return network_thread->BlockingCall(
761 [controller, &mid] { return controller->LookupDtlsTransportByMid(mid); });
762 }
763
ContentHasHeaderExtension(const cricket::ContentInfo & content_info,absl::string_view header_extension_uri)764 bool ContentHasHeaderExtension(const cricket::ContentInfo& content_info,
765 absl::string_view header_extension_uri) {
766 for (const RtpExtension& rtp_header_extension :
767 content_info.media_description()->rtp_header_extensions()) {
768 if (rtp_header_extension.uri == header_extension_uri) {
769 return true;
770 }
771 }
772 return false;
773 }
774
775 } // namespace
776
777 // This class stores state related to a SetRemoteDescription operation, captures
778 // and reports potential errors that migth occur and makes sure to notify the
779 // observer of the operation and the operations chain of completion.
780 class SdpOfferAnswerHandler::RemoteDescriptionOperation {
781 public:
RemoteDescriptionOperation(SdpOfferAnswerHandler * handler,std::unique_ptr<SessionDescriptionInterface> desc,rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer,std::function<void ()> operations_chain_callback)782 RemoteDescriptionOperation(
783 SdpOfferAnswerHandler* handler,
784 std::unique_ptr<SessionDescriptionInterface> desc,
785 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer,
786 std::function<void()> operations_chain_callback)
787 : handler_(handler),
788 desc_(std::move(desc)),
789 observer_(std::move(observer)),
790 operations_chain_callback_(std::move(operations_chain_callback)),
791 unified_plan_(handler_->IsUnifiedPlan()) {
792 if (!desc_) {
793 type_ = static_cast<SdpType>(-1);
794 InvalidParam("SessionDescription is NULL.");
795 } else {
796 type_ = desc_->GetType();
797 }
798 }
799
~RemoteDescriptionOperation()800 ~RemoteDescriptionOperation() {
801 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
802 SignalCompletion();
803 operations_chain_callback_();
804 }
805
ok() const806 bool ok() const { return error_.ok(); }
807
808 // Notifies the observer that the operation is complete and releases the
809 // reference to the observer.
SignalCompletion()810 void SignalCompletion() {
811 if (!observer_)
812 return;
813
814 if (!error_.ok() && type_ != static_cast<SdpType>(-1)) {
815 std::string error_message =
816 GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type_, error_);
817 RTC_LOG(LS_ERROR) << error_message;
818 error_.set_message(std::move(error_message));
819 }
820
821 observer_->OnSetRemoteDescriptionComplete(error_);
822 observer_ = nullptr; // Only fire the notification once.
823 }
824
825 // If a session error has occurred the PeerConnection is in a possibly
826 // inconsistent state so fail right away.
HaveSessionError()827 bool HaveSessionError() {
828 RTC_DCHECK(ok());
829 if (handler_->session_error() != SessionError::kNone)
830 InternalError(handler_->GetSessionErrorMsg());
831 return !ok();
832 }
833
834 // Returns true if the operation was a rollback operation. If this function
835 // returns true, the caller should consider the operation complete. Otherwise
836 // proceed to the next step.
MaybeRollback()837 bool MaybeRollback() {
838 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
839 RTC_DCHECK(ok());
840 if (type_ != SdpType::kRollback) {
841 // Check if we can do an implicit rollback.
842 if (type_ == SdpType::kOffer && unified_plan_ &&
843 handler_->pc_->configuration()->enable_implicit_rollback &&
844 handler_->signaling_state() ==
845 PeerConnectionInterface::kHaveLocalOffer) {
846 handler_->Rollback(type_);
847 }
848 return false;
849 }
850
851 if (unified_plan_) {
852 error_ = handler_->Rollback(type_);
853 } else if (type_ == SdpType::kRollback) {
854 Unsupported("Rollback not supported in Plan B");
855 }
856
857 return true;
858 }
859
860 // Report to UMA the format of the received offer or answer.
ReportOfferAnswerUma()861 void ReportOfferAnswerUma() {
862 RTC_DCHECK(ok());
863 if (type_ == SdpType::kOffer || type_ == SdpType::kAnswer) {
864 handler_->pc_->ReportSdpBundleUsage(*desc_.get());
865 }
866 }
867
868 // Checks if the session description for the operation is valid. If not, the
869 // function captures error information and returns false. Note that if the
870 // return value is false, the operation should be considered done.
IsDescriptionValid()871 bool IsDescriptionValid() {
872 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
873 RTC_DCHECK(ok());
874 RTC_DCHECK(bundle_groups_by_mid_.empty()) << "Already called?";
875 bundle_groups_by_mid_ = GetBundleGroupsByMid(description());
876 error_ = handler_->ValidateSessionDescription(
877 desc_.get(), cricket::CS_REMOTE, bundle_groups_by_mid_);
878 return ok();
879 }
880
881 // Transfers ownership of the session description object over to `handler_`.
ReplaceRemoteDescriptionAndCheckEror()882 bool ReplaceRemoteDescriptionAndCheckEror() {
883 RTC_DCHECK_RUN_ON(handler_->signaling_thread());
884 RTC_DCHECK(ok());
885 RTC_DCHECK(desc_);
886 RTC_DCHECK(!replaced_remote_description_);
887 #if RTC_DCHECK_IS_ON
888 const auto* existing_remote_description = handler_->remote_description();
889 #endif
890
891 error_ = handler_->ReplaceRemoteDescription(std::move(desc_), type_,
892 &replaced_remote_description_);
893
894 if (ok()) {
895 #if RTC_DCHECK_IS_ON
896 // Sanity check that our `old_remote_description()` method always returns
897 // the same value as `remote_description()` did before the call to
898 // ReplaceRemoteDescription.
899 RTC_DCHECK_EQ(existing_remote_description, old_remote_description());
900 #endif
901 } else {
902 SetAsSessionError();
903 }
904
905 return ok();
906 }
907
UpdateChannels()908 bool UpdateChannels() {
909 RTC_DCHECK(ok());
910 RTC_DCHECK(!desc_) << "ReplaceRemoteDescription hasn't been called";
911
912 const auto* remote_description = handler_->remote_description();
913
914 const cricket::SessionDescription* session_desc =
915 remote_description->description();
916
917 // Transport and Media channels will be created only when offer is set.
918 if (unified_plan_) {
919 error_ = handler_->UpdateTransceiversAndDataChannels(
920 cricket::CS_REMOTE, *remote_description,
921 handler_->local_description(), old_remote_description(),
922 bundle_groups_by_mid_);
923 } else {
924 // Media channels will be created only when offer is set. These may use
925 // new transports just created by PushdownTransportDescription.
926 if (type_ == SdpType::kOffer) {
927 // TODO(mallinath) - Handle CreateChannel failure, as new local
928 // description is applied. Restore back to old description.
929 error_ = handler_->CreateChannels(*session_desc);
930 }
931 // Remove unused channels if MediaContentDescription is rejected.
932 handler_->RemoveUnusedChannels(session_desc);
933 }
934
935 return ok();
936 }
937
UpdateSessionState()938 bool UpdateSessionState() {
939 RTC_DCHECK(ok());
940 error_ = handler_->UpdateSessionState(
941 type_, cricket::CS_REMOTE,
942 handler_->remote_description()->description(), bundle_groups_by_mid_);
943 if (!ok())
944 SetAsSessionError();
945 return ok();
946 }
947
UseCandidatesInRemoteDescription()948 bool UseCandidatesInRemoteDescription() {
949 RTC_DCHECK(ok());
950 if (handler_->local_description() &&
951 !handler_->UseCandidatesInRemoteDescription()) {
952 InvalidParam(kInvalidCandidates);
953 }
954 return ok();
955 }
956
957 // Convenience getter for desc_->GetType().
type() const958 SdpType type() const { return type_; }
unified_plan() const959 bool unified_plan() const { return unified_plan_; }
description()960 cricket::SessionDescription* description() { return desc_->description(); }
961
old_remote_description() const962 const SessionDescriptionInterface* old_remote_description() const {
963 RTC_DCHECK(!desc_) << "Called before replacing the remote description";
964 if (type_ == SdpType::kAnswer)
965 return replaced_remote_description_.get();
966 return replaced_remote_description_
967 ? replaced_remote_description_.get()
968 : handler_->current_remote_description();
969 }
970
971 // Returns a reference to a cached map of bundle groups ordered by mid.
972 // Note that this will only be valid after a successful call to
973 // `IsDescriptionValid`.
974 const std::map<std::string, const cricket::ContentGroup*>&
bundle_groups_by_mid() const975 bundle_groups_by_mid() const {
976 RTC_DCHECK(ok());
977 return bundle_groups_by_mid_;
978 }
979
980 private:
981 // Convenience methods for populating the embedded `error_` object.
Unsupported(std::string message)982 void Unsupported(std::string message) {
983 SetError(RTCErrorType::UNSUPPORTED_OPERATION, std::move(message));
984 }
985
InvalidParam(std::string message)986 void InvalidParam(std::string message) {
987 SetError(RTCErrorType::INVALID_PARAMETER, std::move(message));
988 }
989
InternalError(std::string message)990 void InternalError(std::string message) {
991 SetError(RTCErrorType::INTERNAL_ERROR, std::move(message));
992 }
993
SetError(RTCErrorType type,std::string message)994 void SetError(RTCErrorType type, std::string message) {
995 RTC_DCHECK(ok()) << "Overwriting an existing error?";
996 error_ = RTCError(type, std::move(message));
997 }
998
999 // Called when the PeerConnection could be in an inconsistent state and we set
1000 // the session error so that future calls to
1001 // SetLocalDescription/SetRemoteDescription fail.
SetAsSessionError()1002 void SetAsSessionError() {
1003 RTC_DCHECK(!ok());
1004 handler_->SetSessionError(SessionError::kContent, error_.message());
1005 }
1006
1007 SdpOfferAnswerHandler* const handler_;
1008 std::unique_ptr<SessionDescriptionInterface> desc_;
1009 // Keeps the replaced session description object alive while the operation
1010 // is taking place since methods that depend on `old_remote_description()`
1011 // for updating the state, need it.
1012 std::unique_ptr<SessionDescriptionInterface> replaced_remote_description_;
1013 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer_;
1014 std::function<void()> operations_chain_callback_;
1015 RTCError error_ = RTCError::OK();
1016 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid_;
1017 SdpType type_;
1018 const bool unified_plan_;
1019 };
1020 // Used by parameterless SetLocalDescription() to create an offer or answer.
1021 // Upon completion of creating the session description, SetLocalDescription() is
1022 // invoked with the result.
1023 class SdpOfferAnswerHandler::ImplicitCreateSessionDescriptionObserver
1024 : public CreateSessionDescriptionObserver {
1025 public:
ImplicitCreateSessionDescriptionObserver(rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler,rtc::scoped_refptr<SetLocalDescriptionObserverInterface> set_local_description_observer)1026 ImplicitCreateSessionDescriptionObserver(
1027 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler,
1028 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
1029 set_local_description_observer)
1030 : sdp_handler_(std::move(sdp_handler)),
1031 set_local_description_observer_(
1032 std::move(set_local_description_observer)) {}
~ImplicitCreateSessionDescriptionObserver()1033 ~ImplicitCreateSessionDescriptionObserver() override {
1034 RTC_DCHECK(was_called_);
1035 }
1036
SetOperationCompleteCallback(std::function<void ()> operation_complete_callback)1037 void SetOperationCompleteCallback(
1038 std::function<void()> operation_complete_callback) {
1039 operation_complete_callback_ = std::move(operation_complete_callback);
1040 }
1041
was_called() const1042 bool was_called() const { return was_called_; }
1043
OnSuccess(SessionDescriptionInterface * desc_ptr)1044 void OnSuccess(SessionDescriptionInterface* desc_ptr) override {
1045 RTC_DCHECK(!was_called_);
1046 std::unique_ptr<SessionDescriptionInterface> desc(desc_ptr);
1047 was_called_ = true;
1048
1049 // Abort early if `pc_` is no longer valid.
1050 if (!sdp_handler_) {
1051 operation_complete_callback_();
1052 return;
1053 }
1054 // DoSetLocalDescription() is a synchronous operation that invokes
1055 // `set_local_description_observer_` with the result.
1056 sdp_handler_->DoSetLocalDescription(
1057 std::move(desc), std::move(set_local_description_observer_));
1058 operation_complete_callback_();
1059 }
1060
OnFailure(RTCError error)1061 void OnFailure(RTCError error) override {
1062 RTC_DCHECK(!was_called_);
1063 was_called_ = true;
1064 set_local_description_observer_->OnSetLocalDescriptionComplete(RTCError(
1065 error.type(), std::string("SetLocalDescription failed to create "
1066 "session description - ") +
1067 error.message()));
1068 operation_complete_callback_();
1069 }
1070
1071 private:
1072 bool was_called_ = false;
1073 rtc::WeakPtr<SdpOfferAnswerHandler> sdp_handler_;
1074 rtc::scoped_refptr<SetLocalDescriptionObserverInterface>
1075 set_local_description_observer_;
1076 std::function<void()> operation_complete_callback_;
1077 };
1078
1079 // Wraps a CreateSessionDescriptionObserver and an OperationsChain operation
1080 // complete callback. When the observer is invoked, the wrapped observer is
1081 // invoked followed by invoking the completion callback.
1082 class CreateSessionDescriptionObserverOperationWrapper
1083 : public CreateSessionDescriptionObserver {
1084 public:
CreateSessionDescriptionObserverOperationWrapper(rtc::scoped_refptr<CreateSessionDescriptionObserver> observer,std::function<void ()> operation_complete_callback)1085 CreateSessionDescriptionObserverOperationWrapper(
1086 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer,
1087 std::function<void()> operation_complete_callback)
1088 : observer_(std::move(observer)),
1089 operation_complete_callback_(std::move(operation_complete_callback)) {
1090 RTC_DCHECK(observer_);
1091 }
~CreateSessionDescriptionObserverOperationWrapper()1092 ~CreateSessionDescriptionObserverOperationWrapper() override {
1093 #if RTC_DCHECK_IS_ON
1094 RTC_DCHECK(was_called_);
1095 #endif
1096 }
1097
OnSuccess(SessionDescriptionInterface * desc)1098 void OnSuccess(SessionDescriptionInterface* desc) override {
1099 #if RTC_DCHECK_IS_ON
1100 RTC_DCHECK(!was_called_);
1101 was_called_ = true;
1102 #endif // RTC_DCHECK_IS_ON
1103 // Completing the operation before invoking the observer allows the observer
1104 // to execute SetLocalDescription() without delay.
1105 operation_complete_callback_();
1106 observer_->OnSuccess(desc);
1107 }
1108
OnFailure(RTCError error)1109 void OnFailure(RTCError error) override {
1110 #if RTC_DCHECK_IS_ON
1111 RTC_DCHECK(!was_called_);
1112 was_called_ = true;
1113 #endif // RTC_DCHECK_IS_ON
1114 operation_complete_callback_();
1115 observer_->OnFailure(std::move(error));
1116 }
1117
1118 private:
1119 #if RTC_DCHECK_IS_ON
1120 bool was_called_ = false;
1121 #endif // RTC_DCHECK_IS_ON
1122 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer_;
1123 std::function<void()> operation_complete_callback_;
1124 };
1125
1126 // Wrapper for SetSessionDescriptionObserver that invokes the success or failure
1127 // callback in a posted message handled by the peer connection. This introduces
1128 // a delay that prevents recursive API calls by the observer, but this also
1129 // means that the PeerConnection can be modified before the observer sees the
1130 // result of the operation. This is ill-advised for synchronizing states.
1131 //
1132 // Implements both the SetLocalDescriptionObserverInterface and the
1133 // SetRemoteDescriptionObserverInterface.
1134 class SdpOfferAnswerHandler::SetSessionDescriptionObserverAdapter
1135 : public SetLocalDescriptionObserverInterface,
1136 public SetRemoteDescriptionObserverInterface {
1137 public:
SetSessionDescriptionObserverAdapter(rtc::WeakPtr<SdpOfferAnswerHandler> handler,rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer)1138 SetSessionDescriptionObserverAdapter(
1139 rtc::WeakPtr<SdpOfferAnswerHandler> handler,
1140 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer)
1141 : handler_(std::move(handler)),
1142 inner_observer_(std::move(inner_observer)) {}
1143
1144 // SetLocalDescriptionObserverInterface implementation.
OnSetLocalDescriptionComplete(RTCError error)1145 void OnSetLocalDescriptionComplete(RTCError error) override {
1146 OnSetDescriptionComplete(std::move(error));
1147 }
1148 // SetRemoteDescriptionObserverInterface implementation.
OnSetRemoteDescriptionComplete(RTCError error)1149 void OnSetRemoteDescriptionComplete(RTCError error) override {
1150 OnSetDescriptionComplete(std::move(error));
1151 }
1152
1153 private:
OnSetDescriptionComplete(RTCError error)1154 void OnSetDescriptionComplete(RTCError error) {
1155 if (!handler_)
1156 return;
1157 if (error.ok()) {
1158 handler_->pc_->message_handler()->PostSetSessionDescriptionSuccess(
1159 inner_observer_.get());
1160 } else {
1161 handler_->pc_->message_handler()->PostSetSessionDescriptionFailure(
1162 inner_observer_.get(), std::move(error));
1163 }
1164 }
1165
1166 rtc::WeakPtr<SdpOfferAnswerHandler> handler_;
1167 rtc::scoped_refptr<SetSessionDescriptionObserver> inner_observer_;
1168 };
1169
1170 class SdpOfferAnswerHandler::LocalIceCredentialsToReplace {
1171 public:
1172 // Sets the ICE credentials that need restarting to the ICE credentials of
1173 // the current and pending descriptions.
SetIceCredentialsFromLocalDescriptions(const SessionDescriptionInterface * current_local_description,const SessionDescriptionInterface * pending_local_description)1174 void SetIceCredentialsFromLocalDescriptions(
1175 const SessionDescriptionInterface* current_local_description,
1176 const SessionDescriptionInterface* pending_local_description) {
1177 ice_credentials_.clear();
1178 if (current_local_description) {
1179 AppendIceCredentialsFromSessionDescription(*current_local_description);
1180 }
1181 if (pending_local_description) {
1182 AppendIceCredentialsFromSessionDescription(*pending_local_description);
1183 }
1184 }
1185
ClearIceCredentials()1186 void ClearIceCredentials() { ice_credentials_.clear(); }
1187
1188 // Returns true if we have ICE credentials that need restarting.
HasIceCredentials() const1189 bool HasIceCredentials() const { return !ice_credentials_.empty(); }
1190
1191 // Returns true if `local_description` shares no ICE credentials with the
1192 // ICE credentials that need restarting.
SatisfiesIceRestart(const SessionDescriptionInterface & local_description) const1193 bool SatisfiesIceRestart(
1194 const SessionDescriptionInterface& local_description) const {
1195 for (const auto& transport_info :
1196 local_description.description()->transport_infos()) {
1197 if (ice_credentials_.find(std::make_pair(
1198 transport_info.description.ice_ufrag,
1199 transport_info.description.ice_pwd)) != ice_credentials_.end()) {
1200 return false;
1201 }
1202 }
1203 return true;
1204 }
1205
1206 private:
AppendIceCredentialsFromSessionDescription(const SessionDescriptionInterface & desc)1207 void AppendIceCredentialsFromSessionDescription(
1208 const SessionDescriptionInterface& desc) {
1209 for (const auto& transport_info : desc.description()->transport_infos()) {
1210 ice_credentials_.insert(
1211 std::make_pair(transport_info.description.ice_ufrag,
1212 transport_info.description.ice_pwd));
1213 }
1214 }
1215
1216 std::set<std::pair<std::string, std::string>> ice_credentials_;
1217 };
1218
SdpOfferAnswerHandler(PeerConnectionSdpMethods * pc,ConnectionContext * context)1219 SdpOfferAnswerHandler::SdpOfferAnswerHandler(PeerConnectionSdpMethods* pc,
1220 ConnectionContext* context)
1221 : pc_(pc),
1222 context_(context),
1223 local_streams_(StreamCollection::Create()),
1224 remote_streams_(StreamCollection::Create()),
1225 operations_chain_(rtc::OperationsChain::Create()),
1226 rtcp_cname_(GenerateRtcpCname()),
1227 local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()),
1228 weak_ptr_factory_(this) {
1229 operations_chain_->SetOnChainEmptyCallback(
1230 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr()]() {
1231 if (!this_weak_ptr)
1232 return;
1233 this_weak_ptr->OnOperationsChainEmpty();
1234 });
1235 }
1236
~SdpOfferAnswerHandler()1237 SdpOfferAnswerHandler::~SdpOfferAnswerHandler() {}
1238
1239 // Static
Create(PeerConnectionSdpMethods * pc,const PeerConnectionInterface::RTCConfiguration & configuration,PeerConnectionDependencies & dependencies,ConnectionContext * context)1240 std::unique_ptr<SdpOfferAnswerHandler> SdpOfferAnswerHandler::Create(
1241 PeerConnectionSdpMethods* pc,
1242 const PeerConnectionInterface::RTCConfiguration& configuration,
1243 PeerConnectionDependencies& dependencies,
1244 ConnectionContext* context) {
1245 auto handler = absl::WrapUnique(new SdpOfferAnswerHandler(pc, context));
1246 handler->Initialize(configuration, dependencies, context);
1247 return handler;
1248 }
1249
Initialize(const PeerConnectionInterface::RTCConfiguration & configuration,PeerConnectionDependencies & dependencies,ConnectionContext * context)1250 void SdpOfferAnswerHandler::Initialize(
1251 const PeerConnectionInterface::RTCConfiguration& configuration,
1252 PeerConnectionDependencies& dependencies,
1253 ConnectionContext* context) {
1254 RTC_DCHECK_RUN_ON(signaling_thread());
1255 // 100 kbps is used by default, but can be overriden by a non-standard
1256 // RTCConfiguration value (not available on Web).
1257 video_options_.screencast_min_bitrate_kbps =
1258 configuration.screencast_min_bitrate.value_or(100);
1259 audio_options_.combined_audio_video_bwe =
1260 configuration.combined_audio_video_bwe;
1261
1262 audio_options_.audio_jitter_buffer_max_packets =
1263 configuration.audio_jitter_buffer_max_packets;
1264
1265 audio_options_.audio_jitter_buffer_fast_accelerate =
1266 configuration.audio_jitter_buffer_fast_accelerate;
1267
1268 audio_options_.audio_jitter_buffer_min_delay_ms =
1269 configuration.audio_jitter_buffer_min_delay_ms;
1270
1271 // Obtain a certificate from RTCConfiguration if any were provided (optional).
1272 rtc::scoped_refptr<rtc::RTCCertificate> certificate;
1273 if (!configuration.certificates.empty()) {
1274 // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of
1275 // just picking the first one. The decision should be made based on the DTLS
1276 // handshake. The DTLS negotiations need to know about all certificates.
1277 certificate = configuration.certificates[0];
1278 }
1279
1280 webrtc_session_desc_factory_ =
1281 std::make_unique<WebRtcSessionDescriptionFactory>(
1282 context, this, pc_->session_id(), pc_->dtls_enabled(),
1283 std::move(dependencies.cert_generator), std::move(certificate),
1284 [this](const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
1285 RTC_DCHECK_RUN_ON(signaling_thread());
1286 transport_controller_s()->SetLocalCertificate(certificate);
1287 },
1288 pc_->trials());
1289
1290 if (pc_->options()->disable_encryption) {
1291 webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
1292 }
1293
1294 webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions(
1295 pc_->GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions);
1296 webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan());
1297
1298 if (dependencies.video_bitrate_allocator_factory) {
1299 video_bitrate_allocator_factory_ =
1300 std::move(dependencies.video_bitrate_allocator_factory);
1301 } else {
1302 video_bitrate_allocator_factory_ =
1303 CreateBuiltinVideoBitrateAllocatorFactory();
1304 }
1305 }
1306
1307 // ==================================================================
1308 // Access to pc_ variables
media_engine() const1309 cricket::MediaEngineInterface* SdpOfferAnswerHandler::media_engine() const {
1310 RTC_DCHECK(context_);
1311 return context_->media_engine();
1312 }
1313
transceivers()1314 TransceiverList* SdpOfferAnswerHandler::transceivers() {
1315 if (!pc_->rtp_manager()) {
1316 return nullptr;
1317 }
1318 return pc_->rtp_manager()->transceivers();
1319 }
1320
transceivers() const1321 const TransceiverList* SdpOfferAnswerHandler::transceivers() const {
1322 if (!pc_->rtp_manager()) {
1323 return nullptr;
1324 }
1325 return pc_->rtp_manager()->transceivers();
1326 }
transport_controller_s()1327 JsepTransportController* SdpOfferAnswerHandler::transport_controller_s() {
1328 return pc_->transport_controller_s();
1329 }
transport_controller_n()1330 JsepTransportController* SdpOfferAnswerHandler::transport_controller_n() {
1331 return pc_->transport_controller_n();
1332 }
transport_controller_s() const1333 const JsepTransportController* SdpOfferAnswerHandler::transport_controller_s()
1334 const {
1335 return pc_->transport_controller_s();
1336 }
transport_controller_n() const1337 const JsepTransportController* SdpOfferAnswerHandler::transport_controller_n()
1338 const {
1339 return pc_->transport_controller_n();
1340 }
data_channel_controller()1341 DataChannelController* SdpOfferAnswerHandler::data_channel_controller() {
1342 return pc_->data_channel_controller();
1343 }
data_channel_controller() const1344 const DataChannelController* SdpOfferAnswerHandler::data_channel_controller()
1345 const {
1346 return pc_->data_channel_controller();
1347 }
port_allocator()1348 cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() {
1349 return pc_->port_allocator();
1350 }
port_allocator() const1351 const cricket::PortAllocator* SdpOfferAnswerHandler::port_allocator() const {
1352 return pc_->port_allocator();
1353 }
rtp_manager()1354 RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() {
1355 return pc_->rtp_manager();
1356 }
rtp_manager() const1357 const RtpTransmissionManager* SdpOfferAnswerHandler::rtp_manager() const {
1358 return pc_->rtp_manager();
1359 }
1360
1361 // ===================================================================
1362
PrepareForShutdown()1363 void SdpOfferAnswerHandler::PrepareForShutdown() {
1364 RTC_DCHECK_RUN_ON(signaling_thread());
1365 weak_ptr_factory_.InvalidateWeakPtrs();
1366 }
1367
Close()1368 void SdpOfferAnswerHandler::Close() {
1369 ChangeSignalingState(PeerConnectionInterface::kClosed);
1370 }
1371
RestartIce()1372 void SdpOfferAnswerHandler::RestartIce() {
1373 RTC_DCHECK_RUN_ON(signaling_thread());
1374 local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions(
1375 current_local_description(), pending_local_description());
1376 UpdateNegotiationNeeded();
1377 }
1378
signaling_thread() const1379 rtc::Thread* SdpOfferAnswerHandler::signaling_thread() const {
1380 return context_->signaling_thread();
1381 }
1382
network_thread() const1383 rtc::Thread* SdpOfferAnswerHandler::network_thread() const {
1384 return context_->network_thread();
1385 }
1386
CreateOffer(CreateSessionDescriptionObserver * observer,const PeerConnectionInterface::RTCOfferAnswerOptions & options)1387 void SdpOfferAnswerHandler::CreateOffer(
1388 CreateSessionDescriptionObserver* observer,
1389 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
1390 RTC_DCHECK_RUN_ON(signaling_thread());
1391 // Chain this operation. If asynchronous operations are pending on the chain,
1392 // this operation will be queued to be invoked, otherwise the contents of the
1393 // lambda will execute immediately.
1394 operations_chain_->ChainOperation(
1395 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1396 observer_refptr =
1397 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
1398 options](std::function<void()> operations_chain_callback) {
1399 // Abort early if `this_weak_ptr` is no longer valid.
1400 if (!this_weak_ptr) {
1401 observer_refptr->OnFailure(
1402 RTCError(RTCErrorType::INTERNAL_ERROR,
1403 "CreateOffer failed because the session was shut down"));
1404 operations_chain_callback();
1405 return;
1406 }
1407 // The operation completes asynchronously when the wrapper is invoked.
1408 auto observer_wrapper = rtc::make_ref_counted<
1409 CreateSessionDescriptionObserverOperationWrapper>(
1410 std::move(observer_refptr), std::move(operations_chain_callback));
1411 this_weak_ptr->DoCreateOffer(options, observer_wrapper);
1412 });
1413 }
1414
SetLocalDescription(SetSessionDescriptionObserver * observer,SessionDescriptionInterface * desc_ptr)1415 void SdpOfferAnswerHandler::SetLocalDescription(
1416 SetSessionDescriptionObserver* observer,
1417 SessionDescriptionInterface* desc_ptr) {
1418 RTC_DCHECK_RUN_ON(signaling_thread());
1419 // Chain this operation. If asynchronous operations are pending on the chain,
1420 // this operation will be queued to be invoked, otherwise the contents of the
1421 // lambda will execute immediately.
1422 operations_chain_->ChainOperation(
1423 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1424 observer_refptr =
1425 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1426 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1427 std::function<void()> operations_chain_callback) mutable {
1428 // Abort early if `this_weak_ptr` is no longer valid.
1429 if (!this_weak_ptr) {
1430 // For consistency with SetSessionDescriptionObserverAdapter whose
1431 // posted messages doesn't get processed when the PC is destroyed, we
1432 // do not inform `observer_refptr` that the operation failed.
1433 operations_chain_callback();
1434 return;
1435 }
1436 // SetSessionDescriptionObserverAdapter takes care of making sure the
1437 // `observer_refptr` is invoked in a posted message.
1438 this_weak_ptr->DoSetLocalDescription(
1439 std::move(desc),
1440 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1441 this_weak_ptr, observer_refptr));
1442 // For backwards-compatability reasons, we declare the operation as
1443 // completed here (rather than in a post), so that the operation chain
1444 // is not blocked by this operation when the observer is invoked. This
1445 // allows the observer to trigger subsequent offer/answer operations
1446 // synchronously if the operation chain is now empty.
1447 operations_chain_callback();
1448 });
1449 }
1450
SetLocalDescription(std::unique_ptr<SessionDescriptionInterface> desc,rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer)1451 void SdpOfferAnswerHandler::SetLocalDescription(
1452 std::unique_ptr<SessionDescriptionInterface> desc,
1453 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1454 RTC_DCHECK_RUN_ON(signaling_thread());
1455 // Chain this operation. If asynchronous operations are pending on the chain,
1456 // this operation will be queued to be invoked, otherwise the contents of the
1457 // lambda will execute immediately.
1458 operations_chain_->ChainOperation(
1459 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1460 desc = std::move(desc)](
1461 std::function<void()> operations_chain_callback) mutable {
1462 // Abort early if `this_weak_ptr` is no longer valid.
1463 if (!this_weak_ptr) {
1464 observer->OnSetLocalDescriptionComplete(RTCError(
1465 RTCErrorType::INTERNAL_ERROR,
1466 "SetLocalDescription failed because the session was shut down"));
1467 operations_chain_callback();
1468 return;
1469 }
1470 this_weak_ptr->DoSetLocalDescription(std::move(desc), observer);
1471 // DoSetLocalDescription() is implemented as a synchronous operation.
1472 // The `observer` will already have been informed that it completed, and
1473 // we can mark this operation as complete without any loose ends.
1474 operations_chain_callback();
1475 });
1476 }
1477
SetLocalDescription(SetSessionDescriptionObserver * observer)1478 void SdpOfferAnswerHandler::SetLocalDescription(
1479 SetSessionDescriptionObserver* observer) {
1480 RTC_DCHECK_RUN_ON(signaling_thread());
1481 SetLocalDescription(
1482 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1483 weak_ptr_factory_.GetWeakPtr(),
1484 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer)));
1485 }
1486
SetLocalDescription(rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer)1487 void SdpOfferAnswerHandler::SetLocalDescription(
1488 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
1489 RTC_DCHECK_RUN_ON(signaling_thread());
1490 // The `create_sdp_observer` handles performing DoSetLocalDescription() with
1491 // the resulting description as well as completing the operation.
1492 auto create_sdp_observer =
1493 rtc::make_ref_counted<ImplicitCreateSessionDescriptionObserver>(
1494 weak_ptr_factory_.GetWeakPtr(), observer);
1495 // Chain this operation. If asynchronous operations are pending on the chain,
1496 // this operation will be queued to be invoked, otherwise the contents of the
1497 // lambda will execute immediately.
1498 operations_chain_->ChainOperation(
1499 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1500 create_sdp_observer](std::function<void()> operations_chain_callback) {
1501 // The `create_sdp_observer` is responsible for completing the
1502 // operation.
1503 create_sdp_observer->SetOperationCompleteCallback(
1504 std::move(operations_chain_callback));
1505 // Abort early if `this_weak_ptr` is no longer valid. This triggers the
1506 // same code path as if DoCreateOffer() or DoCreateAnswer() failed.
1507 if (!this_weak_ptr) {
1508 create_sdp_observer->OnFailure(RTCError(
1509 RTCErrorType::INTERNAL_ERROR,
1510 "SetLocalDescription failed because the session was shut down"));
1511 return;
1512 }
1513 switch (this_weak_ptr->signaling_state()) {
1514 case PeerConnectionInterface::kStable:
1515 case PeerConnectionInterface::kHaveLocalOffer:
1516 case PeerConnectionInterface::kHaveRemotePrAnswer:
1517 // TODO(hbos): If [LastCreatedOffer] exists and still represents the
1518 // current state of the system, use that instead of creating another
1519 // offer.
1520 this_weak_ptr->DoCreateOffer(
1521 PeerConnectionInterface::RTCOfferAnswerOptions(),
1522 create_sdp_observer);
1523 break;
1524 case PeerConnectionInterface::kHaveLocalPrAnswer:
1525 case PeerConnectionInterface::kHaveRemoteOffer:
1526 // TODO(hbos): If [LastCreatedAnswer] exists and still represents
1527 // the current state of the system, use that instead of creating
1528 // another answer.
1529 this_weak_ptr->DoCreateAnswer(
1530 PeerConnectionInterface::RTCOfferAnswerOptions(),
1531 create_sdp_observer);
1532 break;
1533 case PeerConnectionInterface::kClosed:
1534 create_sdp_observer->OnFailure(RTCError(
1535 RTCErrorType::INVALID_STATE,
1536 "SetLocalDescription called when PeerConnection is closed."));
1537 break;
1538 }
1539 });
1540 }
1541
ApplyLocalDescription(std::unique_ptr<SessionDescriptionInterface> desc,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)1542 RTCError SdpOfferAnswerHandler::ApplyLocalDescription(
1543 std::unique_ptr<SessionDescriptionInterface> desc,
1544 const std::map<std::string, const cricket::ContentGroup*>&
1545 bundle_groups_by_mid) {
1546 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyLocalDescription");
1547 RTC_DCHECK_RUN_ON(signaling_thread());
1548 RTC_DCHECK(desc);
1549
1550 // Invalidate the stats caches to make sure that they get
1551 // updated the next time getStats() gets called, as updating the session
1552 // description affects the stats.
1553 pc_->ClearStatsCache();
1554
1555 // Take a reference to the old local description since it's used below to
1556 // compare against the new local description. When setting the new local
1557 // description, grab ownership of the replaced session description in case it
1558 // is the same as `old_local_description`, to keep it alive for the duration
1559 // of the method.
1560 const SessionDescriptionInterface* old_local_description =
1561 local_description();
1562 std::unique_ptr<SessionDescriptionInterface> replaced_local_description;
1563 SdpType type = desc->GetType();
1564 if (type == SdpType::kAnswer) {
1565 replaced_local_description = pending_local_description_
1566 ? std::move(pending_local_description_)
1567 : std::move(current_local_description_);
1568 current_local_description_ = std::move(desc);
1569 pending_local_description_ = nullptr;
1570 current_remote_description_ = std::move(pending_remote_description_);
1571 } else {
1572 replaced_local_description = std::move(pending_local_description_);
1573 pending_local_description_ = std::move(desc);
1574 }
1575 if (!initial_offerer_) {
1576 initial_offerer_.emplace(type == SdpType::kOffer);
1577 }
1578 // The session description to apply now must be accessed by
1579 // `local_description()`.
1580 RTC_DCHECK(local_description());
1581
1582 // Report statistics about any use of simulcast.
1583 ReportSimulcastApiVersion(kSimulcastVersionApplyLocalDescription,
1584 *local_description()->description());
1585
1586 if (!is_caller_) {
1587 if (remote_description()) {
1588 // Remote description was applied first, so this PC is the callee.
1589 is_caller_ = false;
1590 } else {
1591 // Local description is applied first, so this PC is the caller.
1592 is_caller_ = true;
1593 }
1594 }
1595
1596 RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type);
1597 if (!error.ok()) {
1598 return error;
1599 }
1600
1601 if (IsUnifiedPlan()) {
1602 error = UpdateTransceiversAndDataChannels(
1603 cricket::CS_LOCAL, *local_description(), old_local_description,
1604 remote_description(), bundle_groups_by_mid);
1605 if (!error.ok()) {
1606 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1607 << ")";
1608 return error;
1609 }
1610 if (ConfiguredForMedia()) {
1611 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
1612 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
1613 for (const auto& transceiver_ext : transceivers()->List()) {
1614 auto transceiver = transceiver_ext->internal();
1615 if (transceiver->stopped()) {
1616 continue;
1617 }
1618
1619 // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots.
1620 // Note that code paths that don't set MID won't be able to use
1621 // information about DTLS transports.
1622 if (transceiver->mid()) {
1623 auto dtls_transport = LookupDtlsTransportByMid(
1624 context_->network_thread(), transport_controller_s(),
1625 *transceiver->mid());
1626 transceiver->sender_internal()->set_transport(dtls_transport);
1627 transceiver->receiver_internal()->set_transport(dtls_transport);
1628 }
1629
1630 const ContentInfo* content =
1631 FindMediaSectionForTransceiver(transceiver, local_description());
1632 if (!content) {
1633 continue;
1634 }
1635 const MediaContentDescription* media_desc =
1636 content->media_description();
1637 // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run
1638 // the following steps:
1639 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
1640 // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and
1641 // transceiver's [[FiredDirection]] slot is either "sendrecv" or
1642 // "recvonly", process the removal of a remote track for the media
1643 // description, given transceiver, removeList, and muteTracks.
1644 if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
1645 (transceiver->fired_direction() &&
1646 RtpTransceiverDirectionHasRecv(
1647 *transceiver->fired_direction()))) {
1648 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
1649 &removed_streams);
1650 }
1651 // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and
1652 // [[FiredDirection]] slots to direction.
1653 transceiver->set_current_direction(media_desc->direction());
1654 transceiver->set_fired_direction(media_desc->direction());
1655 }
1656 }
1657 auto observer = pc_->Observer();
1658 for (const auto& transceiver : remove_list) {
1659 observer->OnRemoveTrack(transceiver->receiver());
1660 }
1661 for (const auto& stream : removed_streams) {
1662 observer->OnRemoveStream(stream);
1663 }
1664 }
1665 } else {
1666 // Media channels will be created only when offer is set. These may use new
1667 // transports just created by PushdownTransportDescription.
1668 if (type == SdpType::kOffer) {
1669 // TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local
1670 // description is applied. Restore back to old description.
1671 RTCError error = CreateChannels(*local_description()->description());
1672 if (!error.ok()) {
1673 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1674 << ")";
1675 return error;
1676 }
1677 }
1678 // Remove unused channels if MediaContentDescription is rejected.
1679 RemoveUnusedChannels(local_description()->description());
1680 }
1681
1682 error = UpdateSessionState(type, cricket::CS_LOCAL,
1683 local_description()->description(),
1684 bundle_groups_by_mid);
1685 if (!error.ok()) {
1686 RTC_LOG(LS_ERROR) << error.message() << " (" << SdpTypeToString(type)
1687 << ")";
1688 return error;
1689 }
1690
1691 // Now that we have a local description, we can push down remote candidates.
1692 UseCandidatesInRemoteDescription();
1693
1694 pending_ice_restarts_.clear();
1695 if (session_error() != SessionError::kNone) {
1696 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg());
1697 }
1698
1699 // If setting the description decided our SSL role, allocate any necessary
1700 // SCTP sids.
1701 rtc::SSLRole role;
1702 if (pc_->GetSctpSslRole(&role)) {
1703 data_channel_controller()->AllocateSctpSids(role);
1704 }
1705
1706 if (IsUnifiedPlan()) {
1707 if (ConfiguredForMedia()) {
1708 // We must use List and not ListInternal here because
1709 // transceivers()->StableState() is indexed by the non-internal refptr.
1710 for (const auto& transceiver_ext : transceivers()->List()) {
1711 auto transceiver = transceiver_ext->internal();
1712 if (transceiver->stopped()) {
1713 continue;
1714 }
1715 const ContentInfo* content =
1716 FindMediaSectionForTransceiver(transceiver, local_description());
1717 if (!content) {
1718 continue;
1719 }
1720 cricket::ChannelInterface* channel = transceiver->channel();
1721 if (content->rejected || !channel || channel->local_streams().empty()) {
1722 // 0 is a special value meaning "this sender has no associated send
1723 // stream". Need to call this so the sender won't attempt to configure
1724 // a no longer existing stream and run into DCHECKs in the lower
1725 // layers.
1726 transceiver->sender_internal()->SetSsrc(0);
1727 } else {
1728 // Get the StreamParams from the channel which could generate SSRCs.
1729 const std::vector<StreamParams>& streams = channel->local_streams();
1730 transceiver->sender_internal()->set_stream_ids(
1731 streams[0].stream_ids());
1732 auto encodings =
1733 transceiver->sender_internal()->init_send_encodings();
1734 transceiver->sender_internal()->SetSsrc(streams[0].first_ssrc());
1735 if (!encodings.empty()) {
1736 transceivers()
1737 ->StableState(transceiver_ext)
1738 ->SetInitSendEncodings(encodings);
1739 }
1740 }
1741 }
1742 }
1743 } else {
1744 // Plan B semantics.
1745
1746 // Update state and SSRC of local MediaStreams and DataChannels based on the
1747 // local session description.
1748 const cricket::ContentInfo* audio_content =
1749 GetFirstAudioContent(local_description()->description());
1750 if (audio_content) {
1751 if (audio_content->rejected) {
1752 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
1753 } else {
1754 const cricket::AudioContentDescription* audio_desc =
1755 audio_content->media_description()->as_audio();
1756 UpdateLocalSenders(audio_desc->streams(), audio_desc->type());
1757 }
1758 }
1759
1760 const cricket::ContentInfo* video_content =
1761 GetFirstVideoContent(local_description()->description());
1762 if (video_content) {
1763 if (video_content->rejected) {
1764 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
1765 } else {
1766 const cricket::VideoContentDescription* video_desc =
1767 video_content->media_description()->as_video();
1768 UpdateLocalSenders(video_desc->streams(), video_desc->type());
1769 }
1770 }
1771 }
1772
1773 // This function does nothing with data content.
1774
1775 if (type == SdpType::kAnswer &&
1776 local_ice_credentials_to_replace_->SatisfiesIceRestart(
1777 *current_local_description_)) {
1778 local_ice_credentials_to_replace_->ClearIceCredentials();
1779 }
1780
1781 return RTCError::OK();
1782 }
1783
SetRemoteDescription(SetSessionDescriptionObserver * observer,SessionDescriptionInterface * desc_ptr)1784 void SdpOfferAnswerHandler::SetRemoteDescription(
1785 SetSessionDescriptionObserver* observer,
1786 SessionDescriptionInterface* desc_ptr) {
1787 RTC_DCHECK_RUN_ON(signaling_thread());
1788 // Chain this operation. If asynchronous operations are pending on the chain,
1789 // this operation will be queued to be invoked, otherwise the contents of the
1790 // lambda will execute immediately.
1791 operations_chain_->ChainOperation(
1792 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
1793 observer_refptr =
1794 rtc::scoped_refptr<SetSessionDescriptionObserver>(observer),
1795 desc = std::unique_ptr<SessionDescriptionInterface>(desc_ptr)](
1796 std::function<void()> operations_chain_callback) mutable {
1797 // Abort early if `this_weak_ptr` is no longer valid.
1798 if (!this_weak_ptr) {
1799 // For consistency with SetSessionDescriptionObserverAdapter whose
1800 // posted messages doesn't get processed when the PC is destroyed, we
1801 // do not inform `observer_refptr` that the operation failed.
1802 operations_chain_callback();
1803 return;
1804 }
1805 // SetSessionDescriptionObserverAdapter takes care of making sure the
1806 // `observer_refptr` is invoked in a posted message.
1807 this_weak_ptr->DoSetRemoteDescription(
1808 std::make_unique<RemoteDescriptionOperation>(
1809 this_weak_ptr.get(), std::move(desc),
1810 rtc::make_ref_counted<SetSessionDescriptionObserverAdapter>(
1811 this_weak_ptr, observer_refptr),
1812 std::move(operations_chain_callback)));
1813 });
1814 }
1815
SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc,rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer)1816 void SdpOfferAnswerHandler::SetRemoteDescription(
1817 std::unique_ptr<SessionDescriptionInterface> desc,
1818 rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer) {
1819 RTC_DCHECK_RUN_ON(signaling_thread());
1820 // Chain this operation. If asynchronous operations are pending on the chain,
1821 // this operation will be queued to be invoked, otherwise the contents of the
1822 // lambda will execute immediately.
1823 operations_chain_->ChainOperation(
1824 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer,
1825 desc = std::move(desc)](
1826 std::function<void()> operations_chain_callback) mutable {
1827 if (!observer) {
1828 RTC_DLOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
1829 operations_chain_callback();
1830 return;
1831 }
1832
1833 // Abort early if `this_weak_ptr` is no longer valid.
1834 if (!this_weak_ptr) {
1835 observer->OnSetRemoteDescriptionComplete(RTCError(
1836 RTCErrorType::INTERNAL_ERROR,
1837 "SetRemoteDescription failed because the session was shut down"));
1838 operations_chain_callback();
1839 return;
1840 }
1841
1842 this_weak_ptr->DoSetRemoteDescription(
1843 std::make_unique<RemoteDescriptionOperation>(
1844 this_weak_ptr.get(), std::move(desc), std::move(observer),
1845 std::move(operations_chain_callback)));
1846 });
1847 }
1848
ReplaceRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc,SdpType sdp_type,std::unique_ptr<SessionDescriptionInterface> * replaced_description)1849 RTCError SdpOfferAnswerHandler::ReplaceRemoteDescription(
1850 std::unique_ptr<SessionDescriptionInterface> desc,
1851 SdpType sdp_type,
1852 std::unique_ptr<SessionDescriptionInterface>* replaced_description) {
1853 RTC_DCHECK(replaced_description);
1854 if (sdp_type == SdpType::kAnswer) {
1855 *replaced_description = pending_remote_description_
1856 ? std::move(pending_remote_description_)
1857 : std::move(current_remote_description_);
1858 current_remote_description_ = std::move(desc);
1859 pending_remote_description_ = nullptr;
1860 current_local_description_ = std::move(pending_local_description_);
1861 } else {
1862 *replaced_description = std::move(pending_remote_description_);
1863 pending_remote_description_ = std::move(desc);
1864 }
1865
1866 // The session description to apply now must be accessed by
1867 // `remote_description()`.
1868 const cricket::SessionDescription* session_desc =
1869 remote_description()->description();
1870
1871 // Report statistics about any use of simulcast.
1872 ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription,
1873 *session_desc);
1874
1875 // NOTE: This will perform a BlockingCall() to the network thread.
1876 return transport_controller_s()->SetRemoteDescription(sdp_type, session_desc);
1877 }
1878
ApplyRemoteDescription(std::unique_ptr<RemoteDescriptionOperation> operation)1879 void SdpOfferAnswerHandler::ApplyRemoteDescription(
1880 std::unique_ptr<RemoteDescriptionOperation> operation) {
1881 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ApplyRemoteDescription");
1882 RTC_DCHECK_RUN_ON(signaling_thread());
1883 RTC_DCHECK(operation->description());
1884
1885 // Invalidate the stats caches to make sure that they get
1886 // updated next time getStats() gets called, as updating the session
1887 // description affects the stats.
1888 pc_->ClearStatsCache();
1889
1890 if (!operation->ReplaceRemoteDescriptionAndCheckEror())
1891 return;
1892
1893 if (!operation->UpdateChannels())
1894 return;
1895
1896 // NOTE: Candidates allocation will be initiated only when
1897 // SetLocalDescription is called.
1898 if (!operation->UpdateSessionState())
1899 return;
1900
1901 if (!operation->UseCandidatesInRemoteDescription())
1902 return;
1903
1904 if (operation->old_remote_description()) {
1905 for (const cricket::ContentInfo& content :
1906 operation->old_remote_description()->description()->contents()) {
1907 // Check if this new SessionDescription contains new ICE ufrag and
1908 // password that indicates the remote peer requests an ICE restart.
1909 // TODO(deadbeef): When we start storing both the current and pending
1910 // remote description, this should reset pending_ice_restarts and compare
1911 // against the current description.
1912 if (CheckForRemoteIceRestart(operation->old_remote_description(),
1913 remote_description(), content.name)) {
1914 if (operation->type() == SdpType::kOffer) {
1915 pending_ice_restarts_.insert(content.name);
1916 }
1917 } else {
1918 // We retain all received candidates only if ICE is not restarted.
1919 // When ICE is restarted, all previous candidates belong to an old
1920 // generation and should not be kept.
1921 // TODO(deadbeef): This goes against the W3C spec which says the remote
1922 // description should only contain candidates from the last set remote
1923 // description plus any candidates added since then. We should remove
1924 // this once we're sure it won't break anything.
1925 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
1926 operation->old_remote_description(), content.name,
1927 mutable_remote_description());
1928 }
1929 }
1930 }
1931
1932 if (operation->HaveSessionError())
1933 return;
1934
1935 // Set the the ICE connection state to connecting since the connection may
1936 // become writable with peer reflexive candidates before any remote candidate
1937 // is signaled.
1938 // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix
1939 // is to have a new signal the indicates a change in checking state from the
1940 // transport and expose a new checking() member from transport that can be
1941 // read to determine the current checking state. The existing SignalConnecting
1942 // actually means "gathering candidates", so cannot be be used here.
1943 if (remote_description()->GetType() != SdpType::kOffer &&
1944 remote_description()->number_of_mediasections() > 0u &&
1945 pc_->ice_connection_state_internal() ==
1946 PeerConnectionInterface::kIceConnectionNew) {
1947 pc_->SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1948 }
1949
1950 // If setting the description decided our SSL role, allocate any necessary
1951 // SCTP sids.
1952 rtc::SSLRole role;
1953 if (pc_->GetSctpSslRole(&role)) {
1954 data_channel_controller()->AllocateSctpSids(role);
1955 }
1956
1957 if (operation->unified_plan()) {
1958 ApplyRemoteDescriptionUpdateTransceiverState(operation->type());
1959 }
1960
1961 const cricket::AudioContentDescription* audio_desc =
1962 GetFirstAudioContentDescription(remote_description()->description());
1963 const cricket::VideoContentDescription* video_desc =
1964 GetFirstVideoContentDescription(remote_description()->description());
1965
1966 // Check if the descriptions include streams, just in case the peer supports
1967 // MSID, but doesn't indicate so with "a=msid-semantic".
1968 if (remote_description()->description()->msid_supported() ||
1969 (audio_desc && !audio_desc->streams().empty()) ||
1970 (video_desc && !video_desc->streams().empty())) {
1971 remote_peer_supports_msid_ = true;
1972 }
1973
1974 if (!operation->unified_plan()) {
1975 PlanBUpdateSendersAndReceivers(
1976 GetFirstAudioContent(remote_description()->description()), audio_desc,
1977 GetFirstVideoContent(remote_description()->description()), video_desc);
1978 }
1979
1980 if (operation->type() == SdpType::kAnswer) {
1981 if (local_ice_credentials_to_replace_->SatisfiesIceRestart(
1982 *current_local_description_)) {
1983 local_ice_credentials_to_replace_->ClearIceCredentials();
1984 }
1985
1986 RemoveStoppedTransceivers();
1987 }
1988
1989 // Consider the operation complete at this point.
1990 operation->SignalCompletion();
1991
1992 SetRemoteDescriptionPostProcess(operation->type() == SdpType::kAnswer);
1993 }
1994
ApplyRemoteDescriptionUpdateTransceiverState(SdpType sdp_type)1995 void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState(
1996 SdpType sdp_type) {
1997 RTC_DCHECK_RUN_ON(signaling_thread());
1998 RTC_DCHECK(IsUnifiedPlan());
1999 if (!ConfiguredForMedia()) {
2000 return;
2001 }
2002 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
2003 now_receiving_transceivers;
2004 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
2005 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
2006 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
2007 for (const auto& transceiver_ext : transceivers()->List()) {
2008 const auto transceiver = transceiver_ext->internal();
2009 const ContentInfo* content =
2010 FindMediaSectionForTransceiver(transceiver, remote_description());
2011 if (!content) {
2012 continue;
2013 }
2014 const MediaContentDescription* media_desc = content->media_description();
2015 RtpTransceiverDirection local_direction =
2016 RtpTransceiverDirectionReversed(media_desc->direction());
2017 // Remember the previous remote streams if this is a remote offer. This
2018 // makes it possible to rollback modifications to the streams.
2019 if (sdp_type == SdpType::kOffer) {
2020 transceivers()
2021 ->StableState(transceiver_ext)
2022 ->SetRemoteStreamIds(transceiver->receiver()->stream_ids());
2023 }
2024 // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the
2025 // RTCSessionDescription: Set the associated remote streams given
2026 // transceiver.[[Receiver]], msids, addList, and removeList".
2027 // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription
2028 if (RtpTransceiverDirectionHasRecv(local_direction)) {
2029 std::vector<std::string> stream_ids;
2030 if (!media_desc->streams().empty()) {
2031 // The remote description has signaled the stream IDs.
2032 stream_ids = media_desc->streams()[0].stream_ids();
2033 }
2034
2035 RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name
2036 << " (" << GetStreamIdsString(stream_ids) << ").";
2037 SetAssociatedRemoteStreams(transceiver->receiver_internal(), stream_ids,
2038 &added_streams, &removed_streams);
2039 // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6
2040 // "Set the RTCSessionDescription: If direction is sendrecv or recvonly,
2041 // and transceiver's current direction is neither sendrecv nor recvonly,
2042 // process the addition of a remote track for the media description.
2043 if (!transceiver->fired_direction() ||
2044 !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) {
2045 RTC_LOG(LS_INFO) << "Processing the addition of a remote track for MID="
2046 << content->name << ".";
2047 // Since the transceiver is passed to the user in an
2048 // OnTrack event, we must use the proxied transceiver.
2049 now_receiving_transceivers.push_back(transceiver_ext);
2050 }
2051 }
2052 // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's
2053 // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the
2054 // removal of a remote track for the media description, given transceiver,
2055 // removeList, and muteTracks.
2056 if (!RtpTransceiverDirectionHasRecv(local_direction) &&
2057 (transceiver->fired_direction() &&
2058 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) {
2059 ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list,
2060 &removed_streams);
2061 }
2062 // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction.
2063 if (sdp_type == SdpType::kOffer) {
2064 // Remember the previous fired direction if this is a remote offer. This
2065 // makes it possible to rollback modifications to [[FiredDirection]],
2066 // which is necessary for "ontrack" to fire in or after rollback.
2067 transceivers()
2068 ->StableState(transceiver_ext)
2069 ->SetFiredDirection(transceiver->fired_direction());
2070 }
2071 transceiver->set_fired_direction(local_direction);
2072 // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run
2073 // the following steps:
2074 if (sdp_type == SdpType::kPrAnswer || sdp_type == SdpType::kAnswer) {
2075 // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to
2076 // direction.
2077 transceiver->set_current_direction(local_direction);
2078 // 2.2.8.1.11.[3-6]: Set the transport internal slots.
2079 if (transceiver->mid()) {
2080 auto dtls_transport = LookupDtlsTransportByMid(
2081 context_->network_thread(), transport_controller_s(),
2082 *transceiver->mid());
2083 transceiver->sender_internal()->set_transport(dtls_transport);
2084 transceiver->receiver_internal()->set_transport(dtls_transport);
2085 }
2086 }
2087 // 2.2.8.1.12: If the media description is rejected, and transceiver is
2088 // not already stopped, stop the RTCRtpTransceiver transceiver.
2089 if (content->rejected && !transceiver->stopped()) {
2090 RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name
2091 << " since the media section was rejected.";
2092 transceiver->StopTransceiverProcedure();
2093 }
2094 if (!content->rejected && RtpTransceiverDirectionHasRecv(local_direction)) {
2095 if (!media_desc->streams().empty() &&
2096 media_desc->streams()[0].has_ssrcs()) {
2097 uint32_t ssrc = media_desc->streams()[0].first_ssrc();
2098 transceiver->receiver_internal()->SetupMediaChannel(ssrc);
2099 } else {
2100 transceiver->receiver_internal()->SetupUnsignaledMediaChannel();
2101 }
2102 }
2103 }
2104 // Once all processing has finished, fire off callbacks.
2105 auto observer = pc_->Observer();
2106 for (const auto& transceiver : now_receiving_transceivers) {
2107 pc_->legacy_stats()->AddTrack(transceiver->receiver()->track().get());
2108 observer->OnTrack(transceiver);
2109 observer->OnAddTrack(transceiver->receiver(),
2110 transceiver->receiver()->streams());
2111 }
2112 for (const auto& stream : added_streams) {
2113 observer->OnAddStream(stream);
2114 }
2115 for (const auto& transceiver : remove_list) {
2116 observer->OnRemoveTrack(transceiver->receiver());
2117 }
2118 for (const auto& stream : removed_streams) {
2119 observer->OnRemoveStream(stream);
2120 }
2121 }
2122
PlanBUpdateSendersAndReceivers(const cricket::ContentInfo * audio_content,const cricket::AudioContentDescription * audio_desc,const cricket::ContentInfo * video_content,const cricket::VideoContentDescription * video_desc)2123 void SdpOfferAnswerHandler::PlanBUpdateSendersAndReceivers(
2124 const cricket::ContentInfo* audio_content,
2125 const cricket::AudioContentDescription* audio_desc,
2126 const cricket::ContentInfo* video_content,
2127 const cricket::VideoContentDescription* video_desc) {
2128 RTC_DCHECK_RUN_ON(signaling_thread());
2129 RTC_DCHECK(!IsUnifiedPlan());
2130
2131 // We wait to signal new streams until we finish processing the description,
2132 // since only at that point will new streams have all their tracks.
2133 rtc::scoped_refptr<StreamCollection> new_streams(StreamCollection::Create());
2134
2135 // TODO(steveanton): When removing RTP senders/receivers in response to a
2136 // rejected media section, there is some cleanup logic that expects the
2137 // voice/ video channel to still be set. But in this method the voice/video
2138 // channel would have been destroyed by the SetRemoteDescription caller
2139 // above so the cleanup that relies on them fails to run. The RemoveSenders
2140 // calls should be moved to right before the DestroyChannel calls to fix
2141 // this.
2142
2143 // Find all audio rtp streams and create corresponding remote AudioTracks
2144 // and MediaStreams.
2145 if (audio_content) {
2146 if (audio_content->rejected) {
2147 RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
2148 } else {
2149 bool default_audio_track_needed =
2150 !remote_peer_supports_msid_ &&
2151 RtpTransceiverDirectionHasSend(audio_desc->direction());
2152 UpdateRemoteSendersList(GetActiveStreams(audio_desc),
2153 default_audio_track_needed, audio_desc->type(),
2154 new_streams.get());
2155 }
2156 }
2157
2158 // Find all video rtp streams and create corresponding remote VideoTracks
2159 // and MediaStreams.
2160 if (video_content) {
2161 if (video_content->rejected) {
2162 RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
2163 } else {
2164 bool default_video_track_needed =
2165 !remote_peer_supports_msid_ &&
2166 RtpTransceiverDirectionHasSend(video_desc->direction());
2167 UpdateRemoteSendersList(GetActiveStreams(video_desc),
2168 default_video_track_needed, video_desc->type(),
2169 new_streams.get());
2170 }
2171 }
2172
2173 // Iterate new_streams and notify the observer about new MediaStreams.
2174 auto observer = pc_->Observer();
2175 for (size_t i = 0; i < new_streams->count(); ++i) {
2176 MediaStreamInterface* new_stream = new_streams->at(i);
2177 pc_->legacy_stats()->AddStream(new_stream);
2178 observer->OnAddStream(rtc::scoped_refptr<MediaStreamInterface>(new_stream));
2179 }
2180
2181 UpdateEndedRemoteMediaStreams();
2182 }
2183
DoSetLocalDescription(std::unique_ptr<SessionDescriptionInterface> desc,rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer)2184 void SdpOfferAnswerHandler::DoSetLocalDescription(
2185 std::unique_ptr<SessionDescriptionInterface> desc,
2186 rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
2187 RTC_DCHECK_RUN_ON(signaling_thread());
2188 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetLocalDescription");
2189
2190 if (!observer) {
2191 RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL.";
2192 return;
2193 }
2194
2195 if (!desc) {
2196 observer->OnSetLocalDescriptionComplete(
2197 RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL."));
2198 return;
2199 }
2200
2201 // If a session error has occurred the PeerConnection is in a possibly
2202 // inconsistent state so fail right away.
2203 if (session_error() != SessionError::kNone) {
2204 std::string error_message = GetSessionErrorMsg();
2205 RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message;
2206 observer->OnSetLocalDescriptionComplete(
2207 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2208 return;
2209 }
2210
2211 // For SLD we support only explicit rollback.
2212 if (desc->GetType() == SdpType::kRollback) {
2213 if (IsUnifiedPlan()) {
2214 observer->OnSetLocalDescriptionComplete(Rollback(desc->GetType()));
2215 } else {
2216 observer->OnSetLocalDescriptionComplete(
2217 RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2218 "Rollback not supported in Plan B"));
2219 }
2220 return;
2221 }
2222
2223 std::map<std::string, const cricket::ContentGroup*> bundle_groups_by_mid =
2224 GetBundleGroupsByMid(desc->description());
2225 RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL,
2226 bundle_groups_by_mid);
2227 if (!error.ok()) {
2228 std::string error_message = GetSetDescriptionErrorMessage(
2229 cricket::CS_LOCAL, desc->GetType(), error);
2230 RTC_LOG(LS_ERROR) << error_message;
2231 observer->OnSetLocalDescriptionComplete(
2232 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2233 return;
2234 }
2235
2236 // Grab the description type before moving ownership to ApplyLocalDescription,
2237 // which may destroy it before returning.
2238 const SdpType type = desc->GetType();
2239
2240 error = ApplyLocalDescription(std::move(desc), bundle_groups_by_mid);
2241 // `desc` may be destroyed at this point.
2242
2243 if (!error.ok()) {
2244 // If ApplyLocalDescription fails, the PeerConnection could be in an
2245 // inconsistent state, so act conservatively here and set the session error
2246 // so that future calls to SetLocalDescription/SetRemoteDescription fail.
2247 SetSessionError(SessionError::kContent, error.message());
2248 std::string error_message =
2249 GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error);
2250 RTC_LOG(LS_ERROR) << error_message;
2251 observer->OnSetLocalDescriptionComplete(
2252 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2253 return;
2254 }
2255 RTC_DCHECK(local_description());
2256
2257 if (local_description()->GetType() == SdpType::kAnswer) {
2258 RemoveStoppedTransceivers();
2259
2260 // TODO(deadbeef): We already had to hop to the network thread for
2261 // MaybeStartGathering...
2262 context_->network_thread()->BlockingCall(
2263 [this] { port_allocator()->DiscardCandidatePool(); });
2264 }
2265
2266 observer->OnSetLocalDescriptionComplete(RTCError::OK());
2267 pc_->NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED);
2268
2269 // Check if negotiation is needed. We must do this after informing the
2270 // observer that SetLocalDescription() has completed to ensure negotiation is
2271 // not needed prior to the promise resolving.
2272 if (IsUnifiedPlan()) {
2273 bool was_negotiation_needed = is_negotiation_needed_;
2274 UpdateNegotiationNeeded();
2275 if (signaling_state() == PeerConnectionInterface::kStable &&
2276 was_negotiation_needed && is_negotiation_needed_) {
2277 // Legacy version.
2278 pc_->Observer()->OnRenegotiationNeeded();
2279 // Spec-compliant version; the event may get invalidated before firing.
2280 GenerateNegotiationNeededEvent();
2281 }
2282 }
2283
2284 // MaybeStartGathering needs to be called after informing the observer so that
2285 // we don't signal any candidates before signaling that SetLocalDescription
2286 // completed.
2287 transport_controller_s()->MaybeStartGathering();
2288 }
2289
DoCreateOffer(const PeerConnectionInterface::RTCOfferAnswerOptions & options,rtc::scoped_refptr<CreateSessionDescriptionObserver> observer)2290 void SdpOfferAnswerHandler::DoCreateOffer(
2291 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2292 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2293 RTC_DCHECK_RUN_ON(signaling_thread());
2294 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateOffer");
2295
2296 if (!observer) {
2297 RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
2298 return;
2299 }
2300
2301 if (pc_->IsClosed()) {
2302 std::string error = "CreateOffer called when PeerConnection is closed.";
2303 RTC_LOG(LS_ERROR) << error;
2304 pc_->message_handler()->PostCreateSessionDescriptionFailure(
2305 observer.get(),
2306 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
2307 return;
2308 }
2309
2310 // If a session error has occurred the PeerConnection is in a possibly
2311 // inconsistent state so fail right away.
2312 if (session_error() != SessionError::kNone) {
2313 std::string error_message = GetSessionErrorMsg();
2314 RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message;
2315 pc_->message_handler()->PostCreateSessionDescriptionFailure(
2316 observer.get(),
2317 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2318 return;
2319 }
2320
2321 if (!ValidateOfferAnswerOptions(options)) {
2322 std::string error = "CreateOffer called with invalid options.";
2323 RTC_LOG(LS_ERROR) << error;
2324 pc_->message_handler()->PostCreateSessionDescriptionFailure(
2325 observer.get(),
2326 RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error)));
2327 return;
2328 }
2329
2330 // Legacy handling for offer_to_receive_audio and offer_to_receive_video.
2331 // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions".
2332 if (IsUnifiedPlan()) {
2333 RTCError error = HandleLegacyOfferOptions(options);
2334 if (!error.ok()) {
2335 pc_->message_handler()->PostCreateSessionDescriptionFailure(
2336 observer.get(), std::move(error));
2337 return;
2338 }
2339 }
2340
2341 cricket::MediaSessionOptions session_options;
2342 GetOptionsForOffer(options, &session_options);
2343 webrtc_session_desc_factory_->CreateOffer(observer.get(), options,
2344 session_options);
2345 }
2346
CreateAnswer(CreateSessionDescriptionObserver * observer,const PeerConnectionInterface::RTCOfferAnswerOptions & options)2347 void SdpOfferAnswerHandler::CreateAnswer(
2348 CreateSessionDescriptionObserver* observer,
2349 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
2350 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateAnswer");
2351 RTC_DCHECK_RUN_ON(signaling_thread());
2352 // Chain this operation. If asynchronous operations are pending on the chain,
2353 // this operation will be queued to be invoked, otherwise the contents of the
2354 // lambda will execute immediately.
2355 operations_chain_->ChainOperation(
2356 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2357 observer_refptr =
2358 rtc::scoped_refptr<CreateSessionDescriptionObserver>(observer),
2359 options](std::function<void()> operations_chain_callback) {
2360 // Abort early if `this_weak_ptr` is no longer valid.
2361 if (!this_weak_ptr) {
2362 observer_refptr->OnFailure(RTCError(
2363 RTCErrorType::INTERNAL_ERROR,
2364 "CreateAnswer failed because the session was shut down"));
2365 operations_chain_callback();
2366 return;
2367 }
2368 // The operation completes asynchronously when the wrapper is invoked.
2369 auto observer_wrapper = rtc::make_ref_counted<
2370 CreateSessionDescriptionObserverOperationWrapper>(
2371 std::move(observer_refptr), std::move(operations_chain_callback));
2372 this_weak_ptr->DoCreateAnswer(options, observer_wrapper);
2373 });
2374 }
2375
DoCreateAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions & options,rtc::scoped_refptr<CreateSessionDescriptionObserver> observer)2376 void SdpOfferAnswerHandler::DoCreateAnswer(
2377 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
2378 rtc::scoped_refptr<CreateSessionDescriptionObserver> observer) {
2379 RTC_DCHECK_RUN_ON(signaling_thread());
2380 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoCreateAnswer");
2381 if (!observer) {
2382 RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
2383 return;
2384 }
2385
2386 // If a session error has occurred the PeerConnection is in a possibly
2387 // inconsistent state so fail right away.
2388 if (session_error() != SessionError::kNone) {
2389 std::string error_message = GetSessionErrorMsg();
2390 RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message;
2391 pc_->message_handler()->PostCreateSessionDescriptionFailure(
2392 observer.get(),
2393 RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
2394 return;
2395 }
2396
2397 if (!(signaling_state_ == PeerConnectionInterface::kHaveRemoteOffer ||
2398 signaling_state_ == PeerConnectionInterface::kHaveLocalPrAnswer)) {
2399 std::string error =
2400 "PeerConnection cannot create an answer in a state other than "
2401 "have-remote-offer or have-local-pranswer.";
2402 RTC_LOG(LS_ERROR) << error;
2403 pc_->message_handler()->PostCreateSessionDescriptionFailure(
2404 observer.get(),
2405 RTCError(RTCErrorType::INVALID_STATE, std::move(error)));
2406 return;
2407 }
2408
2409 // The remote description should be set if we're in the right state.
2410 RTC_DCHECK(remote_description());
2411
2412 if (IsUnifiedPlan()) {
2413 if (options.offer_to_receive_audio !=
2414 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2415 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not "
2416 "supported with Unified Plan semantics. Use the "
2417 "RtpTransceiver API instead.";
2418 }
2419 if (options.offer_to_receive_video !=
2420 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
2421 RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not "
2422 "supported with Unified Plan semantics. Use the "
2423 "RtpTransceiver API instead.";
2424 }
2425 }
2426
2427 cricket::MediaSessionOptions session_options;
2428 GetOptionsForAnswer(options, &session_options);
2429 webrtc_session_desc_factory_->CreateAnswer(observer.get(), session_options);
2430 }
2431
DoSetRemoteDescription(std::unique_ptr<RemoteDescriptionOperation> operation)2432 void SdpOfferAnswerHandler::DoSetRemoteDescription(
2433 std::unique_ptr<RemoteDescriptionOperation> operation) {
2434 RTC_DCHECK_RUN_ON(signaling_thread());
2435 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::DoSetRemoteDescription");
2436
2437 if (!operation->ok())
2438 return;
2439
2440 if (operation->HaveSessionError())
2441 return;
2442
2443 if (operation->MaybeRollback())
2444 return;
2445
2446 operation->ReportOfferAnswerUma();
2447
2448 // Handle remote descriptions missing a=mid lines for interop with legacy
2449 // end points.
2450 FillInMissingRemoteMids(operation->description());
2451 if (!operation->IsDescriptionValid())
2452 return;
2453
2454 ApplyRemoteDescription(std::move(operation));
2455 }
2456
2457 // Called after a DoSetRemoteDescription operation completes.
SetRemoteDescriptionPostProcess(bool was_answer)2458 void SdpOfferAnswerHandler::SetRemoteDescriptionPostProcess(bool was_answer) {
2459 RTC_DCHECK(remote_description());
2460
2461 if (was_answer) {
2462 // TODO(deadbeef): We already had to hop to the network thread for
2463 // MaybeStartGathering...
2464 context_->network_thread()->BlockingCall(
2465 [this] { port_allocator()->DiscardCandidatePool(); });
2466 }
2467
2468 pc_->NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED);
2469
2470 // Check if negotiation is needed. We must do this after informing the
2471 // observer that SetRemoteDescription() has completed to ensure negotiation
2472 // is not needed prior to the promise resolving.
2473 if (IsUnifiedPlan()) {
2474 bool was_negotiation_needed = is_negotiation_needed_;
2475 UpdateNegotiationNeeded();
2476 if (signaling_state() == PeerConnectionInterface::kStable &&
2477 was_negotiation_needed && is_negotiation_needed_) {
2478 // Legacy version.
2479 pc_->Observer()->OnRenegotiationNeeded();
2480 // Spec-compliant version; the event may get invalidated before firing.
2481 GenerateNegotiationNeededEvent();
2482 }
2483 }
2484 }
2485
SetAssociatedRemoteStreams(rtc::scoped_refptr<RtpReceiverInternal> receiver,const std::vector<std::string> & stream_ids,std::vector<rtc::scoped_refptr<MediaStreamInterface>> * added_streams,std::vector<rtc::scoped_refptr<MediaStreamInterface>> * removed_streams)2486 void SdpOfferAnswerHandler::SetAssociatedRemoteStreams(
2487 rtc::scoped_refptr<RtpReceiverInternal> receiver,
2488 const std::vector<std::string>& stream_ids,
2489 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* added_streams,
2490 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
2491 RTC_DCHECK_RUN_ON(signaling_thread());
2492 std::vector<rtc::scoped_refptr<MediaStreamInterface>> media_streams;
2493 for (const std::string& stream_id : stream_ids) {
2494 rtc::scoped_refptr<MediaStreamInterface> stream(
2495 remote_streams_->find(stream_id));
2496 if (!stream) {
2497 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
2498 MediaStream::Create(stream_id));
2499 remote_streams_->AddStream(stream);
2500 added_streams->push_back(stream);
2501 }
2502 media_streams.push_back(stream);
2503 }
2504 // Special case: "a=msid" missing, use random stream ID.
2505 if (media_streams.empty() &&
2506 !(remote_description()->description()->msid_signaling() &
2507 cricket::kMsidSignalingMediaSection)) {
2508 if (!missing_msid_default_stream_) {
2509 missing_msid_default_stream_ = MediaStreamProxy::Create(
2510 rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid()));
2511 added_streams->push_back(missing_msid_default_stream_);
2512 }
2513 media_streams.push_back(missing_msid_default_stream_);
2514 }
2515 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
2516 receiver->streams();
2517 // SetStreams() will add/remove the receiver's track to/from the streams.
2518 // This differs from the spec - the spec uses an "addList" and "removeList"
2519 // to update the stream-track relationships in a later step. We do this
2520 // earlier, changing the order of things, but the end-result is the same.
2521 // TODO(hbos): When we remove remote_streams(), use set_stream_ids()
2522 // instead. https://crbug.com/webrtc/9480
2523 receiver->SetStreams(media_streams);
2524 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
2525 }
2526
AddIceCandidate(const IceCandidateInterface * ice_candidate)2527 bool SdpOfferAnswerHandler::AddIceCandidate(
2528 const IceCandidateInterface* ice_candidate) {
2529 const AddIceCandidateResult result = AddIceCandidateInternal(ice_candidate);
2530 NoteAddIceCandidateResult(result);
2531 // If the return value is kAddIceCandidateFailNotReady, the candidate has
2532 // been added, although not 'ready', but that's a success.
2533 return result == kAddIceCandidateSuccess ||
2534 result == kAddIceCandidateFailNotReady;
2535 }
2536
AddIceCandidateInternal(const IceCandidateInterface * ice_candidate)2537 AddIceCandidateResult SdpOfferAnswerHandler::AddIceCandidateInternal(
2538 const IceCandidateInterface* ice_candidate) {
2539 RTC_DCHECK_RUN_ON(signaling_thread());
2540 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
2541 if (pc_->IsClosed()) {
2542 RTC_LOG(LS_ERROR) << "AddIceCandidate: PeerConnection is closed.";
2543 return kAddIceCandidateFailClosed;
2544 }
2545
2546 if (!remote_description()) {
2547 RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added "
2548 "without any remote session description.";
2549 return kAddIceCandidateFailNoRemoteDescription;
2550 }
2551
2552 if (!ice_candidate) {
2553 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null.";
2554 return kAddIceCandidateFailNullCandidate;
2555 }
2556
2557 bool valid = false;
2558 bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid);
2559 if (!valid) {
2560 return kAddIceCandidateFailNotValid;
2561 }
2562
2563 // Add this candidate to the remote session description.
2564 if (!mutable_remote_description()->AddCandidate(ice_candidate)) {
2565 RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used.";
2566 return kAddIceCandidateFailInAddition;
2567 }
2568
2569 if (!ready) {
2570 RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate.";
2571 return kAddIceCandidateFailNotReady;
2572 }
2573
2574 if (!UseCandidate(ice_candidate)) {
2575 return kAddIceCandidateFailNotUsable;
2576 }
2577
2578 pc_->NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED);
2579
2580 return kAddIceCandidateSuccess;
2581 }
2582
AddIceCandidate(std::unique_ptr<IceCandidateInterface> candidate,std::function<void (RTCError)> callback)2583 void SdpOfferAnswerHandler::AddIceCandidate(
2584 std::unique_ptr<IceCandidateInterface> candidate,
2585 std::function<void(RTCError)> callback) {
2586 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AddIceCandidate");
2587 RTC_DCHECK_RUN_ON(signaling_thread());
2588 // Chain this operation. If asynchronous operations are pending on the
2589 // chain, this operation will be queued to be invoked, otherwise the
2590 // contents of the lambda will execute immediately.
2591 operations_chain_->ChainOperation(
2592 [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(),
2593 candidate = std::move(candidate), callback = std::move(callback)](
2594 std::function<void()> operations_chain_callback) {
2595 auto result =
2596 this_weak_ptr
2597 ? this_weak_ptr->AddIceCandidateInternal(candidate.get())
2598 : kAddIceCandidateFailClosed;
2599 NoteAddIceCandidateResult(result);
2600 operations_chain_callback();
2601 switch (result) {
2602 case AddIceCandidateResult::kAddIceCandidateSuccess:
2603 case AddIceCandidateResult::kAddIceCandidateFailNotReady:
2604 // Success!
2605 callback(RTCError::OK());
2606 break;
2607 case AddIceCandidateResult::kAddIceCandidateFailClosed:
2608 // Note that the spec says to just abort without resolving the
2609 // promise in this case, but this layer must return an RTCError.
2610 callback(RTCError(
2611 RTCErrorType::INVALID_STATE,
2612 "AddIceCandidate failed because the session was shut down"));
2613 break;
2614 case AddIceCandidateResult::kAddIceCandidateFailNoRemoteDescription:
2615 // Spec: "If remoteDescription is null return a promise rejected
2616 // with a newly created InvalidStateError."
2617 callback(RTCError(RTCErrorType::INVALID_STATE,
2618 "The remote description was null"));
2619 break;
2620 case AddIceCandidateResult::kAddIceCandidateFailNullCandidate:
2621 // TODO(https://crbug.com/935898): Handle end-of-candidates instead
2622 // of treating null candidate as an error.
2623 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2624 "Error processing ICE candidate"));
2625 break;
2626 case AddIceCandidateResult::kAddIceCandidateFailNotValid:
2627 case AddIceCandidateResult::kAddIceCandidateFailInAddition:
2628 case AddIceCandidateResult::kAddIceCandidateFailNotUsable:
2629 // Spec: "If candidate could not be successfully added [...] Reject
2630 // p with a newly created OperationError and abort these steps."
2631 // UNSUPPORTED_OPERATION maps to OperationError.
2632 callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
2633 "Error processing ICE candidate"));
2634 break;
2635 default:
2636 RTC_DCHECK_NOTREACHED();
2637 }
2638 });
2639 }
2640
RemoveIceCandidates(const std::vector<cricket::Candidate> & candidates)2641 bool SdpOfferAnswerHandler::RemoveIceCandidates(
2642 const std::vector<cricket::Candidate>& candidates) {
2643 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveIceCandidates");
2644 RTC_DCHECK_RUN_ON(signaling_thread());
2645 if (pc_->IsClosed()) {
2646 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: PeerConnection is closed.";
2647 return false;
2648 }
2649
2650 if (!remote_description()) {
2651 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed "
2652 "without any remote session description.";
2653 return false;
2654 }
2655
2656 if (candidates.empty()) {
2657 RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty.";
2658 return false;
2659 }
2660
2661 size_t number_removed =
2662 mutable_remote_description()->RemoveCandidates(candidates);
2663 if (number_removed != candidates.size()) {
2664 RTC_LOG(LS_ERROR)
2665 << "RemoveIceCandidates: Failed to remove candidates. Requested "
2666 << candidates.size() << " but only " << number_removed
2667 << " are removed.";
2668 }
2669
2670 // Remove the candidates from the transport controller.
2671 RTCError error = transport_controller_s()->RemoveRemoteCandidates(candidates);
2672 if (!error.ok()) {
2673 RTC_LOG(LS_ERROR)
2674 << "RemoveIceCandidates: Error when removing remote candidates: "
2675 << error.message();
2676 }
2677 return true;
2678 }
2679
AddLocalIceCandidate(const JsepIceCandidate * candidate)2680 void SdpOfferAnswerHandler::AddLocalIceCandidate(
2681 const JsepIceCandidate* candidate) {
2682 RTC_DCHECK_RUN_ON(signaling_thread());
2683 if (local_description()) {
2684 mutable_local_description()->AddCandidate(candidate);
2685 }
2686 }
2687
RemoveLocalIceCandidates(const std::vector<cricket::Candidate> & candidates)2688 void SdpOfferAnswerHandler::RemoveLocalIceCandidates(
2689 const std::vector<cricket::Candidate>& candidates) {
2690 RTC_DCHECK_RUN_ON(signaling_thread());
2691 if (local_description()) {
2692 mutable_local_description()->RemoveCandidates(candidates);
2693 }
2694 }
2695
local_description() const2696 const SessionDescriptionInterface* SdpOfferAnswerHandler::local_description()
2697 const {
2698 RTC_DCHECK_RUN_ON(signaling_thread());
2699 return pending_local_description_ ? pending_local_description_.get()
2700 : current_local_description_.get();
2701 }
2702
remote_description() const2703 const SessionDescriptionInterface* SdpOfferAnswerHandler::remote_description()
2704 const {
2705 RTC_DCHECK_RUN_ON(signaling_thread());
2706 return pending_remote_description_ ? pending_remote_description_.get()
2707 : current_remote_description_.get();
2708 }
2709
2710 const SessionDescriptionInterface*
current_local_description() const2711 SdpOfferAnswerHandler::current_local_description() const {
2712 RTC_DCHECK_RUN_ON(signaling_thread());
2713 return current_local_description_.get();
2714 }
2715
2716 const SessionDescriptionInterface*
current_remote_description() const2717 SdpOfferAnswerHandler::current_remote_description() const {
2718 RTC_DCHECK_RUN_ON(signaling_thread());
2719 return current_remote_description_.get();
2720 }
2721
2722 const SessionDescriptionInterface*
pending_local_description() const2723 SdpOfferAnswerHandler::pending_local_description() const {
2724 RTC_DCHECK_RUN_ON(signaling_thread());
2725 return pending_local_description_.get();
2726 }
2727
2728 const SessionDescriptionInterface*
pending_remote_description() const2729 SdpOfferAnswerHandler::pending_remote_description() const {
2730 RTC_DCHECK_RUN_ON(signaling_thread());
2731 return pending_remote_description_.get();
2732 }
2733
signaling_state() const2734 PeerConnectionInterface::SignalingState SdpOfferAnswerHandler::signaling_state()
2735 const {
2736 RTC_DCHECK_RUN_ON(signaling_thread());
2737 return signaling_state_;
2738 }
2739
ChangeSignalingState(PeerConnectionInterface::SignalingState signaling_state)2740 void SdpOfferAnswerHandler::ChangeSignalingState(
2741 PeerConnectionInterface::SignalingState signaling_state) {
2742 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::ChangeSignalingState");
2743 RTC_DCHECK_RUN_ON(signaling_thread());
2744 if (signaling_state_ == signaling_state) {
2745 return;
2746 }
2747 RTC_LOG(LS_INFO) << "Session: " << pc_->session_id() << " Old state: "
2748 << PeerConnectionInterface::AsString(signaling_state_)
2749 << " New state: "
2750 << PeerConnectionInterface::AsString(signaling_state);
2751 signaling_state_ = signaling_state;
2752 pc_->Observer()->OnSignalingChange(signaling_state_);
2753 }
2754
UpdateSessionState(SdpType type,cricket::ContentSource source,const cricket::SessionDescription * description,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)2755 RTCError SdpOfferAnswerHandler::UpdateSessionState(
2756 SdpType type,
2757 cricket::ContentSource source,
2758 const cricket::SessionDescription* description,
2759 const std::map<std::string, const cricket::ContentGroup*>&
2760 bundle_groups_by_mid) {
2761 RTC_DCHECK_RUN_ON(signaling_thread());
2762
2763 // If there's already a pending error then no state transition should
2764 // happen. But all call-sites should be verifying this before calling us!
2765 RTC_DCHECK(session_error() == SessionError::kNone);
2766
2767 // If this is answer-ish we're ready to let media flow.
2768 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
2769 EnableSending();
2770 }
2771
2772 // Update the signaling state according to the specified state machine (see
2773 // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum).
2774 if (type == SdpType::kOffer) {
2775 ChangeSignalingState(source == cricket::CS_LOCAL
2776 ? PeerConnectionInterface::kHaveLocalOffer
2777 : PeerConnectionInterface::kHaveRemoteOffer);
2778 } else if (type == SdpType::kPrAnswer) {
2779 ChangeSignalingState(source == cricket::CS_LOCAL
2780 ? PeerConnectionInterface::kHaveLocalPrAnswer
2781 : PeerConnectionInterface::kHaveRemotePrAnswer);
2782 } else {
2783 RTC_DCHECK(type == SdpType::kAnswer);
2784 ChangeSignalingState(PeerConnectionInterface::kStable);
2785 if (ConfiguredForMedia()) {
2786 transceivers()->DiscardStableStates();
2787 }
2788 }
2789
2790 // Update internal objects according to the session description's media
2791 // descriptions.
2792 return PushdownMediaDescription(type, source, bundle_groups_by_mid);
2793 }
2794
ShouldFireNegotiationNeededEvent(uint32_t event_id)2795 bool SdpOfferAnswerHandler::ShouldFireNegotiationNeededEvent(
2796 uint32_t event_id) {
2797 RTC_DCHECK_RUN_ON(signaling_thread());
2798 // Plan B? Always fire to conform with useless legacy behavior.
2799 if (!IsUnifiedPlan()) {
2800 return true;
2801 }
2802 // The event ID has been invalidated. Either negotiation is no longer needed
2803 // or a newer negotiation needed event has been generated.
2804 if (event_id != negotiation_needed_event_id_) {
2805 return false;
2806 }
2807 // The chain is no longer empty, update negotiation needed when it becomes
2808 // empty. This should generate a newer negotiation needed event, making this
2809 // one obsolete.
2810 if (!operations_chain_->IsEmpty()) {
2811 // Since we just suppressed an event that would have been fired, if
2812 // negotiation is still needed by the time the chain becomes empty again,
2813 // we must make sure to generate another event if negotiation is needed
2814 // then. This happens when `is_negotiation_needed_` goes from false to
2815 // true, so we set it to false until UpdateNegotiationNeeded() is called.
2816 is_negotiation_needed_ = false;
2817 update_negotiation_needed_on_empty_chain_ = true;
2818 return false;
2819 }
2820 // We must not fire if the signaling state is no longer "stable". If
2821 // negotiation is still needed when we return to "stable", a new negotiation
2822 // needed event will be generated, so this one can safely be suppressed.
2823 if (signaling_state_ != PeerConnectionInterface::kStable) {
2824 return false;
2825 }
2826 // All checks have passed - please fire "negotiationneeded" now!
2827 return true;
2828 }
2829
2830 rtc::scoped_refptr<StreamCollectionInterface>
local_streams()2831 SdpOfferAnswerHandler::local_streams() {
2832 RTC_DCHECK_RUN_ON(signaling_thread());
2833 RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified "
2834 "Plan SdpSemantics. Please use GetSenders "
2835 "instead.";
2836 return local_streams_;
2837 }
2838
2839 rtc::scoped_refptr<StreamCollectionInterface>
remote_streams()2840 SdpOfferAnswerHandler::remote_streams() {
2841 RTC_DCHECK_RUN_ON(signaling_thread());
2842 RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified "
2843 "Plan SdpSemantics. Please use GetReceivers "
2844 "instead.";
2845 return remote_streams_;
2846 }
2847
AddStream(MediaStreamInterface * local_stream)2848 bool SdpOfferAnswerHandler::AddStream(MediaStreamInterface* local_stream) {
2849 RTC_DCHECK_RUN_ON(signaling_thread());
2850 RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan "
2851 "SdpSemantics. Please use AddTrack instead.";
2852 if (pc_->IsClosed()) {
2853 return false;
2854 }
2855 if (!CanAddLocalMediaStream(local_streams_.get(), local_stream)) {
2856 return false;
2857 }
2858
2859 local_streams_->AddStream(
2860 rtc::scoped_refptr<MediaStreamInterface>(local_stream));
2861 auto observer = std::make_unique<MediaStreamObserver>(
2862 local_stream,
2863 [this](AudioTrackInterface* audio_track,
2864 MediaStreamInterface* media_stream) {
2865 RTC_DCHECK_RUN_ON(signaling_thread());
2866 OnAudioTrackAdded(audio_track, media_stream);
2867 },
2868 [this](AudioTrackInterface* audio_track,
2869 MediaStreamInterface* media_stream) {
2870 RTC_DCHECK_RUN_ON(signaling_thread());
2871 OnAudioTrackRemoved(audio_track, media_stream);
2872 },
2873 [this](VideoTrackInterface* video_track,
2874 MediaStreamInterface* media_stream) {
2875 RTC_DCHECK_RUN_ON(signaling_thread());
2876 OnVideoTrackAdded(video_track, media_stream);
2877 },
2878 [this](VideoTrackInterface* video_track,
2879 MediaStreamInterface* media_stream) {
2880 RTC_DCHECK_RUN_ON(signaling_thread());
2881 OnVideoTrackRemoved(video_track, media_stream);
2882 });
2883 stream_observers_.push_back(std::move(observer));
2884
2885 for (const auto& track : local_stream->GetAudioTracks()) {
2886 rtp_manager()->AddAudioTrack(track.get(), local_stream);
2887 }
2888 for (const auto& track : local_stream->GetVideoTracks()) {
2889 rtp_manager()->AddVideoTrack(track.get(), local_stream);
2890 }
2891
2892 pc_->legacy_stats()->AddStream(local_stream);
2893 UpdateNegotiationNeeded();
2894 return true;
2895 }
2896
RemoveStream(MediaStreamInterface * local_stream)2897 void SdpOfferAnswerHandler::RemoveStream(MediaStreamInterface* local_stream) {
2898 RTC_DCHECK_RUN_ON(signaling_thread());
2899 RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified "
2900 "Plan SdpSemantics. Please use RemoveTrack "
2901 "instead.";
2902 TRACE_EVENT0("webrtc", "PeerConnection::RemoveStream");
2903 if (!pc_->IsClosed()) {
2904 for (const auto& track : local_stream->GetAudioTracks()) {
2905 rtp_manager()->RemoveAudioTrack(track.get(), local_stream);
2906 }
2907 for (const auto& track : local_stream->GetVideoTracks()) {
2908 rtp_manager()->RemoveVideoTrack(track.get(), local_stream);
2909 }
2910 }
2911 local_streams_->RemoveStream(local_stream);
2912 stream_observers_.erase(
2913 std::remove_if(
2914 stream_observers_.begin(), stream_observers_.end(),
2915 [local_stream](const std::unique_ptr<MediaStreamObserver>& observer) {
2916 return observer->stream()->id().compare(local_stream->id()) == 0;
2917 }),
2918 stream_observers_.end());
2919
2920 if (pc_->IsClosed()) {
2921 return;
2922 }
2923 UpdateNegotiationNeeded();
2924 }
2925
OnAudioTrackAdded(AudioTrackInterface * track,MediaStreamInterface * stream)2926 void SdpOfferAnswerHandler::OnAudioTrackAdded(AudioTrackInterface* track,
2927 MediaStreamInterface* stream) {
2928 if (pc_->IsClosed()) {
2929 return;
2930 }
2931 rtp_manager()->AddAudioTrack(track, stream);
2932 UpdateNegotiationNeeded();
2933 }
2934
OnAudioTrackRemoved(AudioTrackInterface * track,MediaStreamInterface * stream)2935 void SdpOfferAnswerHandler::OnAudioTrackRemoved(AudioTrackInterface* track,
2936 MediaStreamInterface* stream) {
2937 if (pc_->IsClosed()) {
2938 return;
2939 }
2940 rtp_manager()->RemoveAudioTrack(track, stream);
2941 UpdateNegotiationNeeded();
2942 }
2943
OnVideoTrackAdded(VideoTrackInterface * track,MediaStreamInterface * stream)2944 void SdpOfferAnswerHandler::OnVideoTrackAdded(VideoTrackInterface* track,
2945 MediaStreamInterface* stream) {
2946 if (pc_->IsClosed()) {
2947 return;
2948 }
2949 rtp_manager()->AddVideoTrack(track, stream);
2950 UpdateNegotiationNeeded();
2951 }
2952
OnVideoTrackRemoved(VideoTrackInterface * track,MediaStreamInterface * stream)2953 void SdpOfferAnswerHandler::OnVideoTrackRemoved(VideoTrackInterface* track,
2954 MediaStreamInterface* stream) {
2955 if (pc_->IsClosed()) {
2956 return;
2957 }
2958 rtp_manager()->RemoveVideoTrack(track, stream);
2959 UpdateNegotiationNeeded();
2960 }
2961
Rollback(SdpType desc_type)2962 RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) {
2963 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::Rollback");
2964 auto state = signaling_state();
2965 if (state != PeerConnectionInterface::kHaveLocalOffer &&
2966 state != PeerConnectionInterface::kHaveRemoteOffer) {
2967 return RTCError(RTCErrorType::INVALID_STATE,
2968 (rtc::StringBuilder("Called in wrong signalingState: ")
2969 << (PeerConnectionInterface::AsString(signaling_state())))
2970 .Release());
2971 }
2972 RTC_DCHECK_RUN_ON(signaling_thread());
2973 RTC_DCHECK(IsUnifiedPlan());
2974 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>
2975 now_receiving_transceivers;
2976 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_added_streams;
2977 std::vector<rtc::scoped_refptr<MediaStreamInterface>> all_removed_streams;
2978 std::vector<rtc::scoped_refptr<RtpReceiverInterface>> removed_receivers;
2979
2980 for (auto&& transceivers_stable_state_pair : transceivers()->StableStates()) {
2981 auto transceiver = transceivers_stable_state_pair.first;
2982 auto state = transceivers_stable_state_pair.second;
2983
2984 if (state.did_set_fired_direction()) {
2985 // If this rollback triggers going from not receiving to receving again,
2986 // we need to fire "ontrack".
2987 bool previously_fired_direction_is_recv =
2988 transceiver->fired_direction().has_value() &&
2989 RtpTransceiverDirectionHasRecv(*transceiver->fired_direction());
2990 bool currently_fired_direction_is_recv =
2991 state.fired_direction().has_value() &&
2992 RtpTransceiverDirectionHasRecv(state.fired_direction().value());
2993 if (!previously_fired_direction_is_recv &&
2994 currently_fired_direction_is_recv) {
2995 now_receiving_transceivers.push_back(transceiver);
2996 }
2997 transceiver->internal()->set_fired_direction(state.fired_direction());
2998 }
2999
3000 if (state.remote_stream_ids()) {
3001 std::vector<rtc::scoped_refptr<MediaStreamInterface>> added_streams;
3002 std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
3003 SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(),
3004 state.remote_stream_ids().value(),
3005 &added_streams, &removed_streams);
3006 all_added_streams.insert(all_added_streams.end(), added_streams.begin(),
3007 added_streams.end());
3008 all_removed_streams.insert(all_removed_streams.end(),
3009 removed_streams.begin(),
3010 removed_streams.end());
3011 if (!state.has_m_section() && !state.newly_created()) {
3012 continue;
3013 }
3014 }
3015
3016 // Due to the above `continue` statement, the below code only runs if there
3017 // is a change in mid association (has_m_section), if the transceiver was
3018 // newly created (newly_created) or if remote streams were not set.
3019
3020 RTC_DCHECK(transceiver->internal()->mid().has_value());
3021 transceiver->internal()->ClearChannel();
3022
3023 if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer &&
3024 transceiver->receiver()) {
3025 removed_receivers.push_back(transceiver->receiver());
3026 }
3027 if (state.newly_created()) {
3028 if (transceiver->internal()->reused_for_addtrack()) {
3029 transceiver->internal()->set_created_by_addtrack(true);
3030 } else {
3031 transceiver->internal()->StopTransceiverProcedure();
3032 transceivers()->Remove(transceiver);
3033 }
3034 }
3035 if (state.init_send_encodings()) {
3036 transceiver->internal()->sender_internal()->set_init_send_encodings(
3037 state.init_send_encodings().value());
3038 }
3039 transceiver->internal()->sender_internal()->set_transport(nullptr);
3040 transceiver->internal()->receiver_internal()->set_transport(nullptr);
3041 transceiver->internal()->set_mid(state.mid());
3042 transceiver->internal()->set_mline_index(state.mline_index());
3043 }
3044 RTCError e = transport_controller_s()->RollbackTransports();
3045 if (!e.ok()) {
3046 return e;
3047 }
3048 transceivers()->DiscardStableStates();
3049 pending_local_description_.reset();
3050 pending_remote_description_.reset();
3051 ChangeSignalingState(PeerConnectionInterface::kStable);
3052
3053 // Once all processing has finished, fire off callbacks.
3054 for (const auto& transceiver : now_receiving_transceivers) {
3055 pc_->Observer()->OnTrack(transceiver);
3056 pc_->Observer()->OnAddTrack(transceiver->receiver(),
3057 transceiver->receiver()->streams());
3058 }
3059 for (const auto& receiver : removed_receivers) {
3060 pc_->Observer()->OnRemoveTrack(receiver);
3061 }
3062 for (const auto& stream : all_added_streams) {
3063 pc_->Observer()->OnAddStream(stream);
3064 }
3065 for (const auto& stream : all_removed_streams) {
3066 pc_->Observer()->OnRemoveStream(stream);
3067 }
3068
3069 // The assumption is that in case of implicit rollback
3070 // UpdateNegotiationNeeded gets called in SetRemoteDescription.
3071 if (desc_type == SdpType::kRollback) {
3072 UpdateNegotiationNeeded();
3073 if (is_negotiation_needed_) {
3074 // Legacy version.
3075 pc_->Observer()->OnRenegotiationNeeded();
3076 // Spec-compliant version; the event may get invalidated before firing.
3077 GenerateNegotiationNeededEvent();
3078 }
3079 }
3080 return RTCError::OK();
3081 }
3082
IsUnifiedPlan() const3083 bool SdpOfferAnswerHandler::IsUnifiedPlan() const {
3084 return pc_->IsUnifiedPlan();
3085 }
3086
OnOperationsChainEmpty()3087 void SdpOfferAnswerHandler::OnOperationsChainEmpty() {
3088 RTC_DCHECK_RUN_ON(signaling_thread());
3089 if (pc_->IsClosed() || !update_negotiation_needed_on_empty_chain_)
3090 return;
3091 update_negotiation_needed_on_empty_chain_ = false;
3092 // Firing when chain is empty is only supported in Unified Plan to avoid
3093 // Plan B regressions. (In Plan B, onnegotiationneeded is already broken
3094 // anyway, so firing it even more might just be confusing.)
3095 if (IsUnifiedPlan()) {
3096 UpdateNegotiationNeeded();
3097 }
3098 }
3099
is_caller()3100 absl::optional<bool> SdpOfferAnswerHandler::is_caller() {
3101 RTC_DCHECK_RUN_ON(signaling_thread());
3102 return is_caller_;
3103 }
3104
HasNewIceCredentials()3105 bool SdpOfferAnswerHandler::HasNewIceCredentials() {
3106 RTC_DCHECK_RUN_ON(signaling_thread());
3107 return local_ice_credentials_to_replace_->HasIceCredentials();
3108 }
3109
IceRestartPending(const std::string & content_name) const3110 bool SdpOfferAnswerHandler::IceRestartPending(
3111 const std::string& content_name) const {
3112 RTC_DCHECK_RUN_ON(signaling_thread());
3113 return pending_ice_restarts_.find(content_name) !=
3114 pending_ice_restarts_.end();
3115 }
3116
NeedsIceRestart(const std::string & content_name) const3117 bool SdpOfferAnswerHandler::NeedsIceRestart(
3118 const std::string& content_name) const {
3119 return pc_->NeedsIceRestart(content_name);
3120 }
3121
GetDtlsRole(const std::string & mid) const3122 absl::optional<rtc::SSLRole> SdpOfferAnswerHandler::GetDtlsRole(
3123 const std::string& mid) const {
3124 RTC_DCHECK_RUN_ON(signaling_thread());
3125 return transport_controller_s()->GetDtlsRole(mid);
3126 }
3127
UpdateNegotiationNeeded()3128 void SdpOfferAnswerHandler::UpdateNegotiationNeeded() {
3129 RTC_DCHECK_RUN_ON(signaling_thread());
3130 if (!IsUnifiedPlan()) {
3131 pc_->Observer()->OnRenegotiationNeeded();
3132 GenerateNegotiationNeededEvent();
3133 return;
3134 }
3135
3136 // In the spec, a task is queued here to run the following steps - this is
3137 // meant to ensure we do not fire onnegotiationneeded prematurely if
3138 // multiple changes are being made at once. In order to support Chromium's
3139 // implementation where the JavaScript representation of the PeerConnection
3140 // lives on a separate thread though, the queuing of a task is instead
3141 // performed by the PeerConnectionObserver posting from the signaling thread
3142 // to the JavaScript main thread that negotiation is needed. And because the
3143 // Operations Chain lives on the WebRTC signaling thread,
3144 // ShouldFireNegotiationNeededEvent() must be called before firing the event
3145 // to ensure the Operations Chain is still empty and the event has not been
3146 // invalidated.
3147
3148 // If connection's [[IsClosed]] slot is true, abort these steps.
3149 if (pc_->IsClosed())
3150 return;
3151
3152 // If connection's signaling state is not "stable", abort these steps.
3153 if (signaling_state() != PeerConnectionInterface::kStable)
3154 return;
3155
3156 // NOTE
3157 // The negotiation-needed flag will be updated once the state transitions to
3158 // "stable", as part of the steps for setting an RTCSessionDescription.
3159
3160 // If the result of checking if negotiation is needed is false, clear the
3161 // negotiation-needed flag by setting connection's [[NegotiationNeeded]]
3162 // slot to false, and abort these steps.
3163 bool is_negotiation_needed = CheckIfNegotiationIsNeeded();
3164 if (!is_negotiation_needed) {
3165 is_negotiation_needed_ = false;
3166 // Invalidate any negotiation needed event that may previosuly have been
3167 // generated.
3168 ++negotiation_needed_event_id_;
3169 return;
3170 }
3171
3172 // If connection's [[NegotiationNeeded]] slot is already true, abort these
3173 // steps.
3174 if (is_negotiation_needed_)
3175 return;
3176
3177 // Set connection's [[NegotiationNeeded]] slot to true.
3178 is_negotiation_needed_ = true;
3179
3180 // Queue a task that runs the following steps:
3181 // If connection's [[IsClosed]] slot is true, abort these steps.
3182 // If connection's [[NegotiationNeeded]] slot is false, abort these steps.
3183 // Fire an event named negotiationneeded at connection.
3184 pc_->Observer()->OnRenegotiationNeeded();
3185 // Fire the spec-compliant version; when ShouldFireNegotiationNeededEvent()
3186 // is used in the task queued by the observer, this event will only fire
3187 // when the chain is empty.
3188 GenerateNegotiationNeededEvent();
3189 }
3190
CheckIfNegotiationIsNeeded()3191 bool SdpOfferAnswerHandler::CheckIfNegotiationIsNeeded() {
3192 RTC_DCHECK_RUN_ON(signaling_thread());
3193 // 1. If any implementation-specific negotiation is required, as described
3194 // at the start of this section, return true.
3195
3196 // 2. If connection.[[LocalIceCredentialsToReplace]] is not empty, return
3197 // true.
3198 if (local_ice_credentials_to_replace_->HasIceCredentials()) {
3199 return true;
3200 }
3201
3202 // 3. Let description be connection.[[CurrentLocalDescription]].
3203 const SessionDescriptionInterface* description = current_local_description();
3204 if (!description)
3205 return true;
3206
3207 // 4. If connection has created any RTCDataChannels, and no m= section in
3208 // description has been negotiated yet for data, return true.
3209 if (data_channel_controller()->HasSctpDataChannels()) {
3210 if (!cricket::GetFirstDataContent(description->description()->contents()))
3211 return true;
3212 }
3213 if (!ConfiguredForMedia()) {
3214 return false;
3215 }
3216
3217 // 5. For each transceiver in connection's set of transceivers, perform the
3218 // following checks:
3219 for (const auto& transceiver : transceivers()->ListInternal()) {
3220 const ContentInfo* current_local_msection =
3221 FindTransceiverMSection(transceiver, description);
3222
3223 const ContentInfo* current_remote_msection =
3224 FindTransceiverMSection(transceiver, current_remote_description());
3225
3226 // 5.4 If transceiver is stopped and is associated with an m= section,
3227 // but the associated m= section is not yet rejected in
3228 // connection.[[CurrentLocalDescription]] or
3229 // connection.[[CurrentRemoteDescription]], return true.
3230 if (transceiver->stopped()) {
3231 RTC_DCHECK(transceiver->stopping());
3232 if (current_local_msection && !current_local_msection->rejected &&
3233 ((current_remote_msection && !current_remote_msection->rejected) ||
3234 !current_remote_msection)) {
3235 return true;
3236 }
3237 continue;
3238 }
3239
3240 // 5.1 If transceiver.[[Stopping]] is true and transceiver.[[Stopped]] is
3241 // false, return true.
3242 if (transceiver->stopping() && !transceiver->stopped())
3243 return true;
3244
3245 // 5.2 If transceiver isn't stopped and isn't yet associated with an m=
3246 // section in description, return true.
3247 if (!current_local_msection)
3248 return true;
3249
3250 const MediaContentDescription* current_local_media_description =
3251 current_local_msection->media_description();
3252 // 5.3 If transceiver isn't stopped and is associated with an m= section
3253 // in description then perform the following checks:
3254
3255 // 5.3.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the
3256 // associated m= section in description either doesn't contain a single
3257 // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this
3258 // m= section, or the MSID values themselves, differ from what is in
3259 // transceiver.sender.[[AssociatedMediaStreamIds]], return true.
3260 if (RtpTransceiverDirectionHasSend(transceiver->direction())) {
3261 if (current_local_media_description->streams().size() == 0)
3262 return true;
3263
3264 std::vector<std::string> msection_msids;
3265 for (const auto& stream : current_local_media_description->streams()) {
3266 for (const std::string& msid : stream.stream_ids())
3267 msection_msids.push_back(msid);
3268 }
3269
3270 std::vector<std::string> transceiver_msids =
3271 transceiver->sender()->stream_ids();
3272 if (msection_msids.size() != transceiver_msids.size())
3273 return true;
3274
3275 absl::c_sort(transceiver_msids);
3276 absl::c_sort(msection_msids);
3277 if (transceiver_msids != msection_msids)
3278 return true;
3279 }
3280
3281 // 5.3.2 If description is of type "offer", and the direction of the
3282 // associated m= section in neither connection.[[CurrentLocalDescription]]
3283 // nor connection.[[CurrentRemoteDescription]] matches
3284 // transceiver.[[Direction]], return true.
3285 if (description->GetType() == SdpType::kOffer) {
3286 if (!current_remote_description())
3287 return true;
3288
3289 if (!current_remote_msection)
3290 return true;
3291
3292 RtpTransceiverDirection current_local_direction =
3293 current_local_media_description->direction();
3294 RtpTransceiverDirection current_remote_direction =
3295 current_remote_msection->media_description()->direction();
3296 if (transceiver->direction() != current_local_direction &&
3297 transceiver->direction() !=
3298 RtpTransceiverDirectionReversed(current_remote_direction)) {
3299 return true;
3300 }
3301 }
3302
3303 // 5.3.3 If description is of type "answer", and the direction of the
3304 // associated m= section in the description does not match
3305 // transceiver.[[Direction]] intersected with the offered direction (as
3306 // described in [JSEP] (section 5.3.1.)), return true.
3307 if (description->GetType() == SdpType::kAnswer) {
3308 if (!remote_description())
3309 return true;
3310
3311 const ContentInfo* offered_remote_msection =
3312 FindTransceiverMSection(transceiver, remote_description());
3313
3314 RtpTransceiverDirection offered_direction =
3315 offered_remote_msection
3316 ? offered_remote_msection->media_description()->direction()
3317 : RtpTransceiverDirection::kInactive;
3318
3319 if (current_local_media_description->direction() !=
3320 (RtpTransceiverDirectionIntersection(
3321 transceiver->direction(),
3322 RtpTransceiverDirectionReversed(offered_direction)))) {
3323 return true;
3324 }
3325 }
3326 }
3327 // If all the preceding checks were performed and true was not returned,
3328 // nothing remains to be negotiated; return false.
3329 return false;
3330 }
3331
GenerateNegotiationNeededEvent()3332 void SdpOfferAnswerHandler::GenerateNegotiationNeededEvent() {
3333 RTC_DCHECK_RUN_ON(signaling_thread());
3334 ++negotiation_needed_event_id_;
3335 pc_->Observer()->OnNegotiationNeededEvent(negotiation_needed_event_id_);
3336 }
3337
ValidateSessionDescription(const SessionDescriptionInterface * sdesc,cricket::ContentSource source,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)3338 RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
3339 const SessionDescriptionInterface* sdesc,
3340 cricket::ContentSource source,
3341 const std::map<std::string, const cricket::ContentGroup*>&
3342 bundle_groups_by_mid) {
3343 // An assumption is that a check for session error is done at a higher level.
3344 RTC_DCHECK_EQ(SessionError::kNone, session_error());
3345
3346 if (!sdesc || !sdesc->description()) {
3347 return RTCError(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
3348 }
3349
3350 SdpType type = sdesc->GetType();
3351 if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) ||
3352 (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) {
3353 return RTCError(RTCErrorType::INVALID_STATE,
3354 (rtc::StringBuilder("Called in wrong state: ")
3355 << PeerConnectionInterface::AsString(signaling_state()))
3356 .Release());
3357 }
3358
3359 RTCError error = ValidateMids(*sdesc->description());
3360 if (!error.ok()) {
3361 return error;
3362 }
3363
3364 // Verify crypto settings.
3365 std::string crypto_error;
3366 if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
3367 pc_->dtls_enabled()) {
3368 RTCError crypto_error = VerifyCrypto(
3369 sdesc->description(), pc_->dtls_enabled(), bundle_groups_by_mid);
3370 if (!crypto_error.ok()) {
3371 return crypto_error;
3372 }
3373 }
3374
3375 // Verify ice-ufrag and ice-pwd.
3376 if (!VerifyIceUfragPwdPresent(sdesc->description(), bundle_groups_by_mid)) {
3377 return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutIceUfragPwd);
3378 }
3379
3380 // Validate bundle, payload types and that there are no collisions.
3381 error = ValidateBundledPayloadTypes(*sdesc->description());
3382 // TODO(bugs.webrtc.org/14420): actually reject.
3383 RTC_HISTOGRAM_BOOLEAN("WebRTC.PeerConnection.ValidBundledPayloadTypes",
3384 error.ok());
3385
3386 if (!pc_->ValidateBundleSettings(sdesc->description(),
3387 bundle_groups_by_mid)) {
3388 return RTCError(RTCErrorType::INVALID_PARAMETER, kBundleWithoutRtcpMux);
3389 }
3390
3391 // TODO(skvlad): When the local rtcp-mux policy is Require, reject any
3392 // m-lines that do not rtcp-mux enabled.
3393
3394 // Verify m-lines in Answer when compared against Offer.
3395 if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
3396 // With an answer we want to compare the new answer session description
3397 // with the offer's session description from the current negotiation.
3398 const cricket::SessionDescription* offer_desc =
3399 (source == cricket::CS_LOCAL) ? remote_description()->description()
3400 : local_description()->description();
3401 if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) ||
3402 !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(),
3403 type)) {
3404 return RTCError(RTCErrorType::INVALID_PARAMETER, kMlineMismatchInAnswer);
3405 }
3406 } else {
3407 // The re-offers should respect the order of m= sections in current
3408 // description. See RFC3264 Section 8 paragraph 4 for more details.
3409 // With a re-offer, either the current local or current remote
3410 // descriptions could be the most up to date, so we would like to check
3411 // against both of them if they exist. It could be the case that one of
3412 // them has a 0 port for a media section, but the other does not. This is
3413 // important to check against in the case that we are recycling an m=
3414 // section.
3415 const cricket::SessionDescription* current_desc = nullptr;
3416 const cricket::SessionDescription* secondary_current_desc = nullptr;
3417 if (local_description()) {
3418 current_desc = local_description()->description();
3419 if (remote_description()) {
3420 secondary_current_desc = remote_description()->description();
3421 }
3422 } else if (remote_description()) {
3423 current_desc = remote_description()->description();
3424 }
3425 if (current_desc &&
3426 !MediaSectionsInSameOrder(*current_desc, secondary_current_desc,
3427 *sdesc->description(), type)) {
3428 return RTCError(RTCErrorType::INVALID_PARAMETER,
3429 kMlineMismatchInSubsequentOffer);
3430 }
3431 }
3432
3433 if (IsUnifiedPlan()) {
3434 // Ensure that each audio and video media section has at most one
3435 // "StreamParams". This will return an error if receiving a session
3436 // description from a "Plan B" endpoint which adds multiple tracks of the
3437 // same type. With Unified Plan, there can only be at most one track per
3438 // media section.
3439 for (const ContentInfo& content : sdesc->description()->contents()) {
3440 const MediaContentDescription& desc = *content.media_description();
3441 if ((desc.type() == cricket::MEDIA_TYPE_AUDIO ||
3442 desc.type() == cricket::MEDIA_TYPE_VIDEO) &&
3443 desc.streams().size() > 1u) {
3444 return RTCError(
3445 RTCErrorType::INVALID_PARAMETER,
3446 "Media section has more than one track specified with a=ssrc lines "
3447 "which is not supported with Unified Plan.");
3448 }
3449 }
3450 }
3451
3452 return RTCError::OK();
3453 }
3454
UpdateTransceiversAndDataChannels(cricket::ContentSource source,const SessionDescriptionInterface & new_session,const SessionDescriptionInterface * old_local_description,const SessionDescriptionInterface * old_remote_description,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)3455 RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels(
3456 cricket::ContentSource source,
3457 const SessionDescriptionInterface& new_session,
3458 const SessionDescriptionInterface* old_local_description,
3459 const SessionDescriptionInterface* old_remote_description,
3460 const std::map<std::string, const cricket::ContentGroup*>&
3461 bundle_groups_by_mid) {
3462 TRACE_EVENT0("webrtc",
3463 "SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels");
3464 RTC_DCHECK_RUN_ON(signaling_thread());
3465 RTC_DCHECK(IsUnifiedPlan());
3466
3467 if (new_session.GetType() == SdpType::kOffer) {
3468 // If the BUNDLE policy is max-bundle, then we know for sure that all
3469 // transports will be bundled from the start. Return an error if
3470 // max-bundle is specified but the session description does not have a
3471 // BUNDLE group.
3472 if (pc_->configuration()->bundle_policy ==
3473 PeerConnectionInterface::kBundlePolicyMaxBundle &&
3474 bundle_groups_by_mid.empty()) {
3475 return RTCError(
3476 RTCErrorType::INVALID_PARAMETER,
3477 "max-bundle configured but session description has no BUNDLE group");
3478 }
3479 }
3480
3481 const ContentInfos& new_contents = new_session.description()->contents();
3482 for (size_t i = 0; i < new_contents.size(); ++i) {
3483 const cricket::ContentInfo& new_content = new_contents[i];
3484 cricket::MediaType media_type = new_content.media_description()->type();
3485 mid_generator_.AddKnownId(new_content.name);
3486 auto it = bundle_groups_by_mid.find(new_content.name);
3487 const cricket::ContentGroup* bundle_group =
3488 it != bundle_groups_by_mid.end() ? it->second : nullptr;
3489 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
3490 media_type == cricket::MEDIA_TYPE_VIDEO) {
3491 const cricket::ContentInfo* old_local_content = nullptr;
3492 if (old_local_description &&
3493 i < old_local_description->description()->contents().size()) {
3494 old_local_content =
3495 &old_local_description->description()->contents()[i];
3496 }
3497 const cricket::ContentInfo* old_remote_content = nullptr;
3498 if (old_remote_description &&
3499 i < old_remote_description->description()->contents().size()) {
3500 old_remote_content =
3501 &old_remote_description->description()->contents()[i];
3502 }
3503 auto transceiver_or_error =
3504 AssociateTransceiver(source, new_session.GetType(), i, new_content,
3505 old_local_content, old_remote_content);
3506 if (!transceiver_or_error.ok()) {
3507 // In the case where a transceiver is rejected locally prior to being
3508 // associated, we don't expect to find a transceiver, but might find it
3509 // in the case where state is still "stopping", not "stopped".
3510 if (new_content.rejected) {
3511 continue;
3512 }
3513 return transceiver_or_error.MoveError();
3514 }
3515 auto transceiver = transceiver_or_error.MoveValue();
3516 RTCError error =
3517 UpdateTransceiverChannel(transceiver, new_content, bundle_group);
3518 // Handle locally rejected content. This code path is only needed for apps
3519 // that SDP munge. Remote rejected content is handled in
3520 // ApplyRemoteDescriptionUpdateTransceiverState().
3521 if (source == cricket::ContentSource::CS_LOCAL && new_content.rejected) {
3522 // Local offer.
3523 if (new_session.GetType() == SdpType::kOffer) {
3524 // If the RtpTransceiver API was used, it would already have made the
3525 // transceiver stopping. But if the rejection was caused by SDP
3526 // munging then we need to ensure the transceiver is stopping here.
3527 if (!transceiver->internal()->stopping()) {
3528 transceiver->internal()->StopStandard();
3529 }
3530 RTC_DCHECK(transceiver->internal()->stopping());
3531 } else {
3532 // Local answer.
3533 RTC_DCHECK(new_session.GetType() == SdpType::kAnswer ||
3534 new_session.GetType() == SdpType::kPrAnswer);
3535 // When RtpTransceiver API is used, rejection happens in the offer and
3536 // the transceiver will already be stopped at local answer time
3537 // (calling stop between SRD(offer) and SLD(answer) would not reject
3538 // the content in the answer - instead this would trigger a follow-up
3539 // O/A exchange). So if the content was rejected but the transceiver
3540 // is not already stopped, SDP munging has happened and we need to
3541 // ensure the transceiver is stopped.
3542 if (!transceiver->internal()->stopped()) {
3543 transceiver->internal()->StopTransceiverProcedure();
3544 }
3545 RTC_DCHECK(transceiver->internal()->stopped());
3546 }
3547 }
3548 if (!error.ok()) {
3549 return error;
3550 }
3551 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3552 if (pc_->GetDataMid() && new_content.name != *(pc_->GetDataMid())) {
3553 // Ignore all but the first data section.
3554 RTC_LOG(LS_INFO) << "Ignoring data media section with MID="
3555 << new_content.name;
3556 continue;
3557 }
3558 RTCError error = UpdateDataChannel(source, new_content, bundle_group);
3559 if (!error.ok()) {
3560 return error;
3561 }
3562 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
3563 RTC_LOG(LS_INFO) << "Ignoring unsupported media type";
3564 } else {
3565 return RTCError(RTCErrorType::INTERNAL_ERROR, "Unknown section type.");
3566 }
3567 }
3568
3569 return RTCError::OK();
3570 }
3571
3572 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
AssociateTransceiver(cricket::ContentSource source,SdpType type,size_t mline_index,const ContentInfo & content,const ContentInfo * old_local_content,const ContentInfo * old_remote_content)3573 SdpOfferAnswerHandler::AssociateTransceiver(
3574 cricket::ContentSource source,
3575 SdpType type,
3576 size_t mline_index,
3577 const ContentInfo& content,
3578 const ContentInfo* old_local_content,
3579 const ContentInfo* old_remote_content) {
3580 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::AssociateTransceiver");
3581 RTC_DCHECK(IsUnifiedPlan());
3582 #if RTC_DCHECK_IS_ON
3583 // If this is an offer then the m= section might be recycled. If the m=
3584 // section is being recycled (defined as: rejected in the current local or
3585 // remote description and not rejected in new description), the transceiver
3586 // should have been removed by RemoveStoppedtransceivers()->
3587 if (IsMediaSectionBeingRecycled(type, content, old_local_content,
3588 old_remote_content)) {
3589 const std::string& old_mid =
3590 (old_local_content && old_local_content->rejected)
3591 ? old_local_content->name
3592 : old_remote_content->name;
3593 auto old_transceiver = transceivers()->FindByMid(old_mid);
3594 // The transceiver should be disassociated in RemoveStoppedTransceivers()
3595 RTC_DCHECK(!old_transceiver);
3596 }
3597 #endif
3598
3599 const MediaContentDescription* media_desc = content.media_description();
3600 auto transceiver = transceivers()->FindByMid(content.name);
3601 if (source == cricket::CS_LOCAL) {
3602 // Find the RtpTransceiver that corresponds to this m= section, using the
3603 // mapping between transceivers and m= section indices established when
3604 // creating the offer.
3605 if (!transceiver) {
3606 transceiver = transceivers()->FindByMLineIndex(mline_index);
3607 }
3608 if (!transceiver) {
3609 // This may happen normally when media sections are rejected.
3610 return RTCError(RTCErrorType::INVALID_PARAMETER,
3611 "Transceiver not found based on m-line index");
3612 }
3613 } else {
3614 RTC_DCHECK_EQ(source, cricket::CS_REMOTE);
3615 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers
3616 // of the same type...
3617 // When simulcast is requested, a transceiver cannot be associated because
3618 // AddTrack cannot be called to initialize it.
3619 if (!transceiver &&
3620 RtpTransceiverDirectionHasRecv(media_desc->direction()) &&
3621 !media_desc->HasSimulcast()) {
3622 transceiver = FindAvailableTransceiverToReceive(media_desc->type());
3623 }
3624 // If no RtpTransceiver was found in the previous step, create one with a
3625 // recvonly direction.
3626 if (!transceiver) {
3627 RTC_LOG(LS_INFO) << "Adding "
3628 << cricket::MediaTypeToString(media_desc->type())
3629 << " transceiver for MID=" << content.name
3630 << " at i=" << mline_index
3631 << " in response to the remote description.";
3632 std::string sender_id = rtc::CreateRandomUuid();
3633 std::vector<RtpEncodingParameters> send_encodings =
3634 GetSendEncodingsFromRemoteDescription(*media_desc);
3635 auto sender = rtp_manager()->CreateSender(media_desc->type(), sender_id,
3636 nullptr, {}, send_encodings);
3637 std::string receiver_id;
3638 if (!media_desc->streams().empty()) {
3639 receiver_id = media_desc->streams()[0].id;
3640 } else {
3641 receiver_id = rtc::CreateRandomUuid();
3642 }
3643 auto receiver =
3644 rtp_manager()->CreateReceiver(media_desc->type(), receiver_id);
3645 transceiver = rtp_manager()->CreateAndAddTransceiver(sender, receiver);
3646 transceiver->internal()->set_direction(
3647 RtpTransceiverDirection::kRecvOnly);
3648 if (type == SdpType::kOffer) {
3649 transceivers()->StableState(transceiver)->set_newly_created();
3650 }
3651 }
3652
3653 RTC_DCHECK(transceiver);
3654
3655 // Check if the offer indicated simulcast but the answer rejected it.
3656 // This can happen when simulcast is not supported on the remote party.
3657 if (SimulcastIsRejected(old_local_content, *media_desc,
3658 pc_->GetCryptoOptions()
3659 .srtp.enable_encrypted_rtp_header_extensions)) {
3660 RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true);
3661 RTCError error =
3662 DisableSimulcastInSender(transceiver->internal()->sender_internal());
3663 if (!error.ok()) {
3664 RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast.";
3665 return std::move(error);
3666 }
3667 }
3668 }
3669
3670 if (transceiver->media_type() != media_desc->type()) {
3671 return RTCError(RTCErrorType::INVALID_PARAMETER,
3672 "Transceiver type does not match media description type.");
3673 }
3674
3675 if (media_desc->HasSimulcast()) {
3676 std::vector<SimulcastLayer> layers =
3677 source == cricket::CS_LOCAL
3678 ? media_desc->simulcast_description().send_layers().GetAllLayers()
3679 : media_desc->simulcast_description()
3680 .receive_layers()
3681 .GetAllLayers();
3682 RTCError error = UpdateSimulcastLayerStatusInSender(
3683 layers, transceiver->internal()->sender_internal());
3684 if (!error.ok()) {
3685 RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers.";
3686 return std::move(error);
3687 }
3688 }
3689 if (type == SdpType::kOffer) {
3690 bool state_changes = transceiver->internal()->mid() != content.name ||
3691 transceiver->internal()->mline_index() != mline_index;
3692 if (state_changes) {
3693 transceivers()
3694 ->StableState(transceiver)
3695 ->SetMSectionIfUnset(transceiver->internal()->mid(),
3696 transceiver->internal()->mline_index());
3697 }
3698 }
3699 // Associate the found or created RtpTransceiver with the m= section by
3700 // setting the value of the RtpTransceiver's mid property to the MID of the m=
3701 // section, and establish a mapping between the transceiver and the index of
3702 // the m= section.
3703 transceiver->internal()->set_mid(content.name);
3704 transceiver->internal()->set_mline_index(mline_index);
3705 return std::move(transceiver);
3706 }
3707
UpdateTransceiverChannel(rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>> transceiver,const cricket::ContentInfo & content,const cricket::ContentGroup * bundle_group)3708 RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel(
3709 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
3710 transceiver,
3711 const cricket::ContentInfo& content,
3712 const cricket::ContentGroup* bundle_group) {
3713 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateTransceiverChannel");
3714 RTC_DCHECK(IsUnifiedPlan());
3715 RTC_DCHECK(transceiver);
3716 cricket::ChannelInterface* channel = transceiver->internal()->channel();
3717 if (content.rejected) {
3718 if (channel) {
3719 transceiver->internal()->ClearChannel();
3720 }
3721 } else {
3722 if (!channel) {
3723 auto error = transceiver->internal()->CreateChannel(
3724 content.name, pc_->call_ptr(), pc_->configuration()->media_config,
3725 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
3726 video_options(), video_bitrate_allocator_factory_.get(),
3727 [&](absl::string_view mid) {
3728 RTC_DCHECK_RUN_ON(network_thread());
3729 return transport_controller_n()->GetRtpTransport(mid);
3730 });
3731 if (!error.ok()) {
3732 return error;
3733 }
3734 }
3735 }
3736 return RTCError::OK();
3737 }
3738
UpdateDataChannel(cricket::ContentSource source,const cricket::ContentInfo & content,const cricket::ContentGroup * bundle_group)3739 RTCError SdpOfferAnswerHandler::UpdateDataChannel(
3740 cricket::ContentSource source,
3741 const cricket::ContentInfo& content,
3742 const cricket::ContentGroup* bundle_group) {
3743 if (content.rejected) {
3744 RTC_LOG(LS_INFO) << "Rejected data channel transport with mid="
3745 << content.mid();
3746
3747 rtc::StringBuilder sb;
3748 sb << "Rejected data channel transport with mid=" << content.mid();
3749 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
3750 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
3751 DestroyDataChannelTransport(error);
3752 } else {
3753 if (!data_channel_controller()->data_channel_transport()) {
3754 RTC_LOG(LS_INFO) << "Creating data channel, mid=" << content.mid();
3755 if (!CreateDataChannel(content.name)) {
3756 return RTCError(RTCErrorType::INTERNAL_ERROR,
3757 "Failed to create data channel.");
3758 }
3759 }
3760 }
3761 return RTCError::OK();
3762 }
3763
ExpectSetLocalDescription(SdpType type)3764 bool SdpOfferAnswerHandler::ExpectSetLocalDescription(SdpType type) {
3765 PeerConnectionInterface::SignalingState state = signaling_state();
3766 if (type == SdpType::kOffer) {
3767 return (state == PeerConnectionInterface::kStable) ||
3768 (state == PeerConnectionInterface::kHaveLocalOffer);
3769 } else {
3770 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3771 return (state == PeerConnectionInterface::kHaveRemoteOffer) ||
3772 (state == PeerConnectionInterface::kHaveLocalPrAnswer);
3773 }
3774 }
3775
ExpectSetRemoteDescription(SdpType type)3776 bool SdpOfferAnswerHandler::ExpectSetRemoteDescription(SdpType type) {
3777 PeerConnectionInterface::SignalingState state = signaling_state();
3778 if (type == SdpType::kOffer) {
3779 return (state == PeerConnectionInterface::kStable) ||
3780 (state == PeerConnectionInterface::kHaveRemoteOffer);
3781 } else {
3782 RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer);
3783 return (state == PeerConnectionInterface::kHaveLocalOffer) ||
3784 (state == PeerConnectionInterface::kHaveRemotePrAnswer);
3785 }
3786 }
3787
FillInMissingRemoteMids(cricket::SessionDescription * new_remote_description)3788 void SdpOfferAnswerHandler::FillInMissingRemoteMids(
3789 cricket::SessionDescription* new_remote_description) {
3790 RTC_DCHECK_RUN_ON(signaling_thread());
3791 RTC_DCHECK(new_remote_description);
3792 const cricket::ContentInfos no_infos;
3793 const cricket::ContentInfos& local_contents =
3794 (local_description() ? local_description()->description()->contents()
3795 : no_infos);
3796 const cricket::ContentInfos& remote_contents =
3797 (remote_description() ? remote_description()->description()->contents()
3798 : no_infos);
3799 for (size_t i = 0; i < new_remote_description->contents().size(); ++i) {
3800 cricket::ContentInfo& content = new_remote_description->contents()[i];
3801 if (!content.name.empty()) {
3802 continue;
3803 }
3804 std::string new_mid;
3805 absl::string_view source_explanation;
3806 if (IsUnifiedPlan()) {
3807 if (i < local_contents.size()) {
3808 new_mid = local_contents[i].name;
3809 source_explanation = "from the matching local media section";
3810 } else if (i < remote_contents.size()) {
3811 new_mid = remote_contents[i].name;
3812 source_explanation = "from the matching previous remote media section";
3813 } else {
3814 new_mid = mid_generator_.GenerateString();
3815 source_explanation = "generated just now";
3816 }
3817 } else {
3818 new_mid = std::string(
3819 GetDefaultMidForPlanB(content.media_description()->type()));
3820 source_explanation = "to match pre-existing behavior";
3821 }
3822 RTC_DCHECK(!new_mid.empty());
3823 content.name = new_mid;
3824 new_remote_description->transport_infos()[i].content_name = new_mid;
3825 RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i
3826 << " is missing an a=mid line. Filling in the value '"
3827 << new_mid << "' " << source_explanation << ".";
3828 }
3829 }
3830
3831 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
FindAvailableTransceiverToReceive(cricket::MediaType media_type) const3832 SdpOfferAnswerHandler::FindAvailableTransceiverToReceive(
3833 cricket::MediaType media_type) const {
3834 RTC_DCHECK_RUN_ON(signaling_thread());
3835 RTC_DCHECK(IsUnifiedPlan());
3836 // From JSEP section 5.10 (Applying a Remote Description):
3837 // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of
3838 // the same type that were added to the PeerConnection by addTrack and are not
3839 // associated with any m= section and are not stopped, find the first such
3840 // RtpTransceiver.
3841 for (auto transceiver : transceivers()->List()) {
3842 if (transceiver->media_type() == media_type &&
3843 transceiver->internal()->created_by_addtrack() && !transceiver->mid() &&
3844 !transceiver->stopped()) {
3845 return transceiver;
3846 }
3847 }
3848 return nullptr;
3849 }
3850
3851 const cricket::ContentInfo*
FindMediaSectionForTransceiver(const RtpTransceiver * transceiver,const SessionDescriptionInterface * sdesc) const3852 SdpOfferAnswerHandler::FindMediaSectionForTransceiver(
3853 const RtpTransceiver* transceiver,
3854 const SessionDescriptionInterface* sdesc) const {
3855 RTC_DCHECK_RUN_ON(signaling_thread());
3856 RTC_DCHECK(transceiver);
3857 RTC_DCHECK(sdesc);
3858 if (IsUnifiedPlan()) {
3859 if (!transceiver->mid()) {
3860 // This transceiver is not associated with a media section yet.
3861 return nullptr;
3862 }
3863 return sdesc->description()->GetContentByName(*transceiver->mid());
3864 } else {
3865 // Plan B only allows at most one audio and one video section, so use the
3866 // first media section of that type.
3867 return cricket::GetFirstMediaContent(sdesc->description()->contents(),
3868 transceiver->media_type());
3869 }
3870 }
3871
GetOptionsForOffer(const PeerConnectionInterface::RTCOfferAnswerOptions & offer_answer_options,cricket::MediaSessionOptions * session_options)3872 void SdpOfferAnswerHandler::GetOptionsForOffer(
3873 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
3874 cricket::MediaSessionOptions* session_options) {
3875 RTC_DCHECK_RUN_ON(signaling_thread());
3876 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
3877
3878 if (IsUnifiedPlan()) {
3879 GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options);
3880 } else {
3881 GetOptionsForPlanBOffer(offer_answer_options, session_options);
3882 }
3883
3884 // Apply ICE restart flag and renomination flag.
3885 bool ice_restart = offer_answer_options.ice_restart || HasNewIceCredentials();
3886 for (auto& options : session_options->media_description_options) {
3887 options.transport_options.ice_restart = ice_restart;
3888 options.transport_options.enable_ice_renomination =
3889 pc_->configuration()->enable_ice_renomination;
3890 }
3891
3892 session_options->rtcp_cname = rtcp_cname_;
3893 session_options->crypto_options = pc_->GetCryptoOptions();
3894 session_options->pooled_ice_credentials =
3895 context_->network_thread()->BlockingCall(
3896 [this] { return port_allocator()->GetPooledIceCredentials(); });
3897 session_options->offer_extmap_allow_mixed =
3898 pc_->configuration()->offer_extmap_allow_mixed;
3899
3900 // Allow fallback for using obsolete SCTP syntax.
3901 // Note that the default in `session_options` is true, while
3902 // the default in `options` is false.
3903 session_options->use_obsolete_sctp_sdp =
3904 offer_answer_options.use_obsolete_sctp_sdp;
3905 }
3906
GetOptionsForPlanBOffer(const PeerConnectionInterface::RTCOfferAnswerOptions & offer_answer_options,cricket::MediaSessionOptions * session_options)3907 void SdpOfferAnswerHandler::GetOptionsForPlanBOffer(
3908 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
3909 cricket::MediaSessionOptions* session_options) {
3910 bool offer_new_data_description =
3911 data_channel_controller()->HasDataChannels();
3912 bool send_audio = false;
3913 bool send_video = false;
3914 bool recv_audio = false;
3915 bool recv_video = false;
3916 if (ConfiguredForMedia()) {
3917 // Figure out transceiver directional preferences.
3918 send_audio =
3919 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
3920 send_video =
3921 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
3922
3923 // By default, generate sendrecv/recvonly m= sections.
3924 recv_audio = true;
3925 recv_video = true;
3926 }
3927 // By default, only offer a new m= section if we have media to send with it.
3928 bool offer_new_audio_description = send_audio;
3929 bool offer_new_video_description = send_video;
3930 if (ConfiguredForMedia()) {
3931 // The "offer_to_receive_X" options allow those defaults to be overridden.
3932 if (offer_answer_options.offer_to_receive_audio !=
3933 PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) {
3934 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
3935 offer_new_audio_description =
3936 offer_new_audio_description ||
3937 (offer_answer_options.offer_to_receive_audio > 0);
3938 }
3939 if (offer_answer_options.offer_to_receive_video !=
3940 RTCOfferAnswerOptions::kUndefined) {
3941 recv_video = (offer_answer_options.offer_to_receive_video > 0);
3942 offer_new_video_description =
3943 offer_new_video_description ||
3944 (offer_answer_options.offer_to_receive_video > 0);
3945 }
3946 }
3947 absl::optional<size_t> audio_index;
3948 absl::optional<size_t> video_index;
3949 absl::optional<size_t> data_index;
3950 // If a current description exists, generate m= sections in the same order,
3951 // using the first audio/video/data section that appears and rejecting
3952 // extraneous ones.
3953 if (local_description()) {
3954 GenerateMediaDescriptionOptions(
3955 local_description(),
3956 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
3957 RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
3958 &audio_index, &video_index, &data_index, session_options);
3959 }
3960
3961 if (ConfiguredForMedia()) {
3962 // Add audio/video/data m= sections to the end if needed.
3963 if (!audio_index && offer_new_audio_description) {
3964 cricket::MediaDescriptionOptions options(
3965 cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
3966 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false);
3967 options.header_extensions =
3968 media_engine()->voice().GetRtpHeaderExtensions();
3969 session_options->media_description_options.push_back(options);
3970 audio_index = session_options->media_description_options.size() - 1;
3971 }
3972 if (!video_index && offer_new_video_description) {
3973 cricket::MediaDescriptionOptions options(
3974 cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
3975 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false);
3976 options.header_extensions =
3977 media_engine()->video().GetRtpHeaderExtensions();
3978 session_options->media_description_options.push_back(options);
3979 video_index = session_options->media_description_options.size() - 1;
3980 }
3981 cricket::MediaDescriptionOptions* audio_media_description_options =
3982 !audio_index
3983 ? nullptr
3984 : &session_options->media_description_options[*audio_index];
3985 cricket::MediaDescriptionOptions* video_media_description_options =
3986 !video_index
3987 ? nullptr
3988 : &session_options->media_description_options[*video_index];
3989
3990 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
3991 audio_media_description_options,
3992 video_media_description_options,
3993 offer_answer_options.num_simulcast_layers);
3994 }
3995 if (!data_index && offer_new_data_description) {
3996 session_options->media_description_options.push_back(
3997 GetMediaDescriptionOptionsForActiveData(cricket::CN_DATA));
3998 }
3999 }
4000
GetOptionsForUnifiedPlanOffer(const RTCOfferAnswerOptions & offer_answer_options,cricket::MediaSessionOptions * session_options)4001 void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanOffer(
4002 const RTCOfferAnswerOptions& offer_answer_options,
4003 cricket::MediaSessionOptions* session_options) {
4004 // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial
4005 // Offers) and 5.2.2 (Subsequent Offers).
4006 RTC_DCHECK_EQ(session_options->media_description_options.size(), 0);
4007 const ContentInfos no_infos;
4008 const ContentInfos& local_contents =
4009 (local_description() ? local_description()->description()->contents()
4010 : no_infos);
4011 const ContentInfos& remote_contents =
4012 (remote_description() ? remote_description()->description()->contents()
4013 : no_infos);
4014 // The mline indices that can be recycled. New transceivers should reuse these
4015 // slots first.
4016 std::queue<size_t> recycleable_mline_indices;
4017 // First, go through each media section that exists in either the local or
4018 // remote description and generate a media section in this offer for the
4019 // associated transceiver. If a media section can be recycled, generate a
4020 // default, rejected media section here that can be later overwritten.
4021 for (size_t i = 0;
4022 i < std::max(local_contents.size(), remote_contents.size()); ++i) {
4023 // Either `local_content` or `remote_content` is non-null.
4024 const ContentInfo* local_content =
4025 (i < local_contents.size() ? &local_contents[i] : nullptr);
4026 const ContentInfo* current_local_content =
4027 GetContentByIndex(current_local_description(), i);
4028 const ContentInfo* remote_content =
4029 (i < remote_contents.size() ? &remote_contents[i] : nullptr);
4030 const ContentInfo* current_remote_content =
4031 GetContentByIndex(current_remote_description(), i);
4032 bool had_been_rejected =
4033 (current_local_content && current_local_content->rejected) ||
4034 (current_remote_content && current_remote_content->rejected);
4035 const std::string& mid =
4036 (local_content ? local_content->name : remote_content->name);
4037 cricket::MediaType media_type =
4038 (local_content ? local_content->media_description()->type()
4039 : remote_content->media_description()->type());
4040 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
4041 media_type == cricket::MEDIA_TYPE_VIDEO) {
4042 // A media section is considered eligible for recycling if it is marked as
4043 // rejected in either the current local or current remote description.
4044 auto transceiver = transceivers()->FindByMid(mid);
4045 if (!transceiver) {
4046 // No associated transceiver. The media section has been stopped.
4047 recycleable_mline_indices.push(i);
4048 session_options->media_description_options.push_back(
4049 cricket::MediaDescriptionOptions(media_type, mid,
4050 RtpTransceiverDirection::kInactive,
4051 /*stopped=*/true));
4052 } else {
4053 // NOTE: a stopping transceiver should be treated as a stopped one in
4054 // createOffer as specified in
4055 // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
4056 if (had_been_rejected && transceiver->stopping()) {
4057 session_options->media_description_options.push_back(
4058 cricket::MediaDescriptionOptions(
4059 transceiver->media_type(), mid,
4060 RtpTransceiverDirection::kInactive,
4061 /*stopped=*/true));
4062 recycleable_mline_indices.push(i);
4063 } else {
4064 session_options->media_description_options.push_back(
4065 GetMediaDescriptionOptionsForTransceiver(
4066 transceiver->internal(), mid,
4067 /*is_create_offer=*/true));
4068 // CreateOffer shouldn't really cause any state changes in
4069 // PeerConnection, but we need a way to match new transceivers to new
4070 // media sections in SetLocalDescription and JSEP specifies this is
4071 // done by recording the index of the media section generated for the
4072 // transceiver in the offer.
4073 transceiver->internal()->set_mline_index(i);
4074 }
4075 }
4076 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4077 RTC_DCHECK(local_content->rejected);
4078 session_options->media_description_options.push_back(
4079 cricket::MediaDescriptionOptions(media_type, mid,
4080 RtpTransceiverDirection::kInactive,
4081 /*stopped=*/true));
4082 } else {
4083 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4084 if (had_been_rejected) {
4085 session_options->media_description_options.push_back(
4086 GetMediaDescriptionOptionsForRejectedData(mid));
4087 } else {
4088 RTC_CHECK(pc_->GetDataMid());
4089 if (mid == *(pc_->GetDataMid())) {
4090 session_options->media_description_options.push_back(
4091 GetMediaDescriptionOptionsForActiveData(mid));
4092 } else {
4093 session_options->media_description_options.push_back(
4094 GetMediaDescriptionOptionsForRejectedData(mid));
4095 }
4096 }
4097 }
4098 }
4099
4100 // Next, look for transceivers that are newly added (that is, are not stopped
4101 // and not associated). Reuse media sections marked as recyclable first,
4102 // otherwise append to the end of the offer. New media sections should be
4103 // added in the order they were added to the PeerConnection.
4104 if (ConfiguredForMedia()) {
4105 for (const auto& transceiver : transceivers()->ListInternal()) {
4106 if (transceiver->mid() || transceiver->stopping()) {
4107 continue;
4108 }
4109 size_t mline_index;
4110 if (!recycleable_mline_indices.empty()) {
4111 mline_index = recycleable_mline_indices.front();
4112 recycleable_mline_indices.pop();
4113 session_options->media_description_options[mline_index] =
4114 GetMediaDescriptionOptionsForTransceiver(
4115 transceiver, mid_generator_.GenerateString(),
4116 /*is_create_offer=*/true);
4117 } else {
4118 mline_index = session_options->media_description_options.size();
4119 session_options->media_description_options.push_back(
4120 GetMediaDescriptionOptionsForTransceiver(
4121 transceiver, mid_generator_.GenerateString(),
4122 /*is_create_offer=*/true));
4123 }
4124 // See comment above for why CreateOffer changes the transceiver's state.
4125 transceiver->set_mline_index(mline_index);
4126 }
4127 }
4128 // Lastly, add a m-section if we have local data channels and an m section
4129 // does not already exist.
4130 if (!pc_->GetDataMid() && data_channel_controller()->HasDataChannels()) {
4131 session_options->media_description_options.push_back(
4132 GetMediaDescriptionOptionsForActiveData(
4133 mid_generator_.GenerateString()));
4134 }
4135 }
4136
GetOptionsForAnswer(const RTCOfferAnswerOptions & offer_answer_options,cricket::MediaSessionOptions * session_options)4137 void SdpOfferAnswerHandler::GetOptionsForAnswer(
4138 const RTCOfferAnswerOptions& offer_answer_options,
4139 cricket::MediaSessionOptions* session_options) {
4140 RTC_DCHECK_RUN_ON(signaling_thread());
4141 ExtractSharedMediaSessionOptions(offer_answer_options, session_options);
4142
4143 if (IsUnifiedPlan()) {
4144 GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options);
4145 } else {
4146 GetOptionsForPlanBAnswer(offer_answer_options, session_options);
4147 }
4148
4149 // Apply ICE renomination flag.
4150 for (auto& options : session_options->media_description_options) {
4151 options.transport_options.enable_ice_renomination =
4152 pc_->configuration()->enable_ice_renomination;
4153 }
4154
4155 session_options->rtcp_cname = rtcp_cname_;
4156 session_options->crypto_options = pc_->GetCryptoOptions();
4157 session_options->pooled_ice_credentials =
4158 context_->network_thread()->BlockingCall(
4159 [this] { return port_allocator()->GetPooledIceCredentials(); });
4160 }
4161
GetOptionsForPlanBAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions & offer_answer_options,cricket::MediaSessionOptions * session_options)4162 void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer(
4163 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4164 cricket::MediaSessionOptions* session_options) {
4165 bool send_audio = false;
4166 bool recv_audio = false;
4167 bool send_video = false;
4168 bool recv_video = false;
4169
4170 if (ConfiguredForMedia()) {
4171 // Figure out transceiver directional preferences.
4172 send_audio =
4173 !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty();
4174 send_video =
4175 !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty();
4176
4177 // By default, generate sendrecv/recvonly m= sections. The direction is also
4178 // restricted by the direction in the offer.
4179 recv_audio = true;
4180 recv_video = true;
4181
4182 // The "offer_to_receive_X" options allow those defaults to be overridden.
4183 if (offer_answer_options.offer_to_receive_audio !=
4184 RTCOfferAnswerOptions::kUndefined) {
4185 recv_audio = (offer_answer_options.offer_to_receive_audio > 0);
4186 }
4187 if (offer_answer_options.offer_to_receive_video !=
4188 RTCOfferAnswerOptions::kUndefined) {
4189 recv_video = (offer_answer_options.offer_to_receive_video > 0);
4190 }
4191 }
4192
4193 absl::optional<size_t> audio_index;
4194 absl::optional<size_t> video_index;
4195 absl::optional<size_t> data_index;
4196
4197 // Generate m= sections that match those in the offer.
4198 // Note that mediasession.cc will handle intersection our preferred
4199 // direction with the offered direction.
4200 GenerateMediaDescriptionOptions(
4201 remote_description(),
4202 RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
4203 RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index,
4204 &video_index, &data_index, session_options);
4205
4206 cricket::MediaDescriptionOptions* audio_media_description_options =
4207 !audio_index ? nullptr
4208 : &session_options->media_description_options[*audio_index];
4209 cricket::MediaDescriptionOptions* video_media_description_options =
4210 !video_index ? nullptr
4211 : &session_options->media_description_options[*video_index];
4212
4213 if (ConfiguredForMedia()) {
4214 AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(),
4215 audio_media_description_options,
4216 video_media_description_options,
4217 offer_answer_options.num_simulcast_layers);
4218 }
4219 }
4220
GetOptionsForUnifiedPlanAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions & offer_answer_options,cricket::MediaSessionOptions * session_options)4221 void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanAnswer(
4222 const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options,
4223 cricket::MediaSessionOptions* session_options) {
4224 // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial
4225 // Answers) and 5.3.2 (Subsequent Answers).
4226 RTC_DCHECK(remote_description());
4227 RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer);
4228 for (const ContentInfo& content :
4229 remote_description()->description()->contents()) {
4230 cricket::MediaType media_type = content.media_description()->type();
4231 if (media_type == cricket::MEDIA_TYPE_AUDIO ||
4232 media_type == cricket::MEDIA_TYPE_VIDEO) {
4233 auto transceiver = transceivers()->FindByMid(content.name);
4234 if (transceiver) {
4235 session_options->media_description_options.push_back(
4236 GetMediaDescriptionOptionsForTransceiver(
4237 transceiver->internal(), content.name,
4238 /*is_create_offer=*/false));
4239 } else {
4240 // This should only happen with rejected transceivers.
4241 RTC_DCHECK(content.rejected);
4242 session_options->media_description_options.push_back(
4243 cricket::MediaDescriptionOptions(media_type, content.name,
4244 RtpTransceiverDirection::kInactive,
4245 /*stopped=*/true));
4246 }
4247 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
4248 RTC_DCHECK(content.rejected);
4249 session_options->media_description_options.push_back(
4250 cricket::MediaDescriptionOptions(media_type, content.name,
4251 RtpTransceiverDirection::kInactive,
4252 /*stopped=*/true));
4253 } else {
4254 RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
4255 // Reject all data sections if data channels are disabled.
4256 // Reject a data section if it has already been rejected.
4257 // Reject all data sections except for the first one.
4258 if (content.rejected || content.name != *(pc_->GetDataMid())) {
4259 session_options->media_description_options.push_back(
4260 GetMediaDescriptionOptionsForRejectedData(content.name));
4261 } else {
4262 session_options->media_description_options.push_back(
4263 GetMediaDescriptionOptionsForActiveData(content.name));
4264 }
4265 }
4266 }
4267 }
4268
SessionErrorToString(SessionError error) const4269 const char* SdpOfferAnswerHandler::SessionErrorToString(
4270 SessionError error) const {
4271 switch (error) {
4272 case SessionError::kNone:
4273 return "ERROR_NONE";
4274 case SessionError::kContent:
4275 return "ERROR_CONTENT";
4276 case SessionError::kTransport:
4277 return "ERROR_TRANSPORT";
4278 }
4279 RTC_DCHECK_NOTREACHED();
4280 return "";
4281 }
4282
GetSessionErrorMsg()4283 std::string SdpOfferAnswerHandler::GetSessionErrorMsg() {
4284 RTC_DCHECK_RUN_ON(signaling_thread());
4285 rtc::StringBuilder desc;
4286 desc << kSessionError << SessionErrorToString(session_error()) << ". ";
4287 desc << kSessionErrorDesc << session_error_desc() << ".";
4288 return desc.Release();
4289 }
4290
SetSessionError(SessionError error,const std::string & error_desc)4291 void SdpOfferAnswerHandler::SetSessionError(SessionError error,
4292 const std::string& error_desc) {
4293 RTC_DCHECK_RUN_ON(signaling_thread());
4294 if (error != session_error_) {
4295 session_error_ = error;
4296 session_error_desc_ = error_desc;
4297 }
4298 }
4299
HandleLegacyOfferOptions(const PeerConnectionInterface::RTCOfferAnswerOptions & options)4300 RTCError SdpOfferAnswerHandler::HandleLegacyOfferOptions(
4301 const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
4302 RTC_DCHECK_RUN_ON(signaling_thread());
4303 RTC_DCHECK(IsUnifiedPlan());
4304
4305 if (options.offer_to_receive_audio == 0) {
4306 RemoveRecvDirectionFromReceivingTransceiversOfType(
4307 cricket::MEDIA_TYPE_AUDIO);
4308 } else if (options.offer_to_receive_audio == 1) {
4309 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO);
4310 } else if (options.offer_to_receive_audio > 1) {
4311 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4312 "offer_to_receive_audio > 1 is not supported.");
4313 }
4314
4315 if (options.offer_to_receive_video == 0) {
4316 RemoveRecvDirectionFromReceivingTransceiversOfType(
4317 cricket::MEDIA_TYPE_VIDEO);
4318 } else if (options.offer_to_receive_video == 1) {
4319 AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO);
4320 } else if (options.offer_to_receive_video > 1) {
4321 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
4322 "offer_to_receive_video > 1 is not supported.");
4323 }
4324
4325 return RTCError::OK();
4326 }
4327
RemoveRecvDirectionFromReceivingTransceiversOfType(cricket::MediaType media_type)4328 void SdpOfferAnswerHandler::RemoveRecvDirectionFromReceivingTransceiversOfType(
4329 cricket::MediaType media_type) {
4330 for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) {
4331 RtpTransceiverDirection new_direction =
4332 RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false);
4333 if (new_direction != transceiver->direction()) {
4334 RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type)
4335 << " transceiver (MID="
4336 << transceiver->mid().value_or("<not set>") << ") from "
4337 << RtpTransceiverDirectionToString(
4338 transceiver->direction())
4339 << " to "
4340 << RtpTransceiverDirectionToString(new_direction)
4341 << " since CreateOffer specified offer_to_receive=0";
4342 transceiver->internal()->set_direction(new_direction);
4343 }
4344 }
4345 }
4346
AddUpToOneReceivingTransceiverOfType(cricket::MediaType media_type)4347 void SdpOfferAnswerHandler::AddUpToOneReceivingTransceiverOfType(
4348 cricket::MediaType media_type) {
4349 RTC_DCHECK_RUN_ON(signaling_thread());
4350 if (GetReceivingTransceiversOfType(media_type).empty()) {
4351 RTC_LOG(LS_INFO)
4352 << "Adding one recvonly " << cricket::MediaTypeToString(media_type)
4353 << " transceiver since CreateOffer specified offer_to_receive=1";
4354 RtpTransceiverInit init;
4355 init.direction = RtpTransceiverDirection::kRecvOnly;
4356 pc_->AddTransceiver(media_type, nullptr, init,
4357 /*update_negotiation_needed=*/false);
4358 }
4359 }
4360
4361 std::vector<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
GetReceivingTransceiversOfType(cricket::MediaType media_type)4362 SdpOfferAnswerHandler::GetReceivingTransceiversOfType(
4363 cricket::MediaType media_type) {
4364 std::vector<
4365 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
4366 receiving_transceivers;
4367 for (const auto& transceiver : transceivers()->List()) {
4368 if (!transceiver->stopped() && transceiver->media_type() == media_type &&
4369 RtpTransceiverDirectionHasRecv(transceiver->direction())) {
4370 receiving_transceivers.push_back(transceiver);
4371 }
4372 }
4373 return receiving_transceivers;
4374 }
4375
ProcessRemovalOfRemoteTrack(rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>> transceiver,std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> * remove_list,std::vector<rtc::scoped_refptr<MediaStreamInterface>> * removed_streams)4376 void SdpOfferAnswerHandler::ProcessRemovalOfRemoteTrack(
4377 rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
4378 transceiver,
4379 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>* remove_list,
4380 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4381 RTC_DCHECK(transceiver->mid());
4382 RTC_LOG(LS_INFO) << "Processing the removal of a track for MID="
4383 << *transceiver->mid();
4384 std::vector<rtc::scoped_refptr<MediaStreamInterface>> previous_streams =
4385 transceiver->internal()->receiver_internal()->streams();
4386 // This will remove the remote track from the streams.
4387 transceiver->internal()->receiver_internal()->set_stream_ids({});
4388 remove_list->push_back(transceiver);
4389 RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams);
4390 }
4391
RemoveRemoteStreamsIfEmpty(const std::vector<rtc::scoped_refptr<MediaStreamInterface>> & remote_streams,std::vector<rtc::scoped_refptr<MediaStreamInterface>> * removed_streams)4392 void SdpOfferAnswerHandler::RemoveRemoteStreamsIfEmpty(
4393 const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& remote_streams,
4394 std::vector<rtc::scoped_refptr<MediaStreamInterface>>* removed_streams) {
4395 RTC_DCHECK_RUN_ON(signaling_thread());
4396 // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of
4397 // streams, see if the stream was removed by checking if this was the last
4398 // receiver with that stream ID.
4399 for (const auto& remote_stream : remote_streams) {
4400 if (remote_stream->GetAudioTracks().empty() &&
4401 remote_stream->GetVideoTracks().empty()) {
4402 remote_streams_->RemoveStream(remote_stream.get());
4403 removed_streams->push_back(remote_stream);
4404 }
4405 }
4406 }
4407
RemoveSenders(cricket::MediaType media_type)4408 void SdpOfferAnswerHandler::RemoveSenders(cricket::MediaType media_type) {
4409 RTC_DCHECK_RUN_ON(signaling_thread());
4410 UpdateLocalSenders(std::vector<cricket::StreamParams>(), media_type);
4411 UpdateRemoteSendersList(std::vector<cricket::StreamParams>(), false,
4412 media_type, nullptr);
4413 }
4414
UpdateLocalSenders(const std::vector<cricket::StreamParams> & streams,cricket::MediaType media_type)4415 void SdpOfferAnswerHandler::UpdateLocalSenders(
4416 const std::vector<cricket::StreamParams>& streams,
4417 cricket::MediaType media_type) {
4418 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateLocalSenders");
4419 RTC_DCHECK_RUN_ON(signaling_thread());
4420 std::vector<RtpSenderInfo>* current_senders =
4421 rtp_manager()->GetLocalSenderInfos(media_type);
4422
4423 // Find removed tracks. I.e., tracks where the track id, stream id or ssrc
4424 // don't match the new StreamParam.
4425 for (auto sender_it = current_senders->begin();
4426 sender_it != current_senders->end();
4427 /* incremented manually */) {
4428 const RtpSenderInfo& info = *sender_it;
4429 const cricket::StreamParams* params =
4430 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4431 if (!params || params->id != info.sender_id ||
4432 params->first_stream_id() != info.stream_id) {
4433 rtp_manager()->OnLocalSenderRemoved(info, media_type);
4434 sender_it = current_senders->erase(sender_it);
4435 } else {
4436 ++sender_it;
4437 }
4438 }
4439
4440 // Find new and active senders.
4441 for (const cricket::StreamParams& params : streams) {
4442 // The sync_label is the MediaStream label and the `stream.id` is the
4443 // sender id.
4444 const std::string& stream_id = params.first_stream_id();
4445 const std::string& sender_id = params.id;
4446 uint32_t ssrc = params.first_ssrc();
4447 const RtpSenderInfo* sender_info =
4448 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
4449 if (!sender_info) {
4450 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
4451 rtp_manager()->OnLocalSenderAdded(current_senders->back(), media_type);
4452 }
4453 }
4454 }
4455
UpdateRemoteSendersList(const cricket::StreamParamsVec & streams,bool default_sender_needed,cricket::MediaType media_type,StreamCollection * new_streams)4456 void SdpOfferAnswerHandler::UpdateRemoteSendersList(
4457 const cricket::StreamParamsVec& streams,
4458 bool default_sender_needed,
4459 cricket::MediaType media_type,
4460 StreamCollection* new_streams) {
4461 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateRemoteSendersList");
4462 RTC_DCHECK_RUN_ON(signaling_thread());
4463 RTC_DCHECK(!IsUnifiedPlan());
4464
4465 std::vector<RtpSenderInfo>* current_senders =
4466 rtp_manager()->GetRemoteSenderInfos(media_type);
4467
4468 // Find removed senders. I.e., senders where the sender id or ssrc don't match
4469 // the new StreamParam.
4470 for (auto sender_it = current_senders->begin();
4471 sender_it != current_senders->end();
4472 /* incremented manually */) {
4473 const RtpSenderInfo& info = *sender_it;
4474 const cricket::StreamParams* params =
4475 cricket::GetStreamBySsrc(streams, info.first_ssrc);
4476 std::string params_stream_id;
4477 if (params) {
4478 params_stream_id =
4479 (!params->first_stream_id().empty() ? params->first_stream_id()
4480 : kDefaultStreamId);
4481 }
4482 bool sender_exists = params && params->id == info.sender_id &&
4483 params_stream_id == info.stream_id;
4484 // If this is a default track, and we still need it, don't remove it.
4485 if ((info.stream_id == kDefaultStreamId && default_sender_needed) ||
4486 sender_exists) {
4487 ++sender_it;
4488 } else {
4489 rtp_manager()->OnRemoteSenderRemoved(
4490 info, remote_streams_->find(info.stream_id), media_type);
4491 sender_it = current_senders->erase(sender_it);
4492 }
4493 }
4494
4495 // Find new and active senders.
4496 for (const cricket::StreamParams& params : streams) {
4497 if (!params.has_ssrcs()) {
4498 // The remote endpoint has streams, but didn't signal ssrcs. For an active
4499 // sender, this means it is coming from a Unified Plan endpoint,so we just
4500 // create a default.
4501 default_sender_needed = true;
4502 break;
4503 }
4504
4505 // `params.id` is the sender id and the stream id uses the first of
4506 // `params.stream_ids`. The remote description could come from a Unified
4507 // Plan endpoint, with multiple or no stream_ids() signaled. Since this is
4508 // not supported in Plan B, we just take the first here and create the
4509 // default stream ID if none is specified.
4510 const std::string& stream_id =
4511 (!params.first_stream_id().empty() ? params.first_stream_id()
4512 : kDefaultStreamId);
4513 const std::string& sender_id = params.id;
4514 uint32_t ssrc = params.first_ssrc();
4515
4516 rtc::scoped_refptr<MediaStreamInterface> stream(
4517 remote_streams_->find(stream_id));
4518 if (!stream) {
4519 // This is a new MediaStream. Create a new remote MediaStream.
4520 stream = MediaStreamProxy::Create(rtc::Thread::Current(),
4521 MediaStream::Create(stream_id));
4522 remote_streams_->AddStream(stream);
4523 new_streams->AddStream(stream);
4524 }
4525
4526 const RtpSenderInfo* sender_info =
4527 rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id);
4528 if (!sender_info) {
4529 current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc));
4530 rtp_manager()->OnRemoteSenderAdded(current_senders->back(), stream.get(),
4531 media_type);
4532 }
4533 }
4534
4535 // Add default sender if necessary.
4536 if (default_sender_needed) {
4537 rtc::scoped_refptr<MediaStreamInterface> default_stream(
4538 remote_streams_->find(kDefaultStreamId));
4539 if (!default_stream) {
4540 // Create the new default MediaStream.
4541 default_stream = MediaStreamProxy::Create(
4542 rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId));
4543 remote_streams_->AddStream(default_stream);
4544 new_streams->AddStream(default_stream);
4545 }
4546 std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO)
4547 ? kDefaultAudioSenderId
4548 : kDefaultVideoSenderId;
4549 const RtpSenderInfo* default_sender_info = rtp_manager()->FindSenderInfo(
4550 *current_senders, kDefaultStreamId, default_sender_id);
4551 if (!default_sender_info) {
4552 current_senders->push_back(
4553 RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0));
4554 rtp_manager()->OnRemoteSenderAdded(current_senders->back(),
4555 default_stream.get(), media_type);
4556 }
4557 }
4558 }
4559
EnableSending()4560 void SdpOfferAnswerHandler::EnableSending() {
4561 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::EnableSending");
4562 RTC_DCHECK_RUN_ON(signaling_thread());
4563 if (!ConfiguredForMedia()) {
4564 return;
4565 }
4566 for (const auto& transceiver : transceivers()->ListInternal()) {
4567 cricket::ChannelInterface* channel = transceiver->channel();
4568 if (channel) {
4569 channel->Enable(true);
4570 }
4571 }
4572 }
4573
PushdownMediaDescription(SdpType type,cricket::ContentSource source,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)4574 RTCError SdpOfferAnswerHandler::PushdownMediaDescription(
4575 SdpType type,
4576 cricket::ContentSource source,
4577 const std::map<std::string, const cricket::ContentGroup*>&
4578 bundle_groups_by_mid) {
4579 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownMediaDescription");
4580 const SessionDescriptionInterface* sdesc =
4581 (source == cricket::CS_LOCAL ? local_description()
4582 : remote_description());
4583 RTC_DCHECK_RUN_ON(signaling_thread());
4584 RTC_DCHECK(sdesc);
4585
4586 if (ConfiguredForMedia()) {
4587 // Note: This will perform a BlockingCall over to the worker thread, which
4588 // we'll also do in a loop below.
4589 if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) {
4590 // Note that this is never expected to fail, since RtpDemuxer doesn't
4591 // return an error when changing payload type demux criteria, which is all
4592 // this does.
4593 return RTCError(RTCErrorType::INTERNAL_ERROR,
4594 "Failed to update payload type demuxing state.");
4595 }
4596
4597 // Push down the new SDP media section for each audio/video transceiver.
4598 auto rtp_transceivers = transceivers()->ListInternal();
4599 std::vector<
4600 std::pair<cricket::ChannelInterface*, const MediaContentDescription*>>
4601 channels;
4602 for (const auto& transceiver : rtp_transceivers) {
4603 const ContentInfo* content_info =
4604 FindMediaSectionForTransceiver(transceiver, sdesc);
4605 cricket::ChannelInterface* channel = transceiver->channel();
4606 if (!channel || !content_info || content_info->rejected) {
4607 continue;
4608 }
4609 const MediaContentDescription* content_desc =
4610 content_info->media_description();
4611 if (!content_desc) {
4612 continue;
4613 }
4614
4615 transceiver->OnNegotiationUpdate(type, content_desc);
4616 channels.push_back(std::make_pair(channel, content_desc));
4617 }
4618
4619 // This for-loop of invokes helps audio impairment during re-negotiations.
4620 // One of the causes is that downstairs decoder creation is synchronous at
4621 // the moment, and that a decoder is created for each codec listed in the
4622 // SDP.
4623 //
4624 // TODO(bugs.webrtc.org/12840): consider merging the invokes again after
4625 // these projects have shipped:
4626 // - bugs.webrtc.org/12462
4627 // - crbug.com/1157227
4628 // - crbug.com/1187289
4629 for (const auto& entry : channels) {
4630 std::string error;
4631 bool success =
4632 context_->worker_thread()->BlockingCall([&]() {
4633 return (source == cricket::CS_LOCAL)
4634 ? entry.first->SetLocalContent(entry.second, type, error)
4635 : entry.first->SetRemoteContent(entry.second, type,
4636 error);
4637 });
4638 if (!success) {
4639 return RTCError(RTCErrorType::INVALID_PARAMETER, error);
4640 }
4641 }
4642 }
4643 // Need complete offer/answer with an SCTP m= section before starting SCTP,
4644 // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
4645 if (pc_->sctp_mid() && local_description() && remote_description()) {
4646 auto local_sctp_description = cricket::GetFirstSctpDataContentDescription(
4647 local_description()->description());
4648 auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription(
4649 remote_description()->description());
4650 if (local_sctp_description && remote_sctp_description) {
4651 int max_message_size;
4652 // A remote max message size of zero means "any size supported".
4653 // We configure the connection with our own max message size.
4654 if (remote_sctp_description->max_message_size() == 0) {
4655 max_message_size = local_sctp_description->max_message_size();
4656 } else {
4657 max_message_size =
4658 std::min(local_sctp_description->max_message_size(),
4659 remote_sctp_description->max_message_size());
4660 }
4661 pc_->StartSctpTransport(local_sctp_description->port(),
4662 remote_sctp_description->port(),
4663 max_message_size);
4664 }
4665 }
4666
4667 return RTCError::OK();
4668 }
4669
PushdownTransportDescription(cricket::ContentSource source,SdpType type)4670 RTCError SdpOfferAnswerHandler::PushdownTransportDescription(
4671 cricket::ContentSource source,
4672 SdpType type) {
4673 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::PushdownTransportDescription");
4674 RTC_DCHECK_RUN_ON(signaling_thread());
4675
4676 if (source == cricket::CS_LOCAL) {
4677 const SessionDescriptionInterface* sdesc = local_description();
4678 RTC_DCHECK(sdesc);
4679 return transport_controller_s()->SetLocalDescription(type,
4680 sdesc->description());
4681 } else {
4682 const SessionDescriptionInterface* sdesc = remote_description();
4683 RTC_DCHECK(sdesc);
4684 return transport_controller_s()->SetRemoteDescription(type,
4685 sdesc->description());
4686 }
4687 }
4688
RemoveStoppedTransceivers()4689 void SdpOfferAnswerHandler::RemoveStoppedTransceivers() {
4690 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::RemoveStoppedTransceivers");
4691 RTC_DCHECK_RUN_ON(signaling_thread());
4692 // 3.2.10.1: For each transceiver in the connection's set of transceivers
4693 // run the following steps:
4694 if (!IsUnifiedPlan())
4695 return;
4696 if (!ConfiguredForMedia()) {
4697 return;
4698 }
4699 // Traverse a copy of the transceiver list.
4700 auto transceiver_list = transceivers()->List();
4701 for (auto transceiver : transceiver_list) {
4702 // 3.2.10.1.1: If transceiver is stopped, associated with an m= section
4703 // and the associated m= section is rejected in
4704 // connection.[[CurrentLocalDescription]] or
4705 // connection.[[CurrentRemoteDescription]], remove the
4706 // transceiver from the connection's set of transceivers.
4707 if (!transceiver->stopped()) {
4708 continue;
4709 }
4710 const ContentInfo* local_content = FindMediaSectionForTransceiver(
4711 transceiver->internal(), local_description());
4712 const ContentInfo* remote_content = FindMediaSectionForTransceiver(
4713 transceiver->internal(), remote_description());
4714 if ((local_content && local_content->rejected) ||
4715 (remote_content && remote_content->rejected)) {
4716 RTC_LOG(LS_INFO) << "Dissociating transceiver"
4717 " since the media section is being recycled.";
4718 transceiver->internal()->set_mid(absl::nullopt);
4719 transceiver->internal()->set_mline_index(absl::nullopt);
4720 } else if (!local_content && !remote_content) {
4721 // TODO(bugs.webrtc.org/11973): Consider if this should be removed already
4722 // See https://github.com/w3c/webrtc-pc/issues/2576
4723 RTC_LOG(LS_INFO)
4724 << "Dropping stopped transceiver that was never associated";
4725 }
4726 transceivers()->Remove(transceiver);
4727 }
4728 }
4729
RemoveUnusedChannels(const SessionDescription * desc)4730 void SdpOfferAnswerHandler::RemoveUnusedChannels(
4731 const SessionDescription* desc) {
4732 RTC_DCHECK_RUN_ON(signaling_thread());
4733 if (ConfiguredForMedia()) {
4734 // Destroy video channel first since it may have a pointer to the
4735 // voice channel.
4736 const cricket::ContentInfo* video_info =
4737 cricket::GetFirstVideoContent(desc);
4738 if (!video_info || video_info->rejected) {
4739 rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel();
4740 }
4741
4742 const cricket::ContentInfo* audio_info =
4743 cricket::GetFirstAudioContent(desc);
4744 if (!audio_info || audio_info->rejected) {
4745 rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel();
4746 }
4747 }
4748 const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc);
4749 if (!data_info) {
4750 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA,
4751 "No data channel section in the description.");
4752 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
4753 DestroyDataChannelTransport(error);
4754 } else if (data_info->rejected) {
4755 rtc::StringBuilder sb;
4756 sb << "Rejected data channel with mid=" << data_info->name << ".";
4757
4758 RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, sb.Release());
4759 error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE);
4760 DestroyDataChannelTransport(error);
4761 }
4762 }
4763
UpdateEndedRemoteMediaStreams()4764 void SdpOfferAnswerHandler::UpdateEndedRemoteMediaStreams() {
4765 RTC_DCHECK_RUN_ON(signaling_thread());
4766 std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_to_remove;
4767 for (size_t i = 0; i < remote_streams_->count(); ++i) {
4768 MediaStreamInterface* stream = remote_streams_->at(i);
4769 if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) {
4770 streams_to_remove.push_back(
4771 rtc::scoped_refptr<MediaStreamInterface>(stream));
4772 }
4773 }
4774
4775 for (auto& stream : streams_to_remove) {
4776 remote_streams_->RemoveStream(stream.get());
4777 pc_->Observer()->OnRemoveStream(std::move(stream));
4778 }
4779 }
4780
UseCandidatesInRemoteDescription()4781 bool SdpOfferAnswerHandler::UseCandidatesInRemoteDescription() {
4782 RTC_DCHECK_RUN_ON(signaling_thread());
4783 auto* remote_desc = remote_description();
4784 if (!remote_desc) {
4785 return true;
4786 }
4787 bool ret = true;
4788
4789 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
4790 const IceCandidateCollection* candidates = remote_desc->candidates(m);
4791 for (size_t n = 0; n < candidates->count(); ++n) {
4792 const IceCandidateInterface* candidate = candidates->at(n);
4793 bool valid = false;
4794 if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) {
4795 if (valid) {
4796 RTC_LOG(LS_INFO)
4797 << "UseCandidatesInRemoteDescription: Not ready to use "
4798 "candidate.";
4799 }
4800 continue;
4801 }
4802 ret = UseCandidate(candidate);
4803 if (!ret) {
4804 break;
4805 }
4806 }
4807 }
4808 return ret;
4809 }
4810
UseCandidate(const IceCandidateInterface * candidate)4811 bool SdpOfferAnswerHandler::UseCandidate(
4812 const IceCandidateInterface* candidate) {
4813 RTC_DCHECK_RUN_ON(signaling_thread());
4814
4815 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
4816
4817 RTCErrorOr<const cricket::ContentInfo*> result =
4818 FindContentInfo(remote_description(), candidate);
4819 if (!result.ok())
4820 return false;
4821
4822 const cricket::Candidate& c = candidate->candidate();
4823 RTCError error = cricket::VerifyCandidate(c);
4824 if (!error.ok()) {
4825 RTC_LOG(LS_WARNING) << "Invalid candidate: " << c.ToString();
4826 return true;
4827 }
4828
4829 pc_->AddRemoteCandidate(result.value()->name, c);
4830
4831 return true;
4832 }
4833
4834 // We need to check the local/remote description for the Transport instead of
4835 // the session, because a new Transport added during renegotiation may have
4836 // them unset while the session has them set from the previous negotiation.
4837 // Not doing so may trigger the auto generation of transport description and
4838 // mess up DTLS identity information, ICE credential, etc.
ReadyToUseRemoteCandidate(const IceCandidateInterface * candidate,const SessionDescriptionInterface * remote_desc,bool * valid)4839 bool SdpOfferAnswerHandler::ReadyToUseRemoteCandidate(
4840 const IceCandidateInterface* candidate,
4841 const SessionDescriptionInterface* remote_desc,
4842 bool* valid) {
4843 RTC_DCHECK_RUN_ON(signaling_thread());
4844 *valid = true;
4845
4846 const SessionDescriptionInterface* current_remote_desc =
4847 remote_desc ? remote_desc : remote_description();
4848
4849 if (!current_remote_desc) {
4850 return false;
4851 }
4852
4853 RTCErrorOr<const cricket::ContentInfo*> result =
4854 FindContentInfo(current_remote_desc, candidate);
4855 if (!result.ok()) {
4856 RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. "
4857 << result.error().message();
4858
4859 *valid = false;
4860 return false;
4861 }
4862
4863 return true;
4864 }
4865
FindContentInfo(const SessionDescriptionInterface * description,const IceCandidateInterface * candidate)4866 RTCErrorOr<const cricket::ContentInfo*> SdpOfferAnswerHandler::FindContentInfo(
4867 const SessionDescriptionInterface* description,
4868 const IceCandidateInterface* candidate) {
4869 if (!candidate->sdp_mid().empty()) {
4870 auto& contents = description->description()->contents();
4871 auto it = absl::c_find_if(
4872 contents, [candidate](const cricket::ContentInfo& content_info) {
4873 return content_info.mid() == candidate->sdp_mid();
4874 });
4875 if (it == contents.end()) {
4876 return RTCError(
4877 RTCErrorType::INVALID_PARAMETER,
4878 "Mid " + candidate->sdp_mid() +
4879 " specified but no media section with that mid found.");
4880 } else {
4881 return &*it;
4882 }
4883 } else if (candidate->sdp_mline_index() >= 0) {
4884 size_t mediacontent_index =
4885 static_cast<size_t>(candidate->sdp_mline_index());
4886 size_t content_size = description->description()->contents().size();
4887 if (mediacontent_index < content_size) {
4888 return &description->description()->contents()[mediacontent_index];
4889 } else {
4890 return RTCError(RTCErrorType::INVALID_RANGE,
4891 "Media line index (" +
4892 rtc::ToString(candidate->sdp_mline_index()) +
4893 ") out of range (number of mlines: " +
4894 rtc::ToString(content_size) + ").");
4895 }
4896 }
4897
4898 return RTCError(RTCErrorType::INVALID_PARAMETER,
4899 "Neither sdp_mline_index nor sdp_mid specified.");
4900 }
4901
CreateChannels(const SessionDescription & desc)4902 RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) {
4903 TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateChannels");
4904 // Creating the media channels. Transports should already have been created
4905 // at this point.
4906 RTC_DCHECK_RUN_ON(signaling_thread());
4907 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc);
4908 if (voice && !voice->rejected &&
4909 !rtp_manager()->GetAudioTransceiver()->internal()->channel()) {
4910 auto error =
4911 rtp_manager()->GetAudioTransceiver()->internal()->CreateChannel(
4912 voice->name, pc_->call_ptr(), pc_->configuration()->media_config,
4913 pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(),
4914 video_options(), video_bitrate_allocator_factory_.get(),
4915 [&](absl::string_view mid) {
4916 RTC_DCHECK_RUN_ON(network_thread());
4917 return transport_controller_n()->GetRtpTransport(mid);
4918 });
4919 if (!error.ok()) {
4920 return error;
4921 }
4922 }
4923
4924 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc);
4925 if (video && !video->rejected &&
4926 !rtp_manager()->GetVideoTransceiver()->internal()->channel()) {
4927 auto error =
4928 rtp_manager()->GetVideoTransceiver()->internal()->CreateChannel(
4929 video->name, pc_->call_ptr(), pc_->configuration()->media_config,
4930 pc_->SrtpRequired(), pc_->GetCryptoOptions(),
4931
4932 audio_options(), video_options(),
4933 video_bitrate_allocator_factory_.get(), [&](absl::string_view mid) {
4934 RTC_DCHECK_RUN_ON(network_thread());
4935 return transport_controller_n()->GetRtpTransport(mid);
4936 });
4937 if (!error.ok()) {
4938 return error;
4939 }
4940 }
4941
4942 const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc);
4943 if (data && !data->rejected &&
4944 !data_channel_controller()->data_channel_transport()) {
4945 if (!CreateDataChannel(data->name)) {
4946 return RTCError(RTCErrorType::INTERNAL_ERROR,
4947 "Failed to create data channel.");
4948 }
4949 }
4950
4951 return RTCError::OK();
4952 }
4953
CreateDataChannel(const std::string & mid)4954 bool SdpOfferAnswerHandler::CreateDataChannel(const std::string& mid) {
4955 RTC_DCHECK_RUN_ON(signaling_thread());
4956 if (!context_->network_thread()->BlockingCall([this, &mid] {
4957 RTC_DCHECK_RUN_ON(context_->network_thread());
4958 return pc_->SetupDataChannelTransport_n(mid);
4959 })) {
4960 return false;
4961 }
4962 // TODO(tommi): Is this necessary? SetupDataChannelTransport_n() above
4963 // will have queued up updating the transport name on the signaling thread
4964 // and could update the mid at the same time. This here is synchronous
4965 // though, but it changes the state of PeerConnection and makes it be
4966 // out of sync (transport name not set while the mid is set).
4967 pc_->SetSctpDataMid(mid);
4968 return true;
4969 }
4970
DestroyDataChannelTransport(RTCError error)4971 void SdpOfferAnswerHandler::DestroyDataChannelTransport(RTCError error) {
4972 RTC_DCHECK_RUN_ON(signaling_thread());
4973 const bool has_sctp = pc_->sctp_mid().has_value();
4974
4975 if (has_sctp)
4976 data_channel_controller()->OnTransportChannelClosed(error);
4977
4978 context_->network_thread()->BlockingCall([this] {
4979 RTC_DCHECK_RUN_ON(context_->network_thread());
4980 pc_->TeardownDataChannelTransport_n();
4981 });
4982
4983 if (has_sctp)
4984 pc_->ResetSctpDataMid();
4985 }
4986
DestroyAllChannels()4987 void SdpOfferAnswerHandler::DestroyAllChannels() {
4988 RTC_DCHECK_RUN_ON(signaling_thread());
4989 if (!transceivers()) {
4990 return;
4991 }
4992
4993 RTC_LOG_THREAD_BLOCK_COUNT();
4994
4995 // Destroy video channels first since they may have a pointer to a voice
4996 // channel.
4997 auto list = transceivers()->List();
4998 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
4999
5000 for (const auto& transceiver : list) {
5001 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
5002 transceiver->internal()->ClearChannel();
5003 }
5004 }
5005 for (const auto& transceiver : list) {
5006 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
5007 transceiver->internal()->ClearChannel();
5008 }
5009 }
5010
5011 DestroyDataChannelTransport({});
5012 }
5013
GenerateMediaDescriptionOptions(const SessionDescriptionInterface * session_desc,RtpTransceiverDirection audio_direction,RtpTransceiverDirection video_direction,absl::optional<size_t> * audio_index,absl::optional<size_t> * video_index,absl::optional<size_t> * data_index,cricket::MediaSessionOptions * session_options)5014 void SdpOfferAnswerHandler::GenerateMediaDescriptionOptions(
5015 const SessionDescriptionInterface* session_desc,
5016 RtpTransceiverDirection audio_direction,
5017 RtpTransceiverDirection video_direction,
5018 absl::optional<size_t>* audio_index,
5019 absl::optional<size_t>* video_index,
5020 absl::optional<size_t>* data_index,
5021 cricket::MediaSessionOptions* session_options) {
5022 RTC_DCHECK_RUN_ON(signaling_thread());
5023 for (const cricket::ContentInfo& content :
5024 session_desc->description()->contents()) {
5025 if (IsAudioContent(&content)) {
5026 // If we already have an audio m= section, reject this extra one.
5027 if (*audio_index) {
5028 session_options->media_description_options.push_back(
5029 cricket::MediaDescriptionOptions(
5030 cricket::MEDIA_TYPE_AUDIO, content.name,
5031 RtpTransceiverDirection::kInactive, /*stopped=*/true));
5032 } else {
5033 bool stopped = (audio_direction == RtpTransceiverDirection::kInactive);
5034 session_options->media_description_options.push_back(
5035 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO,
5036 content.name, audio_direction,
5037 stopped));
5038 *audio_index = session_options->media_description_options.size() - 1;
5039 }
5040 session_options->media_description_options.back().header_extensions =
5041 media_engine()->voice().GetRtpHeaderExtensions();
5042 } else if (IsVideoContent(&content)) {
5043 // If we already have an video m= section, reject this extra one.
5044 if (*video_index) {
5045 session_options->media_description_options.push_back(
5046 cricket::MediaDescriptionOptions(
5047 cricket::MEDIA_TYPE_VIDEO, content.name,
5048 RtpTransceiverDirection::kInactive, /*stopped=*/true));
5049 } else {
5050 bool stopped = (video_direction == RtpTransceiverDirection::kInactive);
5051 session_options->media_description_options.push_back(
5052 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO,
5053 content.name, video_direction,
5054 stopped));
5055 *video_index = session_options->media_description_options.size() - 1;
5056 }
5057 session_options->media_description_options.back().header_extensions =
5058 media_engine()->video().GetRtpHeaderExtensions();
5059 } else if (IsUnsupportedContent(&content)) {
5060 session_options->media_description_options.push_back(
5061 cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_UNSUPPORTED,
5062 content.name,
5063 RtpTransceiverDirection::kInactive,
5064 /*stopped=*/true));
5065 } else {
5066 RTC_DCHECK(IsDataContent(&content));
5067 // If we already have an data m= section, reject this extra one.
5068 if (*data_index) {
5069 session_options->media_description_options.push_back(
5070 GetMediaDescriptionOptionsForRejectedData(content.name));
5071 } else {
5072 session_options->media_description_options.push_back(
5073 GetMediaDescriptionOptionsForActiveData(content.name));
5074 *data_index = session_options->media_description_options.size() - 1;
5075 }
5076 }
5077 }
5078 }
5079
5080 cricket::MediaDescriptionOptions
GetMediaDescriptionOptionsForActiveData(const std::string & mid) const5081 SdpOfferAnswerHandler::GetMediaDescriptionOptionsForActiveData(
5082 const std::string& mid) const {
5083 RTC_DCHECK_RUN_ON(signaling_thread());
5084 // Direction for data sections is meaningless, but legacy endpoints might
5085 // expect sendrecv.
5086 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5087 RtpTransceiverDirection::kSendRecv,
5088 /*stopped=*/false);
5089 return options;
5090 }
5091
5092 cricket::MediaDescriptionOptions
GetMediaDescriptionOptionsForRejectedData(const std::string & mid) const5093 SdpOfferAnswerHandler::GetMediaDescriptionOptionsForRejectedData(
5094 const std::string& mid) const {
5095 RTC_DCHECK_RUN_ON(signaling_thread());
5096 cricket::MediaDescriptionOptions options(cricket::MEDIA_TYPE_DATA, mid,
5097 RtpTransceiverDirection::kInactive,
5098 /*stopped=*/true);
5099 return options;
5100 }
5101
UpdatePayloadTypeDemuxingState(cricket::ContentSource source,const std::map<std::string,const cricket::ContentGroup * > & bundle_groups_by_mid)5102 bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState(
5103 cricket::ContentSource source,
5104 const std::map<std::string, const cricket::ContentGroup*>&
5105 bundle_groups_by_mid) {
5106 TRACE_EVENT0("webrtc",
5107 "SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState");
5108 RTC_DCHECK_RUN_ON(signaling_thread());
5109 // We may need to delete any created default streams and disable creation of
5110 // new ones on the basis of payload type. This is needed to avoid SSRC
5111 // collisions in Call's RtpDemuxer, in the case that a transceiver has
5112 // created a default stream, and then some other channel gets the SSRC
5113 // signaled in the corresponding Unified Plan "m=" section. Specifically, we
5114 // need to disable payload type based demuxing when two bundled "m=" sections
5115 // are using the same payload type(s). For more context
5116 // see https://bugs.chromium.org/p/webrtc/issues/detail?id=11477
5117 const SessionDescriptionInterface* sdesc =
5118 (source == cricket::CS_LOCAL ? local_description()
5119 : remote_description());
5120 struct PayloadTypes {
5121 std::set<int> audio_payload_types;
5122 std::set<int> video_payload_types;
5123 bool pt_demuxing_possible_audio = true;
5124 bool pt_demuxing_possible_video = true;
5125 };
5126 std::map<const cricket::ContentGroup*, PayloadTypes> payload_types_by_bundle;
5127 // If the MID is missing from *any* receiving m= section, this is set to true.
5128 bool mid_header_extension_missing_audio = false;
5129 bool mid_header_extension_missing_video = false;
5130 for (auto& content_info : sdesc->description()->contents()) {
5131 auto it = bundle_groups_by_mid.find(content_info.name);
5132 const cricket::ContentGroup* bundle_group =
5133 it != bundle_groups_by_mid.end() ? it->second : nullptr;
5134 // If this m= section isn't bundled, it's safe to demux by payload type
5135 // since other m= sections using the same payload type will also be using
5136 // different transports.
5137 if (!bundle_group) {
5138 continue;
5139 }
5140 PayloadTypes* payload_types = &payload_types_by_bundle[bundle_group];
5141 if (content_info.rejected ||
5142 (source == cricket::ContentSource::CS_LOCAL &&
5143 !RtpTransceiverDirectionHasRecv(
5144 content_info.media_description()->direction())) ||
5145 (source == cricket::ContentSource::CS_REMOTE &&
5146 !RtpTransceiverDirectionHasSend(
5147 content_info.media_description()->direction()))) {
5148 // Ignore transceivers that are not receiving.
5149 continue;
5150 }
5151 switch (content_info.media_description()->type()) {
5152 case cricket::MediaType::MEDIA_TYPE_AUDIO: {
5153 if (!mid_header_extension_missing_audio) {
5154 mid_header_extension_missing_audio =
5155 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5156 }
5157 const cricket::AudioContentDescription* audio_desc =
5158 content_info.media_description()->as_audio();
5159 for (const cricket::AudioCodec& audio : audio_desc->codecs()) {
5160 if (payload_types->audio_payload_types.count(audio.id)) {
5161 // Two m= sections are using the same payload type, thus demuxing
5162 // by payload type is not possible.
5163 payload_types->pt_demuxing_possible_audio = false;
5164 }
5165 payload_types->audio_payload_types.insert(audio.id);
5166 }
5167 break;
5168 }
5169 case cricket::MediaType::MEDIA_TYPE_VIDEO: {
5170 if (!mid_header_extension_missing_video) {
5171 mid_header_extension_missing_video =
5172 !ContentHasHeaderExtension(content_info, RtpExtension::kMidUri);
5173 }
5174 const cricket::VideoContentDescription* video_desc =
5175 content_info.media_description()->as_video();
5176 for (const cricket::VideoCodec& video : video_desc->codecs()) {
5177 if (payload_types->video_payload_types.count(video.id)) {
5178 // Two m= sections are using the same payload type, thus demuxing
5179 // by payload type is not possible.
5180 payload_types->pt_demuxing_possible_video = false;
5181 }
5182 payload_types->video_payload_types.insert(video.id);
5183 }
5184 break;
5185 }
5186 default:
5187 // Ignore data channels.
5188 continue;
5189 }
5190 }
5191
5192 // In Unified Plan, payload type demuxing is useful for legacy endpoints that
5193 // don't support the MID header extension, but it can also cause incorrrect
5194 // forwarding of packets when going from one m= section to multiple m=
5195 // sections in the same BUNDLE. This only happens if media arrives prior to
5196 // negotiation, but this can cause missing video and unsignalled ssrc bugs
5197 // severe enough to warrant disabling PT demuxing in such cases. Therefore, if
5198 // a MID header extension is present on all m= sections for a given kind
5199 // (audio/video) then we use that as an OK to disable payload type demuxing in
5200 // BUNDLEs of that kind. However if PT demuxing was ever turned on (e.g. MID
5201 // was ever removed on ANY m= section of that kind) then we continue to allow
5202 // PT demuxing in order to prevent disabling it in follow-up O/A exchanges and
5203 // allowing early media by PT.
5204 bool bundled_pt_demux_allowed_audio = !IsUnifiedPlan() ||
5205 mid_header_extension_missing_audio ||
5206 pt_demuxing_has_been_used_audio_;
5207 bool bundled_pt_demux_allowed_video = !IsUnifiedPlan() ||
5208 mid_header_extension_missing_video ||
5209 pt_demuxing_has_been_used_video_;
5210
5211 // Gather all updates ahead of time so that all channels can be updated in a
5212 // single BlockingCall; necessary due to thread guards.
5213 std::vector<std::pair<bool, cricket::ChannelInterface*>> channels_to_update;
5214 for (const auto& transceiver : transceivers()->ListInternal()) {
5215 cricket::ChannelInterface* channel = transceiver->channel();
5216 const ContentInfo* content =
5217 FindMediaSectionForTransceiver(transceiver, sdesc);
5218 if (!channel || !content) {
5219 continue;
5220 }
5221
5222 const cricket::MediaType media_type = channel->media_type();
5223 if (media_type != cricket::MediaType::MEDIA_TYPE_AUDIO &&
5224 media_type != cricket::MediaType::MEDIA_TYPE_VIDEO) {
5225 continue;
5226 }
5227
5228 RtpTransceiverDirection local_direction =
5229 content->media_description()->direction();
5230 if (source == cricket::CS_REMOTE) {
5231 local_direction = RtpTransceiverDirectionReversed(local_direction);
5232 }
5233
5234 auto bundle_it = bundle_groups_by_mid.find(channel->mid());
5235 const cricket::ContentGroup* bundle_group =
5236 bundle_it != bundle_groups_by_mid.end() ? bundle_it->second : nullptr;
5237 bool pt_demux_enabled = RtpTransceiverDirectionHasRecv(local_direction);
5238 if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
5239 pt_demux_enabled &=
5240 !bundle_group ||
5241 (bundled_pt_demux_allowed_audio &&
5242 payload_types_by_bundle[bundle_group].pt_demuxing_possible_audio);
5243 if (pt_demux_enabled) {
5244 pt_demuxing_has_been_used_audio_ = true;
5245 }
5246 } else {
5247 RTC_DCHECK_EQ(media_type, cricket::MediaType::MEDIA_TYPE_VIDEO);
5248 pt_demux_enabled &=
5249 !bundle_group ||
5250 (bundled_pt_demux_allowed_video &&
5251 payload_types_by_bundle[bundle_group].pt_demuxing_possible_video);
5252 if (pt_demux_enabled) {
5253 pt_demuxing_has_been_used_video_ = true;
5254 }
5255 }
5256
5257 channels_to_update.emplace_back(pt_demux_enabled, transceiver->channel());
5258 }
5259
5260 if (channels_to_update.empty()) {
5261 return true;
5262 }
5263
5264 // TODO(bugs.webrtc.org/11993): This BlockingCall() will also block on the
5265 // network thread for every demuxer sink that needs to be updated. The demuxer
5266 // state needs to be fully (and only) managed on the network thread and once
5267 // that's the case, there's no need to stop by on the worker. Ideally we could
5268 // also do this without blocking.
5269 return context_->worker_thread()->BlockingCall([&channels_to_update]() {
5270 for (const auto& it : channels_to_update) {
5271 if (!it.second->SetPayloadTypeDemuxingEnabled(it.first)) {
5272 // Note that the state has already been irrevocably changed at this
5273 // point. Is it useful to stop the loop?
5274 return false;
5275 }
5276 }
5277 return true;
5278 });
5279 }
5280
ConfiguredForMedia() const5281 bool SdpOfferAnswerHandler::ConfiguredForMedia() const {
5282 return context_->media_engine();
5283 }
5284
5285 } // namespace webrtc
5286