OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2012 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 "webrtc/api/webrtcsession.h" | |
12 | |
13 #include <limits.h> | |
14 | |
15 #include <algorithm> | |
16 #include <set> | |
17 #include <utility> | |
18 #include <vector> | |
19 | |
20 #include "webrtc/api/call/audio_sink.h" | |
21 #include "webrtc/api/jsepicecandidate.h" | |
22 #include "webrtc/api/jsepsessiondescription.h" | |
23 #include "webrtc/api/peerconnectioninterface.h" | |
24 #include "webrtc/api/sctputils.h" | |
25 #include "webrtc/api/webrtcsessiondescriptionfactory.h" | |
26 #include "webrtc/base/basictypes.h" | |
27 #include "webrtc/base/bind.h" | |
28 #include "webrtc/base/checks.h" | |
29 #include "webrtc/base/helpers.h" | |
30 #include "webrtc/base/logging.h" | |
31 #include "webrtc/base/stringencode.h" | |
32 #include "webrtc/base/stringutils.h" | |
33 #include "webrtc/call/call.h" | |
34 #include "webrtc/media/base/mediaconstants.h" | |
35 #include "webrtc/media/base/videocapturer.h" | |
36 #include "webrtc/media/sctp/sctptransportinternal.h" | |
37 #include "webrtc/p2p/base/portallocator.h" | |
38 #include "webrtc/pc/channel.h" | |
39 #include "webrtc/pc/channelmanager.h" | |
40 #include "webrtc/pc/mediasession.h" | |
41 | |
42 #ifdef HAVE_QUIC | |
43 #include "webrtc/p2p/quic/quictransportchannel.h" | |
44 #endif // HAVE_QUIC | |
45 | |
46 using cricket::ContentInfo; | |
47 using cricket::ContentInfos; | |
48 using cricket::MediaContentDescription; | |
49 using cricket::SessionDescription; | |
50 using cricket::TransportInfo; | |
51 | |
52 using cricket::LOCAL_PORT_TYPE; | |
53 using cricket::STUN_PORT_TYPE; | |
54 using cricket::RELAY_PORT_TYPE; | |
55 using cricket::PRFLX_PORT_TYPE; | |
56 | |
57 namespace webrtc { | |
58 | |
59 // Error messages | |
60 const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE " | |
61 "is enabled."; | |
62 const char kCreateChannelFailed[] = "Failed to create channels."; | |
63 const char kInvalidCandidates[] = "Description contains invalid candidates."; | |
64 const char kInvalidSdp[] = "Invalid session description."; | |
65 const char kMlineMismatch[] = | |
66 "Offer and answer descriptions m-lines are not matching. Rejecting answer."; | |
67 const char kPushDownTDFailed[] = | |
68 "Failed to push down transport description:"; | |
69 const char kSdpWithoutDtlsFingerprint[] = | |
70 "Called with SDP without DTLS fingerprint."; | |
71 const char kSdpWithoutSdesCrypto[] = | |
72 "Called with SDP without SDES crypto."; | |
73 const char kSdpWithoutIceUfragPwd[] = | |
74 "Called with SDP without ice-ufrag and ice-pwd."; | |
75 const char kSessionError[] = "Session error code: "; | |
76 const char kSessionErrorDesc[] = "Session error description: "; | |
77 const char kDtlsSrtpSetupFailureRtp[] = | |
78 "Couldn't set up DTLS-SRTP on RTP channel."; | |
79 const char kDtlsSrtpSetupFailureRtcp[] = | |
80 "Couldn't set up DTLS-SRTP on RTCP channel."; | |
81 const char kEnableBundleFailed[] = "Failed to enable BUNDLE."; | |
82 | |
83 IceCandidatePairType GetIceCandidatePairCounter( | |
84 const cricket::Candidate& local, | |
85 const cricket::Candidate& remote) { | |
86 const auto& l = local.type(); | |
87 const auto& r = remote.type(); | |
88 const auto& host = LOCAL_PORT_TYPE; | |
89 const auto& srflx = STUN_PORT_TYPE; | |
90 const auto& relay = RELAY_PORT_TYPE; | |
91 const auto& prflx = PRFLX_PORT_TYPE; | |
92 if (l == host && r == host) { | |
93 bool local_private = IPIsPrivate(local.address().ipaddr()); | |
94 bool remote_private = IPIsPrivate(remote.address().ipaddr()); | |
95 if (local_private) { | |
96 if (remote_private) { | |
97 return kIceCandidatePairHostPrivateHostPrivate; | |
98 } else { | |
99 return kIceCandidatePairHostPrivateHostPublic; | |
100 } | |
101 } else { | |
102 if (remote_private) { | |
103 return kIceCandidatePairHostPublicHostPrivate; | |
104 } else { | |
105 return kIceCandidatePairHostPublicHostPublic; | |
106 } | |
107 } | |
108 } | |
109 if (l == host && r == srflx) | |
110 return kIceCandidatePairHostSrflx; | |
111 if (l == host && r == relay) | |
112 return kIceCandidatePairHostRelay; | |
113 if (l == host && r == prflx) | |
114 return kIceCandidatePairHostPrflx; | |
115 if (l == srflx && r == host) | |
116 return kIceCandidatePairSrflxHost; | |
117 if (l == srflx && r == srflx) | |
118 return kIceCandidatePairSrflxSrflx; | |
119 if (l == srflx && r == relay) | |
120 return kIceCandidatePairSrflxRelay; | |
121 if (l == srflx && r == prflx) | |
122 return kIceCandidatePairSrflxPrflx; | |
123 if (l == relay && r == host) | |
124 return kIceCandidatePairRelayHost; | |
125 if (l == relay && r == srflx) | |
126 return kIceCandidatePairRelaySrflx; | |
127 if (l == relay && r == relay) | |
128 return kIceCandidatePairRelayRelay; | |
129 if (l == relay && r == prflx) | |
130 return kIceCandidatePairRelayPrflx; | |
131 if (l == prflx && r == host) | |
132 return kIceCandidatePairPrflxHost; | |
133 if (l == prflx && r == srflx) | |
134 return kIceCandidatePairPrflxSrflx; | |
135 if (l == prflx && r == relay) | |
136 return kIceCandidatePairPrflxRelay; | |
137 return kIceCandidatePairMax; | |
138 } | |
139 | |
140 // Compares |answer| against |offer|. Comparision is done | |
141 // for number of m-lines in answer against offer. If matches true will be | |
142 // returned otherwise false. | |
143 static bool VerifyMediaDescriptions( | |
144 const SessionDescription* answer, const SessionDescription* offer) { | |
145 if (offer->contents().size() != answer->contents().size()) | |
146 return false; | |
147 | |
148 for (size_t i = 0; i < offer->contents().size(); ++i) { | |
149 if ((offer->contents()[i].name) != answer->contents()[i].name) { | |
150 return false; | |
151 } | |
152 const MediaContentDescription* offer_mdesc = | |
153 static_cast<const MediaContentDescription*>( | |
154 offer->contents()[i].description); | |
155 const MediaContentDescription* answer_mdesc = | |
156 static_cast<const MediaContentDescription*>( | |
157 answer->contents()[i].description); | |
158 if (offer_mdesc->type() != answer_mdesc->type()) { | |
159 return false; | |
160 } | |
161 } | |
162 return true; | |
163 } | |
164 | |
165 // Checks that each non-rejected content has SDES crypto keys or a DTLS | |
166 // fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES | |
167 // keys, will be caught in Transport negotiation, and backstopped by Channel's | |
168 // |srtp_required| check. | |
169 static bool VerifyCrypto(const SessionDescription* desc, | |
170 bool dtls_enabled, | |
171 std::string* error) { | |
172 const ContentInfos& contents = desc->contents(); | |
173 for (size_t index = 0; index < contents.size(); ++index) { | |
174 const ContentInfo* cinfo = &contents[index]; | |
175 if (cinfo->rejected) { | |
176 continue; | |
177 } | |
178 | |
179 // If the content isn't rejected, crypto must be present. | |
180 const MediaContentDescription* media = | |
181 static_cast<const MediaContentDescription*>(cinfo->description); | |
182 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); | |
183 if (!media || !tinfo) { | |
184 // Something is not right. | |
185 LOG(LS_ERROR) << kInvalidSdp; | |
186 *error = kInvalidSdp; | |
187 return false; | |
188 } | |
189 if (dtls_enabled) { | |
190 if (!tinfo->description.identity_fingerprint) { | |
191 LOG(LS_WARNING) << | |
192 "Session description must have DTLS fingerprint if DTLS enabled."; | |
193 *error = kSdpWithoutDtlsFingerprint; | |
194 return false; | |
195 } | |
196 } else { | |
197 if (media->cryptos().empty()) { | |
198 LOG(LS_WARNING) << | |
199 "Session description must have SDES when DTLS disabled."; | |
200 *error = kSdpWithoutSdesCrypto; | |
201 return false; | |
202 } | |
203 } | |
204 } | |
205 | |
206 return true; | |
207 } | |
208 | |
209 // Checks that each non-rejected content has ice-ufrag and ice-pwd set. | |
210 static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) { | |
211 const ContentInfos& contents = desc->contents(); | |
212 for (size_t index = 0; index < contents.size(); ++index) { | |
213 const ContentInfo* cinfo = &contents[index]; | |
214 if (cinfo->rejected) { | |
215 continue; | |
216 } | |
217 | |
218 // If the content isn't rejected, ice-ufrag and ice-pwd must be present. | |
219 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); | |
220 if (!tinfo) { | |
221 // Something is not right. | |
222 LOG(LS_ERROR) << kInvalidSdp; | |
223 return false; | |
224 } | |
225 if (tinfo->description.ice_ufrag.empty() || | |
226 tinfo->description.ice_pwd.empty()) { | |
227 LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; | |
228 return false; | |
229 } | |
230 } | |
231 return true; | |
232 } | |
233 | |
234 static bool GetAudioSsrcByTrackId(const SessionDescription* session_description, | |
235 const std::string& track_id, | |
236 uint32_t* ssrc) { | |
237 const cricket::ContentInfo* audio_info = | |
238 cricket::GetFirstAudioContent(session_description); | |
239 if (!audio_info) { | |
240 LOG(LS_ERROR) << "Audio not used in this call"; | |
241 return false; | |
242 } | |
243 | |
244 const cricket::MediaContentDescription* audio_content = | |
245 static_cast<const cricket::MediaContentDescription*>( | |
246 audio_info->description); | |
247 const cricket::StreamParams* stream = | |
248 cricket::GetStreamByIds(audio_content->streams(), "", track_id); | |
249 if (!stream) { | |
250 return false; | |
251 } | |
252 | |
253 *ssrc = stream->first_ssrc(); | |
254 return true; | |
255 } | |
256 | |
257 static bool GetTrackIdBySsrc(const SessionDescription* session_description, | |
258 uint32_t ssrc, | |
259 std::string* track_id) { | |
260 RTC_DCHECK(track_id != NULL); | |
261 | |
262 const cricket::ContentInfo* audio_info = | |
263 cricket::GetFirstAudioContent(session_description); | |
264 if (audio_info) { | |
265 const cricket::MediaContentDescription* audio_content = | |
266 static_cast<const cricket::MediaContentDescription*>( | |
267 audio_info->description); | |
268 | |
269 const auto* found = | |
270 cricket::GetStreamBySsrc(audio_content->streams(), ssrc); | |
271 if (found) { | |
272 *track_id = found->id; | |
273 return true; | |
274 } | |
275 } | |
276 | |
277 const cricket::ContentInfo* video_info = | |
278 cricket::GetFirstVideoContent(session_description); | |
279 if (video_info) { | |
280 const cricket::MediaContentDescription* video_content = | |
281 static_cast<const cricket::MediaContentDescription*>( | |
282 video_info->description); | |
283 | |
284 const auto* found = | |
285 cricket::GetStreamBySsrc(video_content->streams(), ssrc); | |
286 if (found) { | |
287 *track_id = found->id; | |
288 return true; | |
289 } | |
290 } | |
291 return false; | |
292 } | |
293 | |
294 // Get the SCTP port out of a SessionDescription. | |
295 // Return -1 if not found. | |
296 static int GetSctpPort(const SessionDescription* session_description) { | |
297 const ContentInfo* content_info = GetFirstDataContent(session_description); | |
298 RTC_DCHECK(content_info); | |
299 if (!content_info) { | |
300 return -1; | |
301 } | |
302 const cricket::DataContentDescription* data = | |
303 static_cast<const cricket::DataContentDescription*>( | |
304 (content_info->description)); | |
305 std::string value; | |
306 cricket::DataCodec match_pattern(cricket::kGoogleSctpDataCodecPlType, | |
307 cricket::kGoogleSctpDataCodecName); | |
308 for (const cricket::DataCodec& codec : data->codecs()) { | |
309 if (!codec.Matches(match_pattern)) { | |
310 continue; | |
311 } | |
312 if (codec.GetParam(cricket::kCodecParamPort, &value)) { | |
313 return rtc::FromString<int>(value); | |
314 } | |
315 } | |
316 return -1; | |
317 } | |
318 | |
319 static bool BadSdp(const std::string& source, | |
320 const std::string& type, | |
321 const std::string& reason, | |
322 std::string* err_desc) { | |
323 std::ostringstream desc; | |
324 desc << "Failed to set " << source; | |
325 if (!type.empty()) { | |
326 desc << " " << type; | |
327 } | |
328 desc << " sdp: " << reason; | |
329 | |
330 if (err_desc) { | |
331 *err_desc = desc.str(); | |
332 } | |
333 LOG(LS_ERROR) << desc.str(); | |
334 return false; | |
335 } | |
336 | |
337 static bool BadSdp(cricket::ContentSource source, | |
338 const std::string& type, | |
339 const std::string& reason, | |
340 std::string* err_desc) { | |
341 if (source == cricket::CS_LOCAL) { | |
342 return BadSdp("local", type, reason, err_desc); | |
343 } else { | |
344 return BadSdp("remote", type, reason, err_desc); | |
345 } | |
346 } | |
347 | |
348 static bool BadLocalSdp(const std::string& type, | |
349 const std::string& reason, | |
350 std::string* err_desc) { | |
351 return BadSdp(cricket::CS_LOCAL, type, reason, err_desc); | |
352 } | |
353 | |
354 static bool BadRemoteSdp(const std::string& type, | |
355 const std::string& reason, | |
356 std::string* err_desc) { | |
357 return BadSdp(cricket::CS_REMOTE, type, reason, err_desc); | |
358 } | |
359 | |
360 static bool BadOfferSdp(cricket::ContentSource source, | |
361 const std::string& reason, | |
362 std::string* err_desc) { | |
363 return BadSdp(source, SessionDescriptionInterface::kOffer, reason, err_desc); | |
364 } | |
365 | |
366 static bool BadPranswerSdp(cricket::ContentSource source, | |
367 const std::string& reason, | |
368 std::string* err_desc) { | |
369 return BadSdp(source, SessionDescriptionInterface::kPrAnswer, | |
370 reason, err_desc); | |
371 } | |
372 | |
373 static bool BadAnswerSdp(cricket::ContentSource source, | |
374 const std::string& reason, | |
375 std::string* err_desc) { | |
376 return BadSdp(source, SessionDescriptionInterface::kAnswer, reason, err_desc); | |
377 } | |
378 | |
379 #define GET_STRING_OF_STATE(state) \ | |
380 case webrtc::WebRtcSession::state: \ | |
381 result = #state; \ | |
382 break; | |
383 | |
384 static std::string GetStateString(webrtc::WebRtcSession::State state) { | |
385 std::string result; | |
386 switch (state) { | |
387 GET_STRING_OF_STATE(STATE_INIT) | |
388 GET_STRING_OF_STATE(STATE_SENTOFFER) | |
389 GET_STRING_OF_STATE(STATE_RECEIVEDOFFER) | |
390 GET_STRING_OF_STATE(STATE_SENTPRANSWER) | |
391 GET_STRING_OF_STATE(STATE_RECEIVEDPRANSWER) | |
392 GET_STRING_OF_STATE(STATE_INPROGRESS) | |
393 GET_STRING_OF_STATE(STATE_CLOSED) | |
394 default: | |
395 RTC_NOTREACHED(); | |
396 break; | |
397 } | |
398 return result; | |
399 } | |
400 | |
401 #define GET_STRING_OF_ERROR_CODE(err) \ | |
402 case webrtc::WebRtcSession::err: \ | |
403 result = #err; \ | |
404 break; | |
405 | |
406 static std::string GetErrorCodeString(webrtc::WebRtcSession::Error err) { | |
407 std::string result; | |
408 switch (err) { | |
409 GET_STRING_OF_ERROR_CODE(ERROR_NONE) | |
410 GET_STRING_OF_ERROR_CODE(ERROR_CONTENT) | |
411 GET_STRING_OF_ERROR_CODE(ERROR_TRANSPORT) | |
412 default: | |
413 RTC_NOTREACHED(); | |
414 break; | |
415 } | |
416 return result; | |
417 } | |
418 | |
419 static std::string MakeErrorString(const std::string& error, | |
420 const std::string& desc) { | |
421 std::ostringstream ret; | |
422 ret << error << " " << desc; | |
423 return ret.str(); | |
424 } | |
425 | |
426 static std::string MakeTdErrorString(const std::string& desc) { | |
427 return MakeErrorString(kPushDownTDFailed, desc); | |
428 } | |
429 | |
430 // Returns true if |new_desc| requests an ICE restart (i.e., new ufrag/pwd). | |
431 bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc, | |
432 const SessionDescriptionInterface* new_desc, | |
433 const std::string& content_name) { | |
434 if (!old_desc) { | |
435 return false; | |
436 } | |
437 const SessionDescription* new_sd = new_desc->description(); | |
438 const SessionDescription* old_sd = old_desc->description(); | |
439 const ContentInfo* cinfo = new_sd->GetContentByName(content_name); | |
440 if (!cinfo || cinfo->rejected) { | |
441 return false; | |
442 } | |
443 // If the content isn't rejected, check if ufrag and password has changed. | |
444 const cricket::TransportDescription* new_transport_desc = | |
445 new_sd->GetTransportDescriptionByName(content_name); | |
446 const cricket::TransportDescription* old_transport_desc = | |
447 old_sd->GetTransportDescriptionByName(content_name); | |
448 if (!new_transport_desc || !old_transport_desc) { | |
449 // No transport description exists. This is not an ICE restart. | |
450 return false; | |
451 } | |
452 if (cricket::IceCredentialsChanged( | |
453 old_transport_desc->ice_ufrag, old_transport_desc->ice_pwd, | |
454 new_transport_desc->ice_ufrag, new_transport_desc->ice_pwd)) { | |
455 LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name | |
456 << "."; | |
457 return true; | |
458 } | |
459 return false; | |
460 } | |
461 | |
462 WebRtcSession::WebRtcSession( | |
463 webrtc::MediaControllerInterface* media_controller, | |
464 rtc::Thread* network_thread, | |
465 rtc::Thread* worker_thread, | |
466 rtc::Thread* signaling_thread, | |
467 cricket::PortAllocator* port_allocator, | |
468 std::unique_ptr<cricket::TransportController> transport_controller, | |
469 std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory) | |
470 : network_thread_(network_thread), | |
471 worker_thread_(worker_thread), | |
472 signaling_thread_(signaling_thread), | |
473 // RFC 3264: The numeric value of the session id and version in the | |
474 // o line MUST be representable with a "64 bit signed integer". | |
475 // Due to this constraint session id |sid_| is max limited to LLONG_MAX. | |
476 sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)), | |
477 transport_controller_(std::move(transport_controller)), | |
478 sctp_factory_(std::move(sctp_factory)), | |
479 media_controller_(media_controller), | |
480 channel_manager_(media_controller_->channel_manager()), | |
481 ice_observer_(NULL), | |
482 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew), | |
483 ice_connection_receiving_(true), | |
484 older_version_remote_peer_(false), | |
485 dtls_enabled_(false), | |
486 data_channel_type_(cricket::DCT_NONE), | |
487 metrics_observer_(NULL) { | |
488 transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED); | |
489 transport_controller_->SignalConnectionState.connect( | |
490 this, &WebRtcSession::OnTransportControllerConnectionState); | |
491 transport_controller_->SignalReceiving.connect( | |
492 this, &WebRtcSession::OnTransportControllerReceiving); | |
493 transport_controller_->SignalGatheringState.connect( | |
494 this, &WebRtcSession::OnTransportControllerGatheringState); | |
495 transport_controller_->SignalCandidatesGathered.connect( | |
496 this, &WebRtcSession::OnTransportControllerCandidatesGathered); | |
497 transport_controller_->SignalCandidatesRemoved.connect( | |
498 this, &WebRtcSession::OnTransportControllerCandidatesRemoved); | |
499 transport_controller_->SignalDtlsHandshakeError.connect( | |
500 this, &WebRtcSession::OnTransportControllerDtlsHandshakeError); | |
501 } | |
502 | |
503 WebRtcSession::~WebRtcSession() { | |
504 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
505 // Destroy video_channel_ first since it may have a pointer to the | |
506 // voice_channel_. | |
507 if (video_channel_) { | |
508 DestroyVideoChannel(); | |
509 } | |
510 if (voice_channel_) { | |
511 DestroyVoiceChannel(); | |
512 } | |
513 if (rtp_data_channel_) { | |
514 DestroyDataChannel(); | |
515 } | |
516 if (sctp_transport_) { | |
517 SignalDataChannelDestroyed(); | |
518 network_thread_->Invoke<void>( | |
519 RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); | |
520 } | |
521 #ifdef HAVE_QUIC | |
522 if (quic_data_transport_) { | |
523 quic_data_transport_.reset(); | |
524 } | |
525 #endif | |
526 SignalDestroyed(); | |
527 | |
528 LOG(LS_INFO) << "Session: " << id() << " is destroyed."; | |
529 } | |
530 | |
531 bool WebRtcSession::Initialize( | |
532 const PeerConnectionFactoryInterface::Options& options, | |
533 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator, | |
534 const PeerConnectionInterface::RTCConfiguration& rtc_configuration) { | |
535 bundle_policy_ = rtc_configuration.bundle_policy; | |
536 rtcp_mux_policy_ = rtc_configuration.rtcp_mux_policy; | |
537 transport_controller_->SetSslMaxProtocolVersion(options.ssl_max_version); | |
538 | |
539 // Obtain a certificate from RTCConfiguration if any were provided (optional). | |
540 rtc::scoped_refptr<rtc::RTCCertificate> certificate; | |
541 if (!rtc_configuration.certificates.empty()) { | |
542 // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of | |
543 // just picking the first one. The decision should be made based on the DTLS | |
544 // handshake. The DTLS negotiations need to know about all certificates. | |
545 certificate = rtc_configuration.certificates[0]; | |
546 } | |
547 | |
548 SetIceConfig(ParseIceConfig(rtc_configuration)); | |
549 | |
550 if (options.disable_encryption) { | |
551 dtls_enabled_ = false; | |
552 } else { | |
553 // Enable DTLS by default if we have an identity store or a certificate. | |
554 dtls_enabled_ = (cert_generator || certificate); | |
555 // |rtc_configuration| can override the default |dtls_enabled_| value. | |
556 if (rtc_configuration.enable_dtls_srtp) { | |
557 dtls_enabled_ = *(rtc_configuration.enable_dtls_srtp); | |
558 } | |
559 } | |
560 | |
561 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. | |
562 // It takes precendence over the disable_sctp_data_channels | |
563 // PeerConnectionFactoryInterface::Options. | |
564 if (rtc_configuration.enable_rtp_data_channel) { | |
565 data_channel_type_ = cricket::DCT_RTP; | |
566 } | |
567 #ifdef HAVE_QUIC | |
568 else if (rtc_configuration.enable_quic) { | |
569 // Use QUIC instead of DTLS when |enable_quic| is true. | |
570 data_channel_type_ = cricket::DCT_QUIC; | |
571 transport_controller_->use_quic(); | |
572 if (dtls_enabled_) { | |
573 LOG(LS_INFO) << "Using QUIC instead of DTLS"; | |
574 } | |
575 quic_data_transport_.reset( | |
576 new QuicDataTransport(signaling_thread(), worker_thread(), | |
577 network_thread(), transport_controller_.get())); | |
578 } | |
579 #endif // HAVE_QUIC | |
580 else { | |
581 // DTLS has to be enabled to use SCTP. | |
582 if (!options.disable_sctp_data_channels && dtls_enabled_) { | |
583 data_channel_type_ = cricket::DCT_SCTP; | |
584 } | |
585 } | |
586 | |
587 video_options_.screencast_min_bitrate_kbps = | |
588 rtc_configuration.screencast_min_bitrate; | |
589 audio_options_.combined_audio_video_bwe = | |
590 rtc_configuration.combined_audio_video_bwe; | |
591 | |
592 audio_options_.audio_jitter_buffer_max_packets = | |
593 rtc::Optional<int>(rtc_configuration.audio_jitter_buffer_max_packets); | |
594 | |
595 audio_options_.audio_jitter_buffer_fast_accelerate = rtc::Optional<bool>( | |
596 rtc_configuration.audio_jitter_buffer_fast_accelerate); | |
597 | |
598 if (!dtls_enabled_) { | |
599 // Construct with DTLS disabled. | |
600 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( | |
601 signaling_thread(), channel_manager_, this, id(), | |
602 std::unique_ptr<rtc::RTCCertificateGeneratorInterface>())); | |
603 } else { | |
604 // Construct with DTLS enabled. | |
605 if (!certificate) { | |
606 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( | |
607 signaling_thread(), channel_manager_, this, id(), | |
608 std::move(cert_generator))); | |
609 } else { | |
610 // Use the already generated certificate. | |
611 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( | |
612 signaling_thread(), channel_manager_, this, id(), certificate)); | |
613 } | |
614 } | |
615 | |
616 webrtc_session_desc_factory_->SignalCertificateReady.connect( | |
617 this, &WebRtcSession::OnCertificateReady); | |
618 | |
619 if (options.disable_encryption) { | |
620 webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); | |
621 } | |
622 | |
623 return true; | |
624 } | |
625 | |
626 void WebRtcSession::Close() { | |
627 SetState(STATE_CLOSED); | |
628 RemoveUnusedChannels(nullptr); | |
629 RTC_DCHECK(!voice_channel_); | |
630 RTC_DCHECK(!video_channel_); | |
631 RTC_DCHECK(!rtp_data_channel_); | |
632 RTC_DCHECK(!sctp_transport_); | |
633 media_controller_->Close(); | |
634 } | |
635 | |
636 cricket::BaseChannel* WebRtcSession::GetChannel( | |
637 const std::string& content_name) { | |
638 if (voice_channel() && voice_channel()->content_name() == content_name) { | |
639 return voice_channel(); | |
640 } | |
641 if (video_channel() && video_channel()->content_name() == content_name) { | |
642 return video_channel(); | |
643 } | |
644 if (rtp_data_channel() && | |
645 rtp_data_channel()->content_name() == content_name) { | |
646 return rtp_data_channel(); | |
647 } | |
648 return nullptr; | |
649 } | |
650 | |
651 cricket::SecurePolicy WebRtcSession::SdesPolicy() const { | |
652 return webrtc_session_desc_factory_->SdesPolicy(); | |
653 } | |
654 | |
655 bool WebRtcSession::GetSctpSslRole(rtc::SSLRole* role) { | |
656 if (!local_description() || !remote_description()) { | |
657 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " | |
658 << "SSL Role of the SCTP transport."; | |
659 return false; | |
660 } | |
661 if (!sctp_transport_) { | |
662 LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the " | |
663 << "SSL Role of the SCTP transport."; | |
664 return false; | |
665 } | |
666 | |
667 return transport_controller_->GetSslRole(*sctp_transport_name_, role); | |
668 } | |
669 | |
670 bool WebRtcSession::GetSslRole(const std::string& content_name, | |
671 rtc::SSLRole* role) { | |
672 if (!local_description() || !remote_description()) { | |
673 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " | |
674 << "SSL Role of the session."; | |
675 return false; | |
676 } | |
677 | |
678 return transport_controller_->GetSslRole(GetTransportName(content_name), | |
679 role); | |
680 } | |
681 | |
682 void WebRtcSession::CreateOffer( | |
683 CreateSessionDescriptionObserver* observer, | |
684 const PeerConnectionInterface::RTCOfferAnswerOptions& options, | |
685 const cricket::MediaSessionOptions& session_options) { | |
686 webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); | |
687 } | |
688 | |
689 void WebRtcSession::CreateAnswer( | |
690 CreateSessionDescriptionObserver* observer, | |
691 const cricket::MediaSessionOptions& session_options) { | |
692 webrtc_session_desc_factory_->CreateAnswer(observer, session_options); | |
693 } | |
694 | |
695 bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, | |
696 std::string* err_desc) { | |
697 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
698 | |
699 // Takes the ownership of |desc| regardless of the result. | |
700 std::unique_ptr<SessionDescriptionInterface> desc_temp(desc); | |
701 | |
702 // Validate SDP. | |
703 if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) { | |
704 return false; | |
705 } | |
706 | |
707 // Update the initial_offerer flag if this session is the initial_offerer. | |
708 Action action = GetAction(desc->type()); | |
709 if (state() == STATE_INIT && action == kOffer) { | |
710 initial_offerer_ = true; | |
711 transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); | |
712 } | |
713 | |
714 if (action == kAnswer) { | |
715 current_local_description_.reset(desc_temp.release()); | |
716 pending_local_description_.reset(nullptr); | |
717 current_remote_description_.reset(pending_remote_description_.release()); | |
718 } else { | |
719 pending_local_description_.reset(desc_temp.release()); | |
720 } | |
721 | |
722 // Transport and Media channels will be created only when offer is set. | |
723 if (action == kOffer && !CreateChannels(local_description()->description())) { | |
724 // TODO(mallinath) - Handle CreateChannel failure, as new local description | |
725 // is applied. Restore back to old description. | |
726 return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc); | |
727 } | |
728 | |
729 // Remove unused channels if MediaContentDescription is rejected. | |
730 RemoveUnusedChannels(local_description()->description()); | |
731 | |
732 if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) { | |
733 return false; | |
734 } | |
735 if (remote_description()) { | |
736 // Now that we have a local description, we can push down remote candidates. | |
737 UseCandidatesInSessionDescription(remote_description()); | |
738 } | |
739 | |
740 pending_ice_restarts_.clear(); | |
741 if (error() != ERROR_NONE) { | |
742 return BadLocalSdp(desc->type(), GetSessionErrorMsg(), err_desc); | |
743 } | |
744 return true; | |
745 } | |
746 | |
747 bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, | |
748 std::string* err_desc) { | |
749 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
750 | |
751 // Takes the ownership of |desc| regardless of the result. | |
752 std::unique_ptr<SessionDescriptionInterface> desc_temp(desc); | |
753 | |
754 // Validate SDP. | |
755 if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) { | |
756 return false; | |
757 } | |
758 | |
759 const SessionDescriptionInterface* old_remote_description = | |
760 remote_description(); | |
761 // Grab ownership of the description being replaced for the remainder of this | |
762 // method, since it's used below. | |
763 std::unique_ptr<SessionDescriptionInterface> replaced_remote_description; | |
764 Action action = GetAction(desc->type()); | |
765 if (action == kAnswer) { | |
766 replaced_remote_description.reset( | |
767 pending_remote_description_ ? pending_remote_description_.release() | |
768 : current_remote_description_.release()); | |
769 current_remote_description_.reset(desc_temp.release()); | |
770 pending_remote_description_.reset(nullptr); | |
771 current_local_description_.reset(pending_local_description_.release()); | |
772 } else { | |
773 replaced_remote_description.reset(pending_remote_description_.release()); | |
774 pending_remote_description_.reset(desc_temp.release()); | |
775 } | |
776 | |
777 // Transport and Media channels will be created only when offer is set. | |
778 if (action == kOffer && !CreateChannels(desc->description())) { | |
779 // TODO(mallinath) - Handle CreateChannel failure, as new local description | |
780 // is applied. Restore back to old description. | |
781 return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc); | |
782 } | |
783 | |
784 // Remove unused channels if MediaContentDescription is rejected. | |
785 RemoveUnusedChannels(desc->description()); | |
786 | |
787 // NOTE: Candidates allocation will be initiated only when SetLocalDescription | |
788 // is called. | |
789 if (!UpdateSessionState(action, cricket::CS_REMOTE, err_desc)) { | |
790 return false; | |
791 } | |
792 | |
793 if (local_description() && !UseCandidatesInSessionDescription(desc)) { | |
794 return BadRemoteSdp(desc->type(), kInvalidCandidates, err_desc); | |
795 } | |
796 | |
797 if (old_remote_description) { | |
798 for (const cricket::ContentInfo& content : | |
799 old_remote_description->description()->contents()) { | |
800 // Check if this new SessionDescription contains new ICE ufrag and | |
801 // password that indicates the remote peer requests an ICE restart. | |
802 // TODO(deadbeef): When we start storing both the current and pending | |
803 // remote description, this should reset pending_ice_restarts and compare | |
804 // against the current description. | |
805 if (CheckForRemoteIceRestart(old_remote_description, desc, | |
806 content.name)) { | |
807 if (action == kOffer) { | |
808 pending_ice_restarts_.insert(content.name); | |
809 } | |
810 } else { | |
811 // We retain all received candidates only if ICE is not restarted. | |
812 // When ICE is restarted, all previous candidates belong to an old | |
813 // generation and should not be kept. | |
814 // TODO(deadbeef): This goes against the W3C spec which says the remote | |
815 // description should only contain candidates from the last set remote | |
816 // description plus any candidates added since then. We should remove | |
817 // this once we're sure it won't break anything. | |
818 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( | |
819 old_remote_description, content.name, desc); | |
820 } | |
821 } | |
822 } | |
823 | |
824 if (error() != ERROR_NONE) { | |
825 return BadRemoteSdp(desc->type(), GetSessionErrorMsg(), err_desc); | |
826 } | |
827 | |
828 // Set the the ICE connection state to connecting since the connection may | |
829 // become writable with peer reflexive candidates before any remote candidate | |
830 // is signaled. | |
831 // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix | |
832 // is to have a new signal the indicates a change in checking state from the | |
833 // transport and expose a new checking() member from transport that can be | |
834 // read to determine the current checking state. The existing SignalConnecting | |
835 // actually means "gathering candidates", so cannot be be used here. | |
836 if (desc->type() != SessionDescriptionInterface::kOffer && | |
837 ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew) { | |
838 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); | |
839 } | |
840 return true; | |
841 } | |
842 | |
843 void WebRtcSession::LogState(State old_state, State new_state) { | |
844 LOG(LS_INFO) << "Session:" << id() | |
845 << " Old state:" << GetStateString(old_state) | |
846 << " New state:" << GetStateString(new_state); | |
847 } | |
848 | |
849 void WebRtcSession::SetState(State state) { | |
850 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
851 if (state != state_) { | |
852 LogState(state_, state); | |
853 state_ = state; | |
854 SignalState(this, state_); | |
855 } | |
856 } | |
857 | |
858 void WebRtcSession::SetError(Error error, const std::string& error_desc) { | |
859 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
860 if (error != error_) { | |
861 error_ = error; | |
862 error_desc_ = error_desc; | |
863 } | |
864 } | |
865 | |
866 bool WebRtcSession::UpdateSessionState( | |
867 Action action, cricket::ContentSource source, | |
868 std::string* err_desc) { | |
869 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
870 | |
871 // If there's already a pending error then no state transition should happen. | |
872 // But all call-sites should be verifying this before calling us! | |
873 RTC_DCHECK(error() == ERROR_NONE); | |
874 std::string td_err; | |
875 if (action == kOffer) { | |
876 if (!PushdownTransportDescription(source, cricket::CA_OFFER, &td_err)) { | |
877 return BadOfferSdp(source, MakeTdErrorString(td_err), err_desc); | |
878 } | |
879 SetState(source == cricket::CS_LOCAL ? STATE_SENTOFFER | |
880 : STATE_RECEIVEDOFFER); | |
881 if (!PushdownMediaDescription(cricket::CA_OFFER, source, err_desc)) { | |
882 SetError(ERROR_CONTENT, *err_desc); | |
883 } | |
884 if (error() != ERROR_NONE) { | |
885 return BadOfferSdp(source, GetSessionErrorMsg(), err_desc); | |
886 } | |
887 } else if (action == kPrAnswer) { | |
888 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER, &td_err)) { | |
889 return BadPranswerSdp(source, MakeTdErrorString(td_err), err_desc); | |
890 } | |
891 EnableChannels(); | |
892 SetState(source == cricket::CS_LOCAL ? STATE_SENTPRANSWER | |
893 : STATE_RECEIVEDPRANSWER); | |
894 if (!PushdownMediaDescription(cricket::CA_PRANSWER, source, err_desc)) { | |
895 SetError(ERROR_CONTENT, *err_desc); | |
896 } | |
897 if (error() != ERROR_NONE) { | |
898 return BadPranswerSdp(source, GetSessionErrorMsg(), err_desc); | |
899 } | |
900 } else if (action == kAnswer) { | |
901 const cricket::ContentGroup* local_bundle = | |
902 local_description()->description()->GetGroupByName( | |
903 cricket::GROUP_TYPE_BUNDLE); | |
904 const cricket::ContentGroup* remote_bundle = | |
905 remote_description()->description()->GetGroupByName( | |
906 cricket::GROUP_TYPE_BUNDLE); | |
907 if (local_bundle && remote_bundle) { | |
908 // The answerer decides the transport to bundle on. | |
909 const cricket::ContentGroup* answer_bundle = | |
910 (source == cricket::CS_LOCAL ? local_bundle : remote_bundle); | |
911 if (!EnableBundle(*answer_bundle)) { | |
912 LOG(LS_WARNING) << "Failed to enable BUNDLE."; | |
913 return BadAnswerSdp(source, kEnableBundleFailed, err_desc); | |
914 } | |
915 } | |
916 // Only push down the transport description after enabling BUNDLE; we don't | |
917 // want to push down a description on a transport about to be destroyed. | |
918 if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) { | |
919 return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc); | |
920 } | |
921 EnableChannels(); | |
922 SetState(STATE_INPROGRESS); | |
923 if (!PushdownMediaDescription(cricket::CA_ANSWER, source, err_desc)) { | |
924 SetError(ERROR_CONTENT, *err_desc); | |
925 } | |
926 if (error() != ERROR_NONE) { | |
927 return BadAnswerSdp(source, GetSessionErrorMsg(), err_desc); | |
928 } | |
929 } | |
930 return true; | |
931 } | |
932 | |
933 WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) { | |
934 if (type == SessionDescriptionInterface::kOffer) { | |
935 return WebRtcSession::kOffer; | |
936 } else if (type == SessionDescriptionInterface::kPrAnswer) { | |
937 return WebRtcSession::kPrAnswer; | |
938 } else if (type == SessionDescriptionInterface::kAnswer) { | |
939 return WebRtcSession::kAnswer; | |
940 } | |
941 RTC_NOTREACHED() << "unknown action type"; | |
942 return WebRtcSession::kOffer; | |
943 } | |
944 | |
945 bool WebRtcSession::PushdownMediaDescription( | |
946 cricket::ContentAction action, | |
947 cricket::ContentSource source, | |
948 std::string* err) { | |
949 auto set_content = [this, action, source, err](cricket::BaseChannel* ch) { | |
950 if (!ch) { | |
951 return true; | |
952 } else if (source == cricket::CS_LOCAL) { | |
953 return ch->PushdownLocalDescription(local_description()->description(), | |
954 action, err); | |
955 } else { | |
956 return ch->PushdownRemoteDescription(remote_description()->description(), | |
957 action, err); | |
958 } | |
959 }; | |
960 | |
961 bool ret = (set_content(voice_channel()) && set_content(video_channel()) && | |
962 set_content(rtp_data_channel())); | |
963 // Need complete offer/answer with an SCTP m= section before starting SCTP, | |
964 // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19 | |
965 if (sctp_transport_ && local_description() && remote_description() && | |
966 cricket::GetFirstDataContent(local_description()->description()) && | |
967 cricket::GetFirstDataContent(remote_description()->description())) { | |
968 ret &= network_thread_->Invoke<bool>( | |
969 RTC_FROM_HERE, | |
970 rtc::Bind(&WebRtcSession::PushdownSctpParameters_n, this, source)); | |
971 } | |
972 return ret; | |
973 } | |
974 | |
975 bool WebRtcSession::PushdownSctpParameters_n(cricket::ContentSource source) { | |
976 RTC_DCHECK(network_thread_->IsCurrent()); | |
977 RTC_DCHECK(local_description()); | |
978 RTC_DCHECK(remote_description()); | |
979 // Apply the SCTP port (which is hidden inside a DataCodec structure...) | |
980 // When we support "max-message-size", that would also be pushed down here. | |
981 return sctp_transport_->Start( | |
982 GetSctpPort(local_description()->description()), | |
983 GetSctpPort(remote_description()->description())); | |
984 } | |
985 | |
986 bool WebRtcSession::PushdownTransportDescription(cricket::ContentSource source, | |
987 cricket::ContentAction action, | |
988 std::string* error_desc) { | |
989 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
990 | |
991 if (source == cricket::CS_LOCAL) { | |
992 return PushdownLocalTransportDescription(local_description()->description(), | |
993 action, error_desc); | |
994 } | |
995 return PushdownRemoteTransportDescription(remote_description()->description(), | |
996 action, error_desc); | |
997 } | |
998 | |
999 bool WebRtcSession::PushdownLocalTransportDescription( | |
1000 const SessionDescription* sdesc, | |
1001 cricket::ContentAction action, | |
1002 std::string* err) { | |
1003 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
1004 | |
1005 if (!sdesc) { | |
1006 return false; | |
1007 } | |
1008 | |
1009 for (const TransportInfo& tinfo : sdesc->transport_infos()) { | |
1010 if (!transport_controller_->SetLocalTransportDescription( | |
1011 tinfo.content_name, tinfo.description, action, err)) { | |
1012 return false; | |
1013 } | |
1014 } | |
1015 | |
1016 return true; | |
1017 } | |
1018 | |
1019 bool WebRtcSession::PushdownRemoteTransportDescription( | |
1020 const SessionDescription* sdesc, | |
1021 cricket::ContentAction action, | |
1022 std::string* err) { | |
1023 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
1024 | |
1025 if (!sdesc) { | |
1026 return false; | |
1027 } | |
1028 | |
1029 for (const TransportInfo& tinfo : sdesc->transport_infos()) { | |
1030 if (!transport_controller_->SetRemoteTransportDescription( | |
1031 tinfo.content_name, tinfo.description, action, err)) { | |
1032 return false; | |
1033 } | |
1034 } | |
1035 | |
1036 return true; | |
1037 } | |
1038 | |
1039 bool WebRtcSession::GetTransportDescription( | |
1040 const SessionDescription* description, | |
1041 const std::string& content_name, | |
1042 cricket::TransportDescription* tdesc) { | |
1043 if (!description || !tdesc) { | |
1044 return false; | |
1045 } | |
1046 const TransportInfo* transport_info = | |
1047 description->GetTransportInfoByName(content_name); | |
1048 if (!transport_info) { | |
1049 return false; | |
1050 } | |
1051 *tdesc = transport_info->description; | |
1052 return true; | |
1053 } | |
1054 | |
1055 bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { | |
1056 const std::string* first_content_name = bundle.FirstContentName(); | |
1057 if (!first_content_name) { | |
1058 LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; | |
1059 return false; | |
1060 } | |
1061 const std::string& transport_name = *first_content_name; | |
1062 | |
1063 #ifdef HAVE_QUIC | |
1064 if (quic_data_transport_ && | |
1065 bundle.HasContentName(quic_data_transport_->content_name()) && | |
1066 quic_data_transport_->transport_name() != transport_name) { | |
1067 LOG(LS_ERROR) << "Unable to BUNDLE " << quic_data_transport_->content_name() | |
1068 << " on " << transport_name << "with QUIC."; | |
1069 } | |
1070 #endif | |
1071 auto maybe_set_transport = [this, bundle, | |
1072 transport_name](cricket::BaseChannel* ch) { | |
1073 if (!ch || !bundle.HasContentName(ch->content_name())) { | |
1074 return true; | |
1075 } | |
1076 | |
1077 std::string old_transport_name = ch->transport_name(); | |
1078 if (old_transport_name == transport_name) { | |
1079 LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name() | |
1080 << " on " << transport_name << "."; | |
1081 return true; | |
1082 } | |
1083 | |
1084 cricket::DtlsTransportInternal* rtp_dtls_transport = | |
1085 transport_controller_->CreateDtlsTransport( | |
1086 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1087 bool need_rtcp = (ch->rtcp_dtls_transport() != nullptr); | |
1088 cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; | |
1089 if (need_rtcp) { | |
1090 rtcp_dtls_transport = transport_controller_->CreateDtlsTransport_n( | |
1091 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
1092 } | |
1093 | |
1094 ch->SetTransports(rtp_dtls_transport, rtcp_dtls_transport); | |
1095 LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on " | |
1096 << transport_name << "."; | |
1097 transport_controller_->DestroyDtlsTransport( | |
1098 old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1099 // If the channel needs rtcp, it means that the channel used to have a | |
1100 // rtcp transport which needs to be deleted now. | |
1101 if (need_rtcp) { | |
1102 transport_controller_->DestroyDtlsTransport( | |
1103 old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
1104 } | |
1105 return true; | |
1106 }; | |
1107 | |
1108 if (!maybe_set_transport(voice_channel()) || | |
1109 !maybe_set_transport(video_channel()) || | |
1110 !maybe_set_transport(rtp_data_channel())) { | |
1111 return false; | |
1112 } | |
1113 // For SCTP, transport creation/deletion happens here instead of in the | |
1114 // object itself. | |
1115 if (sctp_transport_) { | |
1116 RTC_DCHECK(sctp_transport_name_); | |
1117 RTC_DCHECK(sctp_content_name_); | |
1118 if (transport_name != *sctp_transport_name_ && | |
1119 bundle.HasContentName(*sctp_content_name_)) { | |
1120 network_thread_->Invoke<void>( | |
1121 RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this, | |
1122 transport_name)); | |
1123 } | |
1124 } | |
1125 | |
1126 return true; | |
1127 } | |
1128 | |
1129 bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) { | |
1130 if (!remote_description()) { | |
1131 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added " | |
1132 << "without any remote session description."; | |
1133 return false; | |
1134 } | |
1135 | |
1136 if (!candidate) { | |
1137 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL."; | |
1138 return false; | |
1139 } | |
1140 | |
1141 bool valid = false; | |
1142 bool ready = ReadyToUseRemoteCandidate(candidate, NULL, &valid); | |
1143 if (!valid) { | |
1144 return false; | |
1145 } | |
1146 | |
1147 // Add this candidate to the remote session description. | |
1148 if (!mutable_remote_description()->AddCandidate(candidate)) { | |
1149 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used."; | |
1150 return false; | |
1151 } | |
1152 | |
1153 if (ready) { | |
1154 return UseCandidate(candidate); | |
1155 } else { | |
1156 LOG(LS_INFO) << "ProcessIceMessage: Not ready to use candidate."; | |
1157 return true; | |
1158 } | |
1159 } | |
1160 | |
1161 bool WebRtcSession::RemoveRemoteIceCandidates( | |
1162 const std::vector<cricket::Candidate>& candidates) { | |
1163 if (!remote_description()) { | |
1164 LOG(LS_ERROR) << "RemoveRemoteIceCandidates: ICE candidates can't be " | |
1165 << "removed without any remote session description."; | |
1166 return false; | |
1167 } | |
1168 | |
1169 if (candidates.empty()) { | |
1170 LOG(LS_ERROR) << "RemoveRemoteIceCandidates: candidates are empty."; | |
1171 return false; | |
1172 } | |
1173 | |
1174 size_t number_removed = | |
1175 mutable_remote_description()->RemoveCandidates(candidates); | |
1176 if (number_removed != candidates.size()) { | |
1177 LOG(LS_ERROR) << "RemoveRemoteIceCandidates: Failed to remove candidates. " | |
1178 << "Requested " << candidates.size() << " but only " | |
1179 << number_removed << " are removed."; | |
1180 } | |
1181 | |
1182 // Remove the candidates from the transport controller. | |
1183 std::string error; | |
1184 bool res = transport_controller_->RemoveRemoteCandidates(candidates, &error); | |
1185 if (!res && !error.empty()) { | |
1186 LOG(LS_ERROR) << "Error when removing remote candidates: " << error; | |
1187 } | |
1188 return true; | |
1189 } | |
1190 | |
1191 cricket::IceConfig WebRtcSession::ParseIceConfig( | |
1192 const PeerConnectionInterface::RTCConfiguration& config) const { | |
1193 cricket::ContinualGatheringPolicy gathering_policy; | |
1194 // TODO(honghaiz): Add the third continual gathering policy in | |
1195 // PeerConnectionInterface and map it to GATHER_CONTINUALLY_AND_RECOVER. | |
1196 switch (config.continual_gathering_policy) { | |
1197 case PeerConnectionInterface::GATHER_ONCE: | |
1198 gathering_policy = cricket::GATHER_ONCE; | |
1199 break; | |
1200 case PeerConnectionInterface::GATHER_CONTINUALLY: | |
1201 gathering_policy = cricket::GATHER_CONTINUALLY; | |
1202 break; | |
1203 default: | |
1204 RTC_NOTREACHED(); | |
1205 gathering_policy = cricket::GATHER_ONCE; | |
1206 } | |
1207 cricket::IceConfig ice_config; | |
1208 ice_config.receiving_timeout = config.ice_connection_receiving_timeout; | |
1209 ice_config.prioritize_most_likely_candidate_pairs = | |
1210 config.prioritize_most_likely_ice_candidate_pairs; | |
1211 ice_config.backup_connection_ping_interval = | |
1212 config.ice_backup_candidate_pair_ping_interval; | |
1213 ice_config.continual_gathering_policy = gathering_policy; | |
1214 ice_config.presume_writable_when_fully_relayed = | |
1215 config.presume_writable_when_fully_relayed; | |
1216 return ice_config; | |
1217 } | |
1218 | |
1219 void WebRtcSession::SetIceConfig(const cricket::IceConfig& config) { | |
1220 transport_controller_->SetIceConfig(config); | |
1221 } | |
1222 | |
1223 void WebRtcSession::MaybeStartGathering() { | |
1224 transport_controller_->MaybeStartGathering(); | |
1225 } | |
1226 | |
1227 bool WebRtcSession::GetLocalTrackIdBySsrc(uint32_t ssrc, | |
1228 std::string* track_id) { | |
1229 if (!local_description()) { | |
1230 return false; | |
1231 } | |
1232 return webrtc::GetTrackIdBySsrc(local_description()->description(), ssrc, | |
1233 track_id); | |
1234 } | |
1235 | |
1236 bool WebRtcSession::GetRemoteTrackIdBySsrc(uint32_t ssrc, | |
1237 std::string* track_id) { | |
1238 if (!remote_description()) { | |
1239 return false; | |
1240 } | |
1241 return webrtc::GetTrackIdBySsrc(remote_description()->description(), ssrc, | |
1242 track_id); | |
1243 } | |
1244 | |
1245 std::string WebRtcSession::BadStateErrMsg(State state) { | |
1246 std::ostringstream desc; | |
1247 desc << "Called in wrong state: " << GetStateString(state); | |
1248 return desc.str(); | |
1249 } | |
1250 | |
1251 bool WebRtcSession::CanInsertDtmf(const std::string& track_id) { | |
1252 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
1253 if (!voice_channel_) { | |
1254 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists."; | |
1255 return false; | |
1256 } | |
1257 uint32_t send_ssrc = 0; | |
1258 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc | |
1259 // exists. | |
1260 if (!local_description() || | |
1261 !GetAudioSsrcByTrackId(local_description()->description(), track_id, | |
1262 &send_ssrc)) { | |
1263 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id; | |
1264 return false; | |
1265 } | |
1266 return voice_channel_->CanInsertDtmf(); | |
1267 } | |
1268 | |
1269 bool WebRtcSession::InsertDtmf(const std::string& track_id, | |
1270 int code, int duration) { | |
1271 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
1272 if (!voice_channel_) { | |
1273 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists."; | |
1274 return false; | |
1275 } | |
1276 uint32_t send_ssrc = 0; | |
1277 if (!VERIFY(local_description() && | |
1278 GetAudioSsrcByTrackId(local_description()->description(), | |
1279 track_id, &send_ssrc))) { | |
1280 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id; | |
1281 return false; | |
1282 } | |
1283 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration)) { | |
1284 LOG(LS_ERROR) << "Failed to insert DTMF to channel."; | |
1285 return false; | |
1286 } | |
1287 return true; | |
1288 } | |
1289 | |
1290 sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() { | |
1291 return &SignalDestroyed; | |
1292 } | |
1293 | |
1294 bool WebRtcSession::SendData(const cricket::SendDataParams& params, | |
1295 const rtc::CopyOnWriteBuffer& payload, | |
1296 cricket::SendDataResult* result) { | |
1297 if (!rtp_data_channel_ && !sctp_transport_) { | |
1298 LOG(LS_ERROR) << "SendData called when rtp_data_channel_ " | |
1299 << "and sctp_transport_ are NULL."; | |
1300 return false; | |
1301 } | |
1302 return rtp_data_channel_ | |
1303 ? rtp_data_channel_->SendData(params, payload, result) | |
1304 : network_thread_->Invoke<bool>( | |
1305 RTC_FROM_HERE, | |
1306 Bind(&cricket::SctpTransportInternal::SendData, | |
1307 sctp_transport_.get(), params, payload, result)); | |
1308 } | |
1309 | |
1310 bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) { | |
1311 if (!rtp_data_channel_ && !sctp_transport_) { | |
1312 // Don't log an error here, because DataChannels are expected to call | |
1313 // ConnectDataChannel in this state. It's the only way to initially tell | |
1314 // whether or not the underlying transport is ready. | |
1315 return false; | |
1316 } | |
1317 if (rtp_data_channel_) { | |
1318 rtp_data_channel_->SignalReadyToSendData.connect( | |
1319 webrtc_data_channel, &DataChannel::OnChannelReady); | |
1320 rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel, | |
1321 &DataChannel::OnDataReceived); | |
1322 } else { | |
1323 SignalSctpReadyToSendData.connect(webrtc_data_channel, | |
1324 &DataChannel::OnChannelReady); | |
1325 SignalSctpDataReceived.connect(webrtc_data_channel, | |
1326 &DataChannel::OnDataReceived); | |
1327 SignalSctpStreamClosedRemotely.connect( | |
1328 webrtc_data_channel, &DataChannel::OnStreamClosedRemotely); | |
1329 } | |
1330 return true; | |
1331 } | |
1332 | |
1333 void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) { | |
1334 if (!rtp_data_channel_ && !sctp_transport_) { | |
1335 LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and " | |
1336 "sctp_transport_ are NULL."; | |
1337 return; | |
1338 } | |
1339 if (rtp_data_channel_) { | |
1340 rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel); | |
1341 rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel); | |
1342 } else { | |
1343 SignalSctpReadyToSendData.disconnect(webrtc_data_channel); | |
1344 SignalSctpDataReceived.disconnect(webrtc_data_channel); | |
1345 SignalSctpStreamClosedRemotely.disconnect(webrtc_data_channel); | |
1346 } | |
1347 } | |
1348 | |
1349 void WebRtcSession::AddSctpDataStream(int sid) { | |
1350 if (!sctp_transport_) { | |
1351 LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL."; | |
1352 return; | |
1353 } | |
1354 network_thread_->Invoke<void>( | |
1355 RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream, | |
1356 sctp_transport_.get(), sid)); | |
1357 } | |
1358 | |
1359 void WebRtcSession::RemoveSctpDataStream(int sid) { | |
1360 if (!sctp_transport_) { | |
1361 LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is " | |
1362 << "NULL."; | |
1363 return; | |
1364 } | |
1365 network_thread_->Invoke<void>( | |
1366 RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream, | |
1367 sctp_transport_.get(), sid)); | |
1368 } | |
1369 | |
1370 bool WebRtcSession::ReadyToSendData() const { | |
1371 return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) || | |
1372 sctp_ready_to_send_data_; | |
1373 } | |
1374 | |
1375 std::unique_ptr<SessionStats> WebRtcSession::GetStats_s() { | |
1376 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
1377 ChannelNamePairs channel_name_pairs; | |
1378 if (voice_channel()) { | |
1379 channel_name_pairs.voice = rtc::Optional<ChannelNamePair>(ChannelNamePair( | |
1380 voice_channel()->content_name(), voice_channel()->transport_name())); | |
1381 } | |
1382 if (video_channel()) { | |
1383 channel_name_pairs.video = rtc::Optional<ChannelNamePair>(ChannelNamePair( | |
1384 video_channel()->content_name(), video_channel()->transport_name())); | |
1385 } | |
1386 if (rtp_data_channel()) { | |
1387 channel_name_pairs.data = rtc::Optional<ChannelNamePair>( | |
1388 ChannelNamePair(rtp_data_channel()->content_name(), | |
1389 rtp_data_channel()->transport_name())); | |
1390 } | |
1391 if (sctp_transport_) { | |
1392 RTC_DCHECK(sctp_content_name_); | |
1393 RTC_DCHECK(sctp_transport_name_); | |
1394 channel_name_pairs.data = rtc::Optional<ChannelNamePair>( | |
1395 ChannelNamePair(*sctp_content_name_, *sctp_transport_name_)); | |
1396 } | |
1397 return GetStats(channel_name_pairs); | |
1398 } | |
1399 | |
1400 std::unique_ptr<SessionStats> WebRtcSession::GetStats( | |
1401 const ChannelNamePairs& channel_name_pairs) { | |
1402 if (network_thread()->IsCurrent()) { | |
1403 return GetStats_n(channel_name_pairs); | |
1404 } | |
1405 return network_thread()->Invoke<std::unique_ptr<SessionStats>>( | |
1406 RTC_FROM_HERE, | |
1407 rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs)); | |
1408 } | |
1409 | |
1410 bool WebRtcSession::GetLocalCertificate( | |
1411 const std::string& transport_name, | |
1412 rtc::scoped_refptr<rtc::RTCCertificate>* certificate) { | |
1413 return transport_controller_->GetLocalCertificate(transport_name, | |
1414 certificate); | |
1415 } | |
1416 | |
1417 std::unique_ptr<rtc::SSLCertificate> WebRtcSession::GetRemoteSSLCertificate( | |
1418 const std::string& transport_name) { | |
1419 return transport_controller_->GetRemoteSSLCertificate(transport_name); | |
1420 } | |
1421 | |
1422 cricket::DataChannelType WebRtcSession::data_channel_type() const { | |
1423 return data_channel_type_; | |
1424 } | |
1425 | |
1426 bool WebRtcSession::IceRestartPending(const std::string& content_name) const { | |
1427 return pending_ice_restarts_.find(content_name) != | |
1428 pending_ice_restarts_.end(); | |
1429 } | |
1430 | |
1431 void WebRtcSession::SetNeedsIceRestartFlag() { | |
1432 transport_controller_->SetNeedsIceRestartFlag(); | |
1433 } | |
1434 | |
1435 bool WebRtcSession::NeedsIceRestart(const std::string& content_name) const { | |
1436 return transport_controller_->NeedsIceRestart(content_name); | |
1437 } | |
1438 | |
1439 void WebRtcSession::OnCertificateReady( | |
1440 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) { | |
1441 transport_controller_->SetLocalCertificate(certificate); | |
1442 } | |
1443 | |
1444 void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) { | |
1445 SetError(ERROR_TRANSPORT, | |
1446 rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); | |
1447 } | |
1448 | |
1449 bool WebRtcSession::waiting_for_certificate_for_testing() const { | |
1450 return webrtc_session_desc_factory_->waiting_for_certificate_for_testing(); | |
1451 } | |
1452 | |
1453 const rtc::scoped_refptr<rtc::RTCCertificate>& | |
1454 WebRtcSession::certificate_for_testing() { | |
1455 return transport_controller_->certificate_for_testing(); | |
1456 } | |
1457 | |
1458 void WebRtcSession::SetIceConnectionState( | |
1459 PeerConnectionInterface::IceConnectionState state) { | |
1460 if (ice_connection_state_ == state) { | |
1461 return; | |
1462 } | |
1463 | |
1464 LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_ | |
1465 << " => " << state; | |
1466 RTC_DCHECK(ice_connection_state_ != | |
1467 PeerConnectionInterface::kIceConnectionClosed); | |
1468 ice_connection_state_ = state; | |
1469 if (ice_observer_) { | |
1470 ice_observer_->OnIceConnectionChange(ice_connection_state_); | |
1471 } | |
1472 } | |
1473 | |
1474 void WebRtcSession::OnTransportControllerConnectionState( | |
1475 cricket::IceConnectionState state) { | |
1476 switch (state) { | |
1477 case cricket::kIceConnectionConnecting: | |
1478 // If the current state is Connected or Completed, then there were | |
1479 // writable channels but now there are not, so the next state must | |
1480 // be Disconnected. | |
1481 // kIceConnectionConnecting is currently used as the default, | |
1482 // un-connected state by the TransportController, so its only use is | |
1483 // detecting disconnections. | |
1484 if (ice_connection_state_ == | |
1485 PeerConnectionInterface::kIceConnectionConnected || | |
1486 ice_connection_state_ == | |
1487 PeerConnectionInterface::kIceConnectionCompleted) { | |
1488 SetIceConnectionState( | |
1489 PeerConnectionInterface::kIceConnectionDisconnected); | |
1490 } | |
1491 break; | |
1492 case cricket::kIceConnectionFailed: | |
1493 SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed); | |
1494 break; | |
1495 case cricket::kIceConnectionConnected: | |
1496 LOG(LS_INFO) << "Changing to ICE connected state because " | |
1497 << "all transports are writable."; | |
1498 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); | |
1499 break; | |
1500 case cricket::kIceConnectionCompleted: | |
1501 LOG(LS_INFO) << "Changing to ICE completed state because " | |
1502 << "all transports are complete."; | |
1503 if (ice_connection_state_ != | |
1504 PeerConnectionInterface::kIceConnectionConnected) { | |
1505 // If jumping directly from "checking" to "connected", | |
1506 // signal "connected" first. | |
1507 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); | |
1508 } | |
1509 SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); | |
1510 if (metrics_observer_) { | |
1511 ReportTransportStats(); | |
1512 } | |
1513 break; | |
1514 default: | |
1515 RTC_NOTREACHED(); | |
1516 } | |
1517 } | |
1518 | |
1519 void WebRtcSession::OnTransportControllerReceiving(bool receiving) { | |
1520 SetIceConnectionReceiving(receiving); | |
1521 } | |
1522 | |
1523 void WebRtcSession::SetIceConnectionReceiving(bool receiving) { | |
1524 if (ice_connection_receiving_ == receiving) { | |
1525 return; | |
1526 } | |
1527 ice_connection_receiving_ = receiving; | |
1528 if (ice_observer_) { | |
1529 ice_observer_->OnIceConnectionReceivingChange(receiving); | |
1530 } | |
1531 } | |
1532 | |
1533 void WebRtcSession::OnTransportControllerCandidatesGathered( | |
1534 const std::string& transport_name, | |
1535 const cricket::Candidates& candidates) { | |
1536 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
1537 int sdp_mline_index; | |
1538 if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { | |
1539 LOG(LS_ERROR) << "OnTransportControllerCandidatesGathered: content name " | |
1540 << transport_name << " not found"; | |
1541 return; | |
1542 } | |
1543 | |
1544 for (cricket::Candidates::const_iterator citer = candidates.begin(); | |
1545 citer != candidates.end(); ++citer) { | |
1546 // Use transport_name as the candidate media id. | |
1547 JsepIceCandidate candidate(transport_name, sdp_mline_index, *citer); | |
1548 if (ice_observer_) { | |
1549 ice_observer_->OnIceCandidate(&candidate); | |
1550 } | |
1551 if (local_description()) { | |
1552 mutable_local_description()->AddCandidate(&candidate); | |
1553 } | |
1554 } | |
1555 } | |
1556 | |
1557 void WebRtcSession::OnTransportControllerCandidatesRemoved( | |
1558 const std::vector<cricket::Candidate>& candidates) { | |
1559 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
1560 // Sanity check. | |
1561 for (const cricket::Candidate& candidate : candidates) { | |
1562 if (candidate.transport_name().empty()) { | |
1563 LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " | |
1564 << "empty content name in candidate " | |
1565 << candidate.ToString(); | |
1566 return; | |
1567 } | |
1568 } | |
1569 | |
1570 if (local_description()) { | |
1571 mutable_local_description()->RemoveCandidates(candidates); | |
1572 } | |
1573 if (ice_observer_) { | |
1574 ice_observer_->OnIceCandidatesRemoved(candidates); | |
1575 } | |
1576 } | |
1577 | |
1578 void WebRtcSession::OnTransportControllerDtlsHandshakeError( | |
1579 rtc::SSLHandshakeError error) { | |
1580 if (metrics_observer_) { | |
1581 metrics_observer_->IncrementEnumCounter( | |
1582 webrtc::kEnumCounterDtlsHandshakeError, static_cast<int>(error), | |
1583 static_cast<int>(rtc::SSLHandshakeError::MAX_VALUE)); | |
1584 } | |
1585 } | |
1586 | |
1587 // Enabling voice and video (and RTP data) channel. | |
1588 void WebRtcSession::EnableChannels() { | |
1589 if (voice_channel_ && !voice_channel_->enabled()) | |
1590 voice_channel_->Enable(true); | |
1591 | |
1592 if (video_channel_ && !video_channel_->enabled()) | |
1593 video_channel_->Enable(true); | |
1594 | |
1595 if (rtp_data_channel_ && !rtp_data_channel_->enabled()) | |
1596 rtp_data_channel_->Enable(true); | |
1597 } | |
1598 | |
1599 // Returns the media index for a local ice candidate given the content name. | |
1600 bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name, | |
1601 int* sdp_mline_index) { | |
1602 if (!local_description() || !sdp_mline_index) { | |
1603 return false; | |
1604 } | |
1605 | |
1606 bool content_found = false; | |
1607 const ContentInfos& contents = local_description()->description()->contents(); | |
1608 for (size_t index = 0; index < contents.size(); ++index) { | |
1609 if (contents[index].name == content_name) { | |
1610 *sdp_mline_index = static_cast<int>(index); | |
1611 content_found = true; | |
1612 break; | |
1613 } | |
1614 } | |
1615 return content_found; | |
1616 } | |
1617 | |
1618 bool WebRtcSession::UseCandidatesInSessionDescription( | |
1619 const SessionDescriptionInterface* remote_desc) { | |
1620 if (!remote_desc) { | |
1621 return true; | |
1622 } | |
1623 bool ret = true; | |
1624 | |
1625 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) { | |
1626 const IceCandidateCollection* candidates = remote_desc->candidates(m); | |
1627 for (size_t n = 0; n < candidates->count(); ++n) { | |
1628 const IceCandidateInterface* candidate = candidates->at(n); | |
1629 bool valid = false; | |
1630 if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) { | |
1631 if (valid) { | |
1632 LOG(LS_INFO) << "UseCandidatesInSessionDescription: Not ready to use " | |
1633 << "candidate."; | |
1634 } | |
1635 continue; | |
1636 } | |
1637 ret = UseCandidate(candidate); | |
1638 if (!ret) { | |
1639 break; | |
1640 } | |
1641 } | |
1642 } | |
1643 return ret; | |
1644 } | |
1645 | |
1646 bool WebRtcSession::UseCandidate(const IceCandidateInterface* candidate) { | |
1647 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index()); | |
1648 size_t remote_content_size = | |
1649 remote_description()->description()->contents().size(); | |
1650 if (mediacontent_index >= remote_content_size) { | |
1651 LOG(LS_ERROR) << "UseCandidate: Invalid candidate media index."; | |
1652 return false; | |
1653 } | |
1654 | |
1655 cricket::ContentInfo content = | |
1656 remote_description()->description()->contents()[mediacontent_index]; | |
1657 std::vector<cricket::Candidate> candidates; | |
1658 candidates.push_back(candidate->candidate()); | |
1659 // Invoking BaseSession method to handle remote candidates. | |
1660 std::string error; | |
1661 if (transport_controller_->AddRemoteCandidates(content.name, candidates, | |
1662 &error)) { | |
1663 // Candidates successfully submitted for checking. | |
1664 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew || | |
1665 ice_connection_state_ == | |
1666 PeerConnectionInterface::kIceConnectionDisconnected) { | |
1667 // If state is New, then the session has just gotten its first remote ICE | |
1668 // candidates, so go to Checking. | |
1669 // If state is Disconnected, the session is re-using old candidates or | |
1670 // receiving additional ones, so go to Checking. | |
1671 // If state is Connected, stay Connected. | |
1672 // TODO(bemasc): If state is Connected, and the new candidates are for a | |
1673 // newly added transport, then the state actually _should_ move to | |
1674 // checking. Add a way to distinguish that case. | |
1675 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); | |
1676 } | |
1677 // TODO(bemasc): If state is Completed, go back to Connected. | |
1678 } else { | |
1679 if (!error.empty()) { | |
1680 LOG(LS_WARNING) << error; | |
1681 } | |
1682 } | |
1683 return true; | |
1684 } | |
1685 | |
1686 void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { | |
1687 // Destroy video_channel_ first since it may have a pointer to the | |
1688 // voice_channel_. | |
1689 const cricket::ContentInfo* video_info = | |
1690 cricket::GetFirstVideoContent(desc); | |
1691 if ((!video_info || video_info->rejected) && video_channel_) { | |
1692 DestroyVideoChannel(); | |
1693 } | |
1694 | |
1695 const cricket::ContentInfo* voice_info = | |
1696 cricket::GetFirstAudioContent(desc); | |
1697 if ((!voice_info || voice_info->rejected) && voice_channel_) { | |
1698 DestroyVoiceChannel(); | |
1699 } | |
1700 | |
1701 const cricket::ContentInfo* data_info = | |
1702 cricket::GetFirstDataContent(desc); | |
1703 if (!data_info || data_info->rejected) { | |
1704 if (rtp_data_channel_) { | |
1705 DestroyDataChannel(); | |
1706 } | |
1707 if (sctp_transport_) { | |
1708 SignalDataChannelDestroyed(); | |
1709 network_thread_->Invoke<void>( | |
1710 RTC_FROM_HERE, | |
1711 rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); | |
1712 } | |
1713 #ifdef HAVE_QUIC | |
1714 // Clean up the existing QuicDataTransport and its QuicTransportChannels. | |
1715 if (quic_data_transport_) { | |
1716 quic_data_transport_.reset(); | |
1717 } | |
1718 #endif | |
1719 } | |
1720 } | |
1721 | |
1722 // Returns the name of the transport channel when BUNDLE is enabled, or nullptr | |
1723 // if the channel is not part of any bundle. | |
1724 const std::string* WebRtcSession::GetBundleTransportName( | |
1725 const cricket::ContentInfo* content, | |
1726 const cricket::ContentGroup* bundle) { | |
1727 if (!bundle) { | |
1728 return nullptr; | |
1729 } | |
1730 const std::string* first_content_name = bundle->FirstContentName(); | |
1731 if (!first_content_name) { | |
1732 LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; | |
1733 return nullptr; | |
1734 } | |
1735 if (!bundle->HasContentName(content->name)) { | |
1736 LOG(LS_WARNING) << content->name << " is not part of any bundle group"; | |
1737 return nullptr; | |
1738 } | |
1739 LOG(LS_INFO) << "Bundling " << content->name << " on " << *first_content_name; | |
1740 return first_content_name; | |
1741 } | |
1742 | |
1743 bool WebRtcSession::CreateChannels(const SessionDescription* desc) { | |
1744 const cricket::ContentGroup* bundle_group = nullptr; | |
1745 if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) { | |
1746 bundle_group = desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); | |
1747 if (!bundle_group) { | |
1748 LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified"; | |
1749 return false; | |
1750 } | |
1751 } | |
1752 // Creating the media channels and transport proxies. | |
1753 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc); | |
1754 if (voice && !voice->rejected && !voice_channel_) { | |
1755 if (!CreateVoiceChannel(voice, | |
1756 GetBundleTransportName(voice, bundle_group))) { | |
1757 LOG(LS_ERROR) << "Failed to create voice channel."; | |
1758 return false; | |
1759 } | |
1760 } | |
1761 | |
1762 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc); | |
1763 if (video && !video->rejected && !video_channel_) { | |
1764 if (!CreateVideoChannel(video, | |
1765 GetBundleTransportName(video, bundle_group))) { | |
1766 LOG(LS_ERROR) << "Failed to create video channel."; | |
1767 return false; | |
1768 } | |
1769 } | |
1770 | |
1771 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc); | |
1772 if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected && | |
1773 !rtp_data_channel_ && !sctp_transport_) { | |
1774 if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) { | |
1775 LOG(LS_ERROR) << "Failed to create data channel."; | |
1776 return false; | |
1777 } | |
1778 } | |
1779 | |
1780 return true; | |
1781 } | |
1782 | |
1783 bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content, | |
1784 const std::string* bundle_transport) { | |
1785 bool require_rtcp_mux = | |
1786 rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; | |
1787 | |
1788 std::string transport_name = | |
1789 bundle_transport ? *bundle_transport : content->name; | |
1790 | |
1791 cricket::DtlsTransportInternal* rtp_dtls_transport = | |
1792 transport_controller_->CreateDtlsTransport( | |
1793 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1794 cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; | |
1795 if (!require_rtcp_mux) { | |
1796 rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( | |
1797 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
1798 } | |
1799 | |
1800 voice_channel_.reset(channel_manager_->CreateVoiceChannel( | |
1801 media_controller_, rtp_dtls_transport, rtcp_dtls_transport, | |
1802 transport_controller_->signaling_thread(), content->name, | |
1803 bundle_transport, require_rtcp_mux, SrtpRequired(), audio_options_)); | |
1804 if (!voice_channel_) { | |
1805 transport_controller_->DestroyDtlsTransport( | |
1806 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1807 if (rtcp_dtls_transport) { | |
1808 transport_controller_->DestroyDtlsTransport( | |
1809 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1810 } | |
1811 return false; | |
1812 } | |
1813 | |
1814 voice_channel_->SignalRtcpMuxFullyActive.connect( | |
1815 this, &WebRtcSession::DestroyRtcpTransport_n); | |
1816 voice_channel_->SignalDtlsSrtpSetupFailure.connect( | |
1817 this, &WebRtcSession::OnDtlsSrtpSetupFailure); | |
1818 | |
1819 SignalVoiceChannelCreated(); | |
1820 voice_channel_->SignalSentPacket.connect(this, | |
1821 &WebRtcSession::OnSentPacket_w); | |
1822 return true; | |
1823 } | |
1824 | |
1825 bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content, | |
1826 const std::string* bundle_transport) { | |
1827 bool require_rtcp_mux = | |
1828 rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; | |
1829 | |
1830 std::string transport_name = | |
1831 bundle_transport ? *bundle_transport : content->name; | |
1832 | |
1833 cricket::DtlsTransportInternal* rtp_dtls_transport = | |
1834 transport_controller_->CreateDtlsTransport( | |
1835 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1836 cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; | |
1837 if (!require_rtcp_mux) { | |
1838 rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( | |
1839 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
1840 } | |
1841 | |
1842 video_channel_.reset(channel_manager_->CreateVideoChannel( | |
1843 media_controller_, rtp_dtls_transport, rtcp_dtls_transport, | |
1844 transport_controller_->signaling_thread(), content->name, | |
1845 bundle_transport, require_rtcp_mux, SrtpRequired(), video_options_)); | |
1846 | |
1847 if (!video_channel_) { | |
1848 transport_controller_->DestroyDtlsTransport( | |
1849 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1850 if (rtcp_dtls_transport) { | |
1851 transport_controller_->DestroyDtlsTransport( | |
1852 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1853 } | |
1854 return false; | |
1855 } | |
1856 | |
1857 video_channel_->SignalRtcpMuxFullyActive.connect( | |
1858 this, &WebRtcSession::DestroyRtcpTransport_n); | |
1859 video_channel_->SignalDtlsSrtpSetupFailure.connect( | |
1860 this, &WebRtcSession::OnDtlsSrtpSetupFailure); | |
1861 | |
1862 SignalVideoChannelCreated(); | |
1863 video_channel_->SignalSentPacket.connect(this, | |
1864 &WebRtcSession::OnSentPacket_w); | |
1865 return true; | |
1866 } | |
1867 | |
1868 bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content, | |
1869 const std::string* bundle_transport) { | |
1870 const std::string transport_name = | |
1871 bundle_transport ? *bundle_transport : content->name; | |
1872 #ifdef HAVE_QUIC | |
1873 if (data_channel_type_ == cricket::DCT_QUIC) { | |
1874 RTC_DCHECK(transport_controller_->quic()); | |
1875 quic_data_transport_->SetTransports(transport_name); | |
1876 return true; | |
1877 } | |
1878 #endif // HAVE_QUIC | |
1879 bool sctp = (data_channel_type_ == cricket::DCT_SCTP); | |
1880 if (sctp) { | |
1881 if (!sctp_factory_) { | |
1882 LOG(LS_ERROR) | |
1883 << "Trying to create SCTP transport, but didn't compile with " | |
1884 "SCTP support (HAVE_SCTP)"; | |
1885 return false; | |
1886 } | |
1887 if (!network_thread_->Invoke<bool>( | |
1888 RTC_FROM_HERE, rtc::Bind(&WebRtcSession::CreateSctpTransport_n, | |
1889 this, content->name, transport_name))) { | |
1890 return false; | |
1891 }; | |
1892 } else { | |
1893 bool require_rtcp_mux = | |
1894 rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; | |
1895 | |
1896 std::string transport_name = | |
1897 bundle_transport ? *bundle_transport : content->name; | |
1898 cricket::DtlsTransportInternal* rtp_dtls_transport = | |
1899 transport_controller_->CreateDtlsTransport( | |
1900 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1901 cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; | |
1902 if (!require_rtcp_mux) { | |
1903 rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( | |
1904 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
1905 } | |
1906 | |
1907 rtp_data_channel_.reset(channel_manager_->CreateRtpDataChannel( | |
1908 media_controller_, rtp_dtls_transport, rtcp_dtls_transport, | |
1909 transport_controller_->signaling_thread(), content->name, | |
1910 bundle_transport, require_rtcp_mux, SrtpRequired())); | |
1911 | |
1912 if (!rtp_data_channel_) { | |
1913 transport_controller_->DestroyDtlsTransport( | |
1914 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1915 if (rtcp_dtls_transport) { | |
1916 transport_controller_->DestroyDtlsTransport( | |
1917 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1918 } | |
1919 return false; | |
1920 } | |
1921 | |
1922 rtp_data_channel_->SignalRtcpMuxFullyActive.connect( | |
1923 this, &WebRtcSession::DestroyRtcpTransport_n); | |
1924 rtp_data_channel_->SignalDtlsSrtpSetupFailure.connect( | |
1925 this, &WebRtcSession::OnDtlsSrtpSetupFailure); | |
1926 rtp_data_channel_->SignalSentPacket.connect(this, | |
1927 &WebRtcSession::OnSentPacket_w); | |
1928 } | |
1929 | |
1930 SignalDataChannelCreated(); | |
1931 | |
1932 return true; | |
1933 } | |
1934 | |
1935 std::unique_ptr<SessionStats> WebRtcSession::GetStats_n( | |
1936 const ChannelNamePairs& channel_name_pairs) { | |
1937 RTC_DCHECK(network_thread()->IsCurrent()); | |
1938 std::unique_ptr<SessionStats> session_stats(new SessionStats()); | |
1939 for (const auto channel_name_pair : { &channel_name_pairs.voice, | |
1940 &channel_name_pairs.video, | |
1941 &channel_name_pairs.data }) { | |
1942 if (*channel_name_pair) { | |
1943 cricket::TransportStats transport_stats; | |
1944 if (!transport_controller_->GetStats((*channel_name_pair)->transport_name, | |
1945 &transport_stats)) { | |
1946 return nullptr; | |
1947 } | |
1948 session_stats->proxy_to_transport[(*channel_name_pair)->content_name] = | |
1949 (*channel_name_pair)->transport_name; | |
1950 session_stats->transport_stats[(*channel_name_pair)->transport_name] = | |
1951 std::move(transport_stats); | |
1952 } | |
1953 } | |
1954 return session_stats; | |
1955 } | |
1956 | |
1957 bool WebRtcSession::CreateSctpTransport_n(const std::string& content_name, | |
1958 const std::string& transport_name) { | |
1959 RTC_DCHECK(network_thread_->IsCurrent()); | |
1960 RTC_DCHECK(sctp_factory_); | |
1961 cricket::DtlsTransportInternal* tc = | |
1962 transport_controller_->CreateDtlsTransport_n( | |
1963 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1964 sctp_transport_ = sctp_factory_->CreateSctpTransport(tc); | |
1965 RTC_DCHECK(sctp_transport_); | |
1966 sctp_invoker_.reset(new rtc::AsyncInvoker()); | |
1967 sctp_transport_->SignalReadyToSendData.connect( | |
1968 this, &WebRtcSession::OnSctpTransportReadyToSendData_n); | |
1969 sctp_transport_->SignalDataReceived.connect( | |
1970 this, &WebRtcSession::OnSctpTransportDataReceived_n); | |
1971 sctp_transport_->SignalStreamClosedRemotely.connect( | |
1972 this, &WebRtcSession::OnSctpStreamClosedRemotely_n); | |
1973 sctp_transport_name_ = rtc::Optional<std::string>(transport_name); | |
1974 sctp_content_name_ = rtc::Optional<std::string>(content_name); | |
1975 return true; | |
1976 } | |
1977 | |
1978 void WebRtcSession::ChangeSctpTransport_n(const std::string& transport_name) { | |
1979 RTC_DCHECK(network_thread_->IsCurrent()); | |
1980 RTC_DCHECK(sctp_transport_); | |
1981 RTC_DCHECK(sctp_transport_name_); | |
1982 std::string old_sctp_transport_name = *sctp_transport_name_; | |
1983 sctp_transport_name_ = rtc::Optional<std::string>(transport_name); | |
1984 cricket::DtlsTransportInternal* tc = | |
1985 transport_controller_->CreateDtlsTransport_n( | |
1986 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1987 sctp_transport_->SetTransportChannel(tc); | |
1988 transport_controller_->DestroyDtlsTransport_n( | |
1989 old_sctp_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
1990 } | |
1991 | |
1992 void WebRtcSession::DestroySctpTransport_n() { | |
1993 RTC_DCHECK(network_thread_->IsCurrent()); | |
1994 sctp_transport_.reset(nullptr); | |
1995 sctp_content_name_.reset(); | |
1996 sctp_transport_name_.reset(); | |
1997 sctp_invoker_.reset(nullptr); | |
1998 sctp_ready_to_send_data_ = false; | |
1999 } | |
2000 | |
2001 void WebRtcSession::OnSctpTransportReadyToSendData_n() { | |
2002 RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); | |
2003 RTC_DCHECK(network_thread_->IsCurrent()); | |
2004 sctp_invoker_->AsyncInvoke<void>( | |
2005 RTC_FROM_HERE, signaling_thread_, | |
2006 rtc::Bind(&WebRtcSession::OnSctpTransportReadyToSendData_s, this, true)); | |
2007 } | |
2008 | |
2009 void WebRtcSession::OnSctpTransportReadyToSendData_s(bool ready) { | |
2010 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
2011 sctp_ready_to_send_data_ = ready; | |
2012 SignalSctpReadyToSendData(ready); | |
2013 } | |
2014 | |
2015 void WebRtcSession::OnSctpTransportDataReceived_n( | |
2016 const cricket::ReceiveDataParams& params, | |
2017 const rtc::CopyOnWriteBuffer& payload) { | |
2018 RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); | |
2019 RTC_DCHECK(network_thread_->IsCurrent()); | |
2020 sctp_invoker_->AsyncInvoke<void>( | |
2021 RTC_FROM_HERE, signaling_thread_, | |
2022 rtc::Bind(&WebRtcSession::OnSctpTransportDataReceived_s, this, params, | |
2023 payload)); | |
2024 } | |
2025 | |
2026 void WebRtcSession::OnSctpTransportDataReceived_s( | |
2027 const cricket::ReceiveDataParams& params, | |
2028 const rtc::CopyOnWriteBuffer& payload) { | |
2029 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
2030 if (params.type == cricket::DMT_CONTROL && IsOpenMessage(payload)) { | |
2031 // Received OPEN message; parse and signal that a new data channel should | |
2032 // be created. | |
2033 std::string label; | |
2034 InternalDataChannelInit config; | |
2035 config.id = params.ssrc; | |
2036 if (!ParseDataChannelOpenMessage(payload, &label, &config)) { | |
2037 LOG(LS_WARNING) << "Failed to parse the OPEN message for sid " | |
2038 << params.ssrc; | |
2039 return; | |
2040 } | |
2041 config.open_handshake_role = InternalDataChannelInit::kAcker; | |
2042 SignalDataChannelOpenMessage(label, config); | |
2043 } else { | |
2044 // Otherwise just forward the signal. | |
2045 SignalSctpDataReceived(params, payload); | |
2046 } | |
2047 } | |
2048 | |
2049 void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) { | |
2050 RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); | |
2051 RTC_DCHECK(network_thread_->IsCurrent()); | |
2052 sctp_invoker_->AsyncInvoke<void>( | |
2053 RTC_FROM_HERE, signaling_thread_, | |
2054 rtc::Bind(&sigslot::signal1<int>::operator(), | |
2055 &SignalSctpStreamClosedRemotely, sid)); | |
2056 } | |
2057 | |
2058 // Returns false if bundle is enabled and rtcp_mux is disabled. | |
2059 bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) { | |
2060 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE); | |
2061 if (!bundle_enabled) | |
2062 return true; | |
2063 | |
2064 const cricket::ContentGroup* bundle_group = | |
2065 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); | |
2066 RTC_DCHECK(bundle_group != NULL); | |
2067 | |
2068 const cricket::ContentInfos& contents = desc->contents(); | |
2069 for (cricket::ContentInfos::const_iterator citer = contents.begin(); | |
2070 citer != contents.end(); ++citer) { | |
2071 const cricket::ContentInfo* content = (&*citer); | |
2072 RTC_DCHECK(content != NULL); | |
2073 if (bundle_group->HasContentName(content->name) && | |
2074 !content->rejected && content->type == cricket::NS_JINGLE_RTP) { | |
2075 if (!HasRtcpMuxEnabled(content)) | |
2076 return false; | |
2077 } | |
2078 } | |
2079 // RTCP-MUX is enabled in all the contents. | |
2080 return true; | |
2081 } | |
2082 | |
2083 bool WebRtcSession::HasRtcpMuxEnabled( | |
2084 const cricket::ContentInfo* content) { | |
2085 const cricket::MediaContentDescription* description = | |
2086 static_cast<cricket::MediaContentDescription*>(content->description); | |
2087 return description->rtcp_mux(); | |
2088 } | |
2089 | |
2090 bool WebRtcSession::ValidateSessionDescription( | |
2091 const SessionDescriptionInterface* sdesc, | |
2092 cricket::ContentSource source, std::string* err_desc) { | |
2093 std::string type; | |
2094 if (error() != ERROR_NONE) { | |
2095 return BadSdp(source, type, GetSessionErrorMsg(), err_desc); | |
2096 } | |
2097 | |
2098 if (!sdesc || !sdesc->description()) { | |
2099 return BadSdp(source, type, kInvalidSdp, err_desc); | |
2100 } | |
2101 | |
2102 type = sdesc->type(); | |
2103 Action action = GetAction(sdesc->type()); | |
2104 if (source == cricket::CS_LOCAL) { | |
2105 if (!ExpectSetLocalDescription(action)) | |
2106 return BadLocalSdp(type, BadStateErrMsg(state()), err_desc); | |
2107 } else { | |
2108 if (!ExpectSetRemoteDescription(action)) | |
2109 return BadRemoteSdp(type, BadStateErrMsg(state()), err_desc); | |
2110 } | |
2111 | |
2112 // Verify crypto settings. | |
2113 std::string crypto_error; | |
2114 if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || | |
2115 dtls_enabled_) && | |
2116 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) { | |
2117 return BadSdp(source, type, crypto_error, err_desc); | |
2118 } | |
2119 | |
2120 // Verify ice-ufrag and ice-pwd. | |
2121 if (!VerifyIceUfragPwdPresent(sdesc->description())) { | |
2122 return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc); | |
2123 } | |
2124 | |
2125 if (!ValidateBundleSettings(sdesc->description())) { | |
2126 return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc); | |
2127 } | |
2128 | |
2129 // TODO(skvlad): When the local rtcp-mux policy is Require, reject any | |
2130 // m-lines that do not rtcp-mux enabled. | |
2131 | |
2132 // Verify m-lines in Answer when compared against Offer. | |
2133 if (action == kAnswer) { | |
2134 const cricket::SessionDescription* offer_desc = | |
2135 (source == cricket::CS_LOCAL) ? remote_description()->description() | |
2136 : local_description()->description(); | |
2137 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) { | |
2138 return BadAnswerSdp(source, kMlineMismatch, err_desc); | |
2139 } | |
2140 } | |
2141 | |
2142 return true; | |
2143 } | |
2144 | |
2145 bool WebRtcSession::ExpectSetLocalDescription(Action action) { | |
2146 return ((action == kOffer && state() == STATE_INIT) || | |
2147 // update local offer | |
2148 (action == kOffer && state() == STATE_SENTOFFER) || | |
2149 // update the current ongoing session. | |
2150 (action == kOffer && state() == STATE_INPROGRESS) || | |
2151 // accept remote offer | |
2152 (action == kAnswer && state() == STATE_RECEIVEDOFFER) || | |
2153 (action == kAnswer && state() == STATE_SENTPRANSWER) || | |
2154 (action == kPrAnswer && state() == STATE_RECEIVEDOFFER) || | |
2155 (action == kPrAnswer && state() == STATE_SENTPRANSWER)); | |
2156 } | |
2157 | |
2158 bool WebRtcSession::ExpectSetRemoteDescription(Action action) { | |
2159 return ((action == kOffer && state() == STATE_INIT) || | |
2160 // update remote offer | |
2161 (action == kOffer && state() == STATE_RECEIVEDOFFER) || | |
2162 // update the current ongoing session | |
2163 (action == kOffer && state() == STATE_INPROGRESS) || | |
2164 // accept local offer | |
2165 (action == kAnswer && state() == STATE_SENTOFFER) || | |
2166 (action == kAnswer && state() == STATE_RECEIVEDPRANSWER) || | |
2167 (action == kPrAnswer && state() == STATE_SENTOFFER) || | |
2168 (action == kPrAnswer && state() == STATE_RECEIVEDPRANSWER)); | |
2169 } | |
2170 | |
2171 std::string WebRtcSession::GetSessionErrorMsg() { | |
2172 std::ostringstream desc; | |
2173 desc << kSessionError << GetErrorCodeString(error()) << ". "; | |
2174 desc << kSessionErrorDesc << error_desc() << "."; | |
2175 return desc.str(); | |
2176 } | |
2177 | |
2178 // We need to check the local/remote description for the Transport instead of | |
2179 // the session, because a new Transport added during renegotiation may have | |
2180 // them unset while the session has them set from the previous negotiation. | |
2181 // Not doing so may trigger the auto generation of transport description and | |
2182 // mess up DTLS identity information, ICE credential, etc. | |
2183 bool WebRtcSession::ReadyToUseRemoteCandidate( | |
2184 const IceCandidateInterface* candidate, | |
2185 const SessionDescriptionInterface* remote_desc, | |
2186 bool* valid) { | |
2187 *valid = true; | |
2188 | |
2189 const SessionDescriptionInterface* current_remote_desc = | |
2190 remote_desc ? remote_desc : remote_description(); | |
2191 | |
2192 if (!current_remote_desc) { | |
2193 return false; | |
2194 } | |
2195 | |
2196 size_t mediacontent_index = | |
2197 static_cast<size_t>(candidate->sdp_mline_index()); | |
2198 size_t remote_content_size = | |
2199 current_remote_desc->description()->contents().size(); | |
2200 if (mediacontent_index >= remote_content_size) { | |
2201 LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate media index " | |
2202 << mediacontent_index; | |
2203 | |
2204 *valid = false; | |
2205 return false; | |
2206 } | |
2207 | |
2208 cricket::ContentInfo content = | |
2209 current_remote_desc->description()->contents()[mediacontent_index]; | |
2210 | |
2211 const std::string transport_name = GetTransportName(content.name); | |
2212 if (transport_name.empty()) { | |
2213 return false; | |
2214 } | |
2215 return transport_controller_->ReadyForRemoteCandidates(transport_name); | |
2216 } | |
2217 | |
2218 bool WebRtcSession::SrtpRequired() const { | |
2219 return dtls_enabled_ || | |
2220 webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED; | |
2221 } | |
2222 | |
2223 void WebRtcSession::OnTransportControllerGatheringState( | |
2224 cricket::IceGatheringState state) { | |
2225 RTC_DCHECK(signaling_thread()->IsCurrent()); | |
2226 if (state == cricket::kIceGatheringGathering) { | |
2227 if (ice_observer_) { | |
2228 ice_observer_->OnIceGatheringChange( | |
2229 PeerConnectionInterface::kIceGatheringGathering); | |
2230 } | |
2231 } else if (state == cricket::kIceGatheringComplete) { | |
2232 if (ice_observer_) { | |
2233 ice_observer_->OnIceGatheringChange( | |
2234 PeerConnectionInterface::kIceGatheringComplete); | |
2235 } | |
2236 } | |
2237 } | |
2238 | |
2239 void WebRtcSession::ReportTransportStats() { | |
2240 // Use a set so we don't report the same stats twice if two channels share | |
2241 // a transport. | |
2242 std::set<std::string> transport_names; | |
2243 if (voice_channel()) { | |
2244 transport_names.insert(voice_channel()->transport_name()); | |
2245 } | |
2246 if (video_channel()) { | |
2247 transport_names.insert(video_channel()->transport_name()); | |
2248 } | |
2249 if (rtp_data_channel()) { | |
2250 transport_names.insert(rtp_data_channel()->transport_name()); | |
2251 } | |
2252 if (sctp_transport_name_) { | |
2253 transport_names.insert(*sctp_transport_name_); | |
2254 } | |
2255 for (const auto& name : transport_names) { | |
2256 cricket::TransportStats stats; | |
2257 if (transport_controller_->GetStats(name, &stats)) { | |
2258 ReportBestConnectionState(stats); | |
2259 ReportNegotiatedCiphers(stats); | |
2260 } | |
2261 } | |
2262 } | |
2263 // Walk through the ConnectionInfos to gather best connection usage | |
2264 // for IPv4 and IPv6. | |
2265 void WebRtcSession::ReportBestConnectionState( | |
2266 const cricket::TransportStats& stats) { | |
2267 RTC_DCHECK(metrics_observer_ != NULL); | |
2268 for (cricket::TransportChannelStatsList::const_iterator it = | |
2269 stats.channel_stats.begin(); | |
2270 it != stats.channel_stats.end(); ++it) { | |
2271 for (cricket::ConnectionInfos::const_iterator it_info = | |
2272 it->connection_infos.begin(); | |
2273 it_info != it->connection_infos.end(); ++it_info) { | |
2274 if (!it_info->best_connection) { | |
2275 continue; | |
2276 } | |
2277 | |
2278 PeerConnectionEnumCounterType type = kPeerConnectionEnumCounterMax; | |
2279 const cricket::Candidate& local = it_info->local_candidate; | |
2280 const cricket::Candidate& remote = it_info->remote_candidate; | |
2281 | |
2282 // Increment the counter for IceCandidatePairType. | |
2283 if (local.protocol() == cricket::TCP_PROTOCOL_NAME || | |
2284 (local.type() == RELAY_PORT_TYPE && | |
2285 local.relay_protocol() == cricket::TCP_PROTOCOL_NAME)) { | |
2286 type = kEnumCounterIceCandidatePairTypeTcp; | |
2287 } else if (local.protocol() == cricket::UDP_PROTOCOL_NAME) { | |
2288 type = kEnumCounterIceCandidatePairTypeUdp; | |
2289 } else { | |
2290 RTC_CHECK(0); | |
2291 } | |
2292 metrics_observer_->IncrementEnumCounter( | |
2293 type, GetIceCandidatePairCounter(local, remote), | |
2294 kIceCandidatePairMax); | |
2295 | |
2296 // Increment the counter for IP type. | |
2297 if (local.address().family() == AF_INET) { | |
2298 metrics_observer_->IncrementEnumCounter( | |
2299 kEnumCounterAddressFamily, kBestConnections_IPv4, | |
2300 kPeerConnectionAddressFamilyCounter_Max); | |
2301 | |
2302 } else if (local.address().family() == AF_INET6) { | |
2303 metrics_observer_->IncrementEnumCounter( | |
2304 kEnumCounterAddressFamily, kBestConnections_IPv6, | |
2305 kPeerConnectionAddressFamilyCounter_Max); | |
2306 } else { | |
2307 RTC_CHECK(0); | |
2308 } | |
2309 | |
2310 return; | |
2311 } | |
2312 } | |
2313 } | |
2314 | |
2315 void WebRtcSession::ReportNegotiatedCiphers( | |
2316 const cricket::TransportStats& stats) { | |
2317 RTC_DCHECK(metrics_observer_ != NULL); | |
2318 if (!dtls_enabled_ || stats.channel_stats.empty()) { | |
2319 return; | |
2320 } | |
2321 | |
2322 int srtp_crypto_suite = stats.channel_stats[0].srtp_crypto_suite; | |
2323 int ssl_cipher_suite = stats.channel_stats[0].ssl_cipher_suite; | |
2324 if (srtp_crypto_suite == rtc::SRTP_INVALID_CRYPTO_SUITE && | |
2325 ssl_cipher_suite == rtc::TLS_NULL_WITH_NULL_NULL) { | |
2326 return; | |
2327 } | |
2328 | |
2329 PeerConnectionEnumCounterType srtp_counter_type; | |
2330 PeerConnectionEnumCounterType ssl_counter_type; | |
2331 if (stats.transport_name == cricket::CN_AUDIO) { | |
2332 srtp_counter_type = kEnumCounterAudioSrtpCipher; | |
2333 ssl_counter_type = kEnumCounterAudioSslCipher; | |
2334 } else if (stats.transport_name == cricket::CN_VIDEO) { | |
2335 srtp_counter_type = kEnumCounterVideoSrtpCipher; | |
2336 ssl_counter_type = kEnumCounterVideoSslCipher; | |
2337 } else if (stats.transport_name == cricket::CN_DATA) { | |
2338 srtp_counter_type = kEnumCounterDataSrtpCipher; | |
2339 ssl_counter_type = kEnumCounterDataSslCipher; | |
2340 } else { | |
2341 RTC_NOTREACHED(); | |
2342 return; | |
2343 } | |
2344 | |
2345 if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE) { | |
2346 metrics_observer_->IncrementSparseEnumCounter(srtp_counter_type, | |
2347 srtp_crypto_suite); | |
2348 } | |
2349 if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL) { | |
2350 metrics_observer_->IncrementSparseEnumCounter(ssl_counter_type, | |
2351 ssl_cipher_suite); | |
2352 } | |
2353 } | |
2354 | |
2355 void WebRtcSession::OnSentPacket_w(const rtc::SentPacket& sent_packet) { | |
2356 RTC_DCHECK(worker_thread()->IsCurrent()); | |
2357 media_controller_->call_w()->OnSentPacket(sent_packet); | |
2358 } | |
2359 | |
2360 const std::string WebRtcSession::GetTransportName( | |
2361 const std::string& content_name) { | |
2362 cricket::BaseChannel* channel = GetChannel(content_name); | |
2363 if (!channel) { | |
2364 #ifdef HAVE_QUIC | |
2365 if (data_channel_type_ == cricket::DCT_QUIC && quic_data_transport_ && | |
2366 content_name == quic_data_transport_->transport_name()) { | |
2367 return quic_data_transport_->transport_name(); | |
2368 } | |
2369 #endif | |
2370 if (sctp_transport_) { | |
2371 RTC_DCHECK(sctp_content_name_); | |
2372 RTC_DCHECK(sctp_transport_name_); | |
2373 if (content_name == *sctp_content_name_) { | |
2374 return *sctp_transport_name_; | |
2375 } | |
2376 } | |
2377 // Return an empty string if failed to retrieve the transport name. | |
2378 return ""; | |
2379 } | |
2380 return channel->transport_name(); | |
2381 } | |
2382 | |
2383 void WebRtcSession::DestroyRtcpTransport_n(const std::string& transport_name) { | |
2384 RTC_DCHECK(network_thread()->IsCurrent()); | |
2385 transport_controller_->DestroyDtlsTransport_n( | |
2386 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
2387 } | |
2388 | |
2389 void WebRtcSession::DestroyVideoChannel() { | |
2390 SignalVideoChannelDestroyed(); | |
2391 RTC_DCHECK(video_channel_->rtp_dtls_transport()); | |
2392 std::string transport_name; | |
2393 transport_name = video_channel_->rtp_dtls_transport()->transport_name(); | |
2394 bool need_to_delete_rtcp = (video_channel_->rtcp_dtls_transport() != nullptr); | |
2395 channel_manager_->DestroyVideoChannel(video_channel_.release()); | |
2396 transport_controller_->DestroyDtlsTransport( | |
2397 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
2398 if (need_to_delete_rtcp) { | |
2399 transport_controller_->DestroyDtlsTransport( | |
2400 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
2401 } | |
2402 } | |
2403 | |
2404 void WebRtcSession::DestroyVoiceChannel() { | |
2405 SignalVoiceChannelDestroyed(); | |
2406 RTC_DCHECK(voice_channel_->rtp_dtls_transport()); | |
2407 std::string transport_name; | |
2408 transport_name = voice_channel_->rtp_dtls_transport()->transport_name(); | |
2409 bool need_to_delete_rtcp = (voice_channel_->rtcp_dtls_transport() != nullptr); | |
2410 channel_manager_->DestroyVoiceChannel(voice_channel_.release()); | |
2411 transport_controller_->DestroyDtlsTransport( | |
2412 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
2413 if (need_to_delete_rtcp) { | |
2414 transport_controller_->DestroyDtlsTransport( | |
2415 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
2416 } | |
2417 } | |
2418 | |
2419 void WebRtcSession::DestroyDataChannel() { | |
2420 SignalDataChannelDestroyed(); | |
2421 RTC_DCHECK(rtp_data_channel_->rtp_dtls_transport()); | |
2422 std::string transport_name; | |
2423 transport_name = rtp_data_channel_->rtp_dtls_transport()->transport_name(); | |
2424 bool need_to_delete_rtcp = | |
2425 (rtp_data_channel_->rtcp_dtls_transport() != nullptr); | |
2426 channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release()); | |
2427 transport_controller_->DestroyDtlsTransport( | |
2428 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
2429 if (need_to_delete_rtcp) { | |
2430 transport_controller_->DestroyDtlsTransport( | |
2431 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | |
2432 } | |
2433 } | |
2434 } // namespace webrtc | |
OLD | NEW |