1 /* 2 * libjingle 3 * Copyright 2012, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/app/webrtc/webrtcsession.h" 29 30 #include <algorithm> 31 #include <climits> 32 #include <vector> 33 34 #include "talk/app/webrtc/jsepicecandidate.h" 35 #include "talk/app/webrtc/jsepsessiondescription.h" 36 #include "talk/app/webrtc/mediaconstraintsinterface.h" 37 #include "talk/app/webrtc/mediastreamsignaling.h" 38 #include "talk/app/webrtc/peerconnectioninterface.h" 39 #include "talk/app/webrtc/webrtcsessiondescriptionfactory.h" 40 #include "talk/base/helpers.h" 41 #include "talk/base/logging.h" 42 #include "talk/base/stringencode.h" 43 #include "talk/media/base/constants.h" 44 #include "talk/media/base/videocapturer.h" 45 #include "talk/session/media/channel.h" 46 #include "talk/session/media/channelmanager.h" 47 #include "talk/session/media/mediasession.h" 48 49 using cricket::ContentInfo; 50 using cricket::ContentInfos; 51 using cricket::MediaContentDescription; 52 using cricket::SessionDescription; 53 using cricket::TransportInfo; 54 55 namespace webrtc { 56 57 const char kInternalConstraintPrefix[] = "internal"; 58 59 // Supported MediaConstraints. 60 // DTLS-SRTP pseudo-constraints. 61 const char MediaConstraintsInterface::kEnableDtlsSrtp[] = 62 "DtlsSrtpKeyAgreement"; 63 // DataChannel pseudo constraints. 64 const char MediaConstraintsInterface::kEnableRtpDataChannels[] = 65 "RtpDataChannels"; 66 // This constraint is for internal use only, representing the Chrome command 67 // line flag. So it is prefixed with kInternalConstraintPrefix so JS values 68 // will be removed. 69 const char MediaConstraintsInterface::kEnableSctpDataChannels[] = 70 "internalSctpDataChannels"; 71 72 // Error messages 73 const char kSetLocalSdpFailed[] = "SetLocalDescription failed: "; 74 const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: "; 75 const char kCreateChannelFailed[] = "Failed to create channels."; 76 const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE " 77 "is enabled."; 78 const char kInvalidCandidates[] = "Description contains invalid candidates."; 79 const char kInvalidSdp[] = "Invalid session description."; 80 const char kMlineMismatch[] = 81 "Offer and answer descriptions m-lines are not matching. " 82 "Rejecting answer."; 83 const char kSdpWithoutCrypto[] = "Called with a SDP without crypto enabled."; 84 const char kSessionError[] = "Session error code: "; 85 const char kUpdateStateFailed[] = "Failed to update session state: "; 86 const char kPushDownOfferTDFailed[] = 87 "Failed to push down offer transport description."; 88 const char kPushDownPranswerTDFailed[] = 89 "Failed to push down pranswer transport description."; 90 const char kPushDownAnswerTDFailed[] = 91 "Failed to push down answer transport description."; 92 93 // Compares |answer| against |offer|. Comparision is done 94 // for number of m-lines in answer against offer. If matches true will be 95 // returned otherwise false. 96 static bool VerifyMediaDescriptions( 97 const SessionDescription* answer, const SessionDescription* offer) { 98 if (offer->contents().size() != answer->contents().size()) 99 return false; 100 101 for (size_t i = 0; i < offer->contents().size(); ++i) { 102 if ((offer->contents()[i].name) != answer->contents()[i].name) { 103 return false; 104 } 105 } 106 return true; 107 } 108 109 // Checks that each non-rejected content has SDES crypto keys or a DTLS 110 // fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES 111 // keys, will be caught in Transport negotiation, and backstopped by Channel's 112 // |secure_required| check. 113 static bool VerifyCrypto(const SessionDescription* desc) { 114 if (!desc) { 115 return false; 116 } 117 const ContentInfos& contents = desc->contents(); 118 for (size_t index = 0; index < contents.size(); ++index) { 119 const ContentInfo* cinfo = &contents[index]; 120 if (cinfo->rejected) { 121 continue; 122 } 123 124 // If the content isn't rejected, crypto must be present. 125 const MediaContentDescription* media = 126 static_cast<const MediaContentDescription*>(cinfo->description); 127 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); 128 if (!media || !tinfo) { 129 // Something is not right. 130 LOG(LS_ERROR) << kInvalidSdp; 131 return false; 132 } 133 if (media->cryptos().empty() && 134 !tinfo->description.identity_fingerprint) { 135 // Crypto must be supplied. 136 LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP."; 137 return false; 138 } 139 } 140 141 return true; 142 } 143 144 // Forces |sdesc->crypto_required| to the appropriate state based on the 145 // current security policy, to ensure a failure occurs if there is an error 146 // in crypto negotiation. 147 // Called when processing the local session description. 148 static void UpdateSessionDescriptionSecurePolicy( 149 cricket::SecureMediaPolicy secure_policy, 150 SessionDescription* sdesc) { 151 if (!sdesc) { 152 return; 153 } 154 155 // Updating the |crypto_required_| in MediaContentDescription to the 156 // appropriate state based on the current security policy. 157 for (cricket::ContentInfos::iterator iter = sdesc->contents().begin(); 158 iter != sdesc->contents().end(); ++iter) { 159 if (cricket::IsMediaContent(&*iter)) { 160 MediaContentDescription* mdesc = 161 static_cast<MediaContentDescription*> (iter->description); 162 if (mdesc) { 163 mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED); 164 } 165 } 166 } 167 } 168 169 static bool GetAudioSsrcByTrackId( 170 const SessionDescription* session_description, 171 const std::string& track_id, uint32 *ssrc) { 172 const cricket::ContentInfo* audio_info = 173 cricket::GetFirstAudioContent(session_description); 174 if (!audio_info) { 175 LOG(LS_ERROR) << "Audio not used in this call"; 176 return false; 177 } 178 179 const cricket::MediaContentDescription* audio_content = 180 static_cast<const cricket::MediaContentDescription*>( 181 audio_info->description); 182 cricket::StreamParams stream; 183 if (!cricket::GetStreamByIds(audio_content->streams(), "", track_id, 184 &stream)) { 185 return false; 186 } 187 *ssrc = stream.first_ssrc(); 188 return true; 189 } 190 191 static bool GetTrackIdBySsrc(const SessionDescription* session_description, 192 uint32 ssrc, std::string* track_id) { 193 ASSERT(track_id != NULL); 194 195 cricket::StreamParams stream_out; 196 const cricket::ContentInfo* audio_info = 197 cricket::GetFirstAudioContent(session_description); 198 if (!audio_info) { 199 return false; 200 } 201 const cricket::MediaContentDescription* audio_content = 202 static_cast<const cricket::MediaContentDescription*>( 203 audio_info->description); 204 205 if (cricket::GetStreamBySsrc(audio_content->streams(), ssrc, &stream_out)) { 206 *track_id = stream_out.id; 207 return true; 208 } 209 210 const cricket::ContentInfo* video_info = 211 cricket::GetFirstVideoContent(session_description); 212 if (!video_info) { 213 return false; 214 } 215 const cricket::MediaContentDescription* video_content = 216 static_cast<const cricket::MediaContentDescription*>( 217 video_info->description); 218 219 if (cricket::GetStreamBySsrc(video_content->streams(), ssrc, &stream_out)) { 220 *track_id = stream_out.id; 221 return true; 222 } 223 return false; 224 } 225 226 static bool BadSdp(const std::string& desc, std::string* err_desc) { 227 if (err_desc) { 228 *err_desc = desc; 229 } 230 LOG(LS_ERROR) << desc; 231 return false; 232 } 233 234 static bool BadLocalSdp(const std::string& desc, std::string* err_desc) { 235 std::string set_local_sdp_failed = kSetLocalSdpFailed; 236 set_local_sdp_failed.append(desc); 237 return BadSdp(set_local_sdp_failed, err_desc); 238 } 239 240 static bool BadRemoteSdp(const std::string& desc, std::string* err_desc) { 241 std::string set_remote_sdp_failed = kSetRemoteSdpFailed; 242 set_remote_sdp_failed.append(desc); 243 return BadSdp(set_remote_sdp_failed, err_desc); 244 } 245 246 static bool BadSdp(cricket::ContentSource source, 247 const std::string& desc, std::string* err_desc) { 248 if (source == cricket::CS_LOCAL) { 249 return BadLocalSdp(desc, err_desc); 250 } else { 251 return BadRemoteSdp(desc, err_desc); 252 } 253 } 254 255 static std::string SessionErrorMsg(cricket::BaseSession::Error error) { 256 std::ostringstream desc; 257 desc << kSessionError << error; 258 return desc.str(); 259 } 260 261 #define GET_STRING_OF_STATE(state) \ 262 case cricket::BaseSession::state: \ 263 result = #state; \ 264 break; 265 266 static std::string GetStateString(cricket::BaseSession::State state) { 267 std::string result; 268 switch (state) { 269 GET_STRING_OF_STATE(STATE_INIT) 270 GET_STRING_OF_STATE(STATE_SENTINITIATE) 271 GET_STRING_OF_STATE(STATE_RECEIVEDINITIATE) 272 GET_STRING_OF_STATE(STATE_SENTPRACCEPT) 273 GET_STRING_OF_STATE(STATE_SENTACCEPT) 274 GET_STRING_OF_STATE(STATE_RECEIVEDPRACCEPT) 275 GET_STRING_OF_STATE(STATE_RECEIVEDACCEPT) 276 GET_STRING_OF_STATE(STATE_SENTMODIFY) 277 GET_STRING_OF_STATE(STATE_RECEIVEDMODIFY) 278 GET_STRING_OF_STATE(STATE_SENTREJECT) 279 GET_STRING_OF_STATE(STATE_RECEIVEDREJECT) 280 GET_STRING_OF_STATE(STATE_SENTREDIRECT) 281 GET_STRING_OF_STATE(STATE_SENTTERMINATE) 282 GET_STRING_OF_STATE(STATE_RECEIVEDTERMINATE) 283 GET_STRING_OF_STATE(STATE_INPROGRESS) 284 GET_STRING_OF_STATE(STATE_DEINIT) 285 default: 286 ASSERT(false); 287 break; 288 } 289 return result; 290 } 291 292 #define GET_STRING_OF_ERROR(err) \ 293 case cricket::BaseSession::err: \ 294 result = #err; \ 295 break; 296 297 static std::string GetErrorString(cricket::BaseSession::Error err) { 298 std::string result; 299 switch (err) { 300 GET_STRING_OF_ERROR(ERROR_NONE) 301 GET_STRING_OF_ERROR(ERROR_TIME) 302 GET_STRING_OF_ERROR(ERROR_RESPONSE) 303 GET_STRING_OF_ERROR(ERROR_NETWORK) 304 GET_STRING_OF_ERROR(ERROR_CONTENT) 305 GET_STRING_OF_ERROR(ERROR_TRANSPORT) 306 default: 307 ASSERT(false); 308 break; 309 } 310 return result; 311 } 312 313 static bool SetSessionStateFailed(cricket::ContentSource source, 314 cricket::BaseSession::Error err, 315 std::string* err_desc) { 316 std::string set_state_err = kUpdateStateFailed; 317 set_state_err.append(GetErrorString(err)); 318 return BadSdp(source, set_state_err, err_desc); 319 } 320 321 // Help class used to remember if a a remote peer has requested ice restart by 322 // by sending a description with new ice ufrag and password. 323 class IceRestartAnswerLatch { 324 public: 325 IceRestartAnswerLatch() : ice_restart_(false) { } 326 327 // Returns true if CheckForRemoteIceRestart has been called with a new session 328 // description where ice password and ufrag has changed since last time 329 // Reset() was called. 330 bool Get() const { 331 return ice_restart_; 332 } 333 334 void Reset() { 335 if (ice_restart_) { 336 ice_restart_ = false; 337 } 338 } 339 340 void CheckForRemoteIceRestart( 341 const SessionDescriptionInterface* old_desc, 342 const SessionDescriptionInterface* new_desc) { 343 if (!old_desc || new_desc->type() != SessionDescriptionInterface::kOffer) { 344 return; 345 } 346 const SessionDescription* new_sd = new_desc->description(); 347 const SessionDescription* old_sd = old_desc->description(); 348 const ContentInfos& contents = new_sd->contents(); 349 for (size_t index = 0; index < contents.size(); ++index) { 350 const ContentInfo* cinfo = &contents[index]; 351 if (cinfo->rejected) { 352 continue; 353 } 354 // If the content isn't rejected, check if ufrag and password has 355 // changed. 356 const cricket::TransportDescription* new_transport_desc = 357 new_sd->GetTransportDescriptionByName(cinfo->name); 358 const cricket::TransportDescription* old_transport_desc = 359 old_sd->GetTransportDescriptionByName(cinfo->name); 360 if (!new_transport_desc || !old_transport_desc) { 361 // No transport description exist. This is not an ice restart. 362 continue; 363 } 364 if (new_transport_desc->ice_pwd != old_transport_desc->ice_pwd && 365 new_transport_desc->ice_ufrag != old_transport_desc->ice_ufrag) { 366 LOG(LS_INFO) << "Remote peer request ice restart."; 367 ice_restart_ = true; 368 break; 369 } 370 } 371 } 372 373 private: 374 bool ice_restart_; 375 }; 376 377 WebRtcSession::WebRtcSession( 378 cricket::ChannelManager* channel_manager, 379 talk_base::Thread* signaling_thread, 380 talk_base::Thread* worker_thread, 381 cricket::PortAllocator* port_allocator, 382 MediaStreamSignaling* mediastream_signaling) 383 : cricket::BaseSession(signaling_thread, worker_thread, port_allocator, 384 talk_base::ToString(talk_base::CreateRandomId64() & 385 LLONG_MAX), 386 cricket::NS_JINGLE_RTP, false), 387 // RFC 3264: The numeric value of the session id and version in the 388 // o line MUST be representable with a "64 bit signed integer". 389 // Due to this constraint session id |sid_| is max limited to LLONG_MAX. 390 channel_manager_(channel_manager), 391 mediastream_signaling_(mediastream_signaling), 392 ice_observer_(NULL), 393 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew), 394 older_version_remote_peer_(false), 395 data_channel_type_(cricket::DCT_NONE), 396 ice_restart_latch_(new IceRestartAnswerLatch) { 397 } 398 399 WebRtcSession::~WebRtcSession() { 400 if (voice_channel_.get()) { 401 SignalVoiceChannelDestroyed(); 402 channel_manager_->DestroyVoiceChannel(voice_channel_.release()); 403 } 404 if (video_channel_.get()) { 405 SignalVideoChannelDestroyed(); 406 channel_manager_->DestroyVideoChannel(video_channel_.release()); 407 } 408 if (data_channel_.get()) { 409 SignalDataChannelDestroyed(); 410 channel_manager_->DestroyDataChannel(data_channel_.release()); 411 } 412 for (size_t i = 0; i < saved_candidates_.size(); ++i) { 413 delete saved_candidates_[i]; 414 } 415 delete identity(); 416 } 417 418 bool WebRtcSession::Initialize( 419 const MediaConstraintsInterface* constraints, 420 DTLSIdentityServiceInterface* dtls_identity_service) { 421 // TODO(perkj): Take |constraints| into consideration. Return false if not all 422 // mandatory constraints can be fulfilled. Note that |constraints| 423 // can be null. 424 bool value; 425 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. 426 // It takes precendence over the kEnableSctpDataChannels constraint. 427 if (FindConstraint( 428 constraints, MediaConstraintsInterface::kEnableRtpDataChannels, 429 &value, NULL) && value) { 430 LOG(LS_INFO) << "Allowing RTP data engine."; 431 data_channel_type_ = cricket::DCT_RTP; 432 } else { 433 bool sctp_enabled = FindConstraint( 434 constraints, 435 MediaConstraintsInterface::kEnableSctpDataChannels, 436 &value, NULL) && value; 437 bool dtls_enabled = FindConstraint( 438 constraints, 439 MediaConstraintsInterface::kEnableDtlsSrtp, 440 &value, NULL) && value; 441 442 // DTLS has to be enabled to use SCTP. 443 if (sctp_enabled && dtls_enabled) { 444 LOG(LS_INFO) << "Allowing SCTP data engine."; 445 data_channel_type_ = cricket::DCT_SCTP; 446 } 447 } 448 if (data_channel_type_ != cricket::DCT_NONE) { 449 mediastream_signaling_->SetDataChannelFactory(this); 450 } 451 452 const cricket::VideoCodec default_codec( 453 JsepSessionDescription::kDefaultVideoCodecId, 454 JsepSessionDescription::kDefaultVideoCodecName, 455 JsepSessionDescription::kMaxVideoCodecWidth, 456 JsepSessionDescription::kMaxVideoCodecHeight, 457 JsepSessionDescription::kDefaultVideoCodecFramerate, 458 JsepSessionDescription::kDefaultVideoCodecPreference); 459 channel_manager_->SetDefaultVideoEncoderConfig( 460 cricket::VideoEncoderConfig(default_codec)); 461 462 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( 463 signaling_thread(), 464 channel_manager_, 465 mediastream_signaling_, 466 dtls_identity_service, 467 this, 468 id(), 469 data_channel_type_, 470 constraints)); 471 472 webrtc_session_desc_factory_->SignalIdentityReady.connect( 473 this, &WebRtcSession::OnIdentityReady); 474 return true; 475 } 476 477 void WebRtcSession::Terminate() { 478 SetState(STATE_RECEIVEDTERMINATE); 479 RemoveUnusedChannelsAndTransports(NULL); 480 ASSERT(voice_channel_.get() == NULL); 481 ASSERT(video_channel_.get() == NULL); 482 ASSERT(data_channel_.get() == NULL); 483 } 484 485 bool WebRtcSession::StartCandidatesAllocation() { 486 // SpeculativelyConnectTransportChannels, will call ConnectChannels method 487 // from TransportProxy to start gathering ice candidates. 488 SpeculativelyConnectAllTransportChannels(); 489 if (!saved_candidates_.empty()) { 490 // If there are saved candidates which arrived before local description is 491 // set, copy those to remote description. 492 CopySavedCandidates(remote_desc_.get()); 493 } 494 // Push remote candidates present in remote description to transport channels. 495 UseCandidatesInSessionDescription(remote_desc_.get()); 496 return true; 497 } 498 499 void WebRtcSession::set_secure_policy( 500 cricket::SecureMediaPolicy secure_policy) { 501 webrtc_session_desc_factory_->set_secure(secure_policy); 502 } 503 504 cricket::SecureMediaPolicy WebRtcSession::secure_policy() const { 505 return webrtc_session_desc_factory_->secure(); 506 } 507 508 void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer, 509 const MediaConstraintsInterface* constraints) { 510 webrtc_session_desc_factory_->CreateOffer(observer, constraints); 511 } 512 513 void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer, 514 const MediaConstraintsInterface* constraints) { 515 webrtc_session_desc_factory_->CreateAnswer(observer, constraints); 516 } 517 518 bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, 519 std::string* err_desc) { 520 cricket::SecureMediaPolicy secure_policy = 521 webrtc_session_desc_factory_->secure(); 522 // Takes the ownership of |desc| regardless of the result. 523 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc); 524 525 if (error() != cricket::BaseSession::ERROR_NONE) { 526 return BadLocalSdp(SessionErrorMsg(error()), err_desc); 527 } 528 529 if (!desc || !desc->description()) { 530 return BadLocalSdp(kInvalidSdp, err_desc); 531 } 532 533 if (!VerifyBundleSettings(desc->description())) { 534 return BadLocalSdp(kBundleWithoutRtcpMux, err_desc); 535 } 536 537 Action action = GetAction(desc->type()); 538 if (!ExpectSetLocalDescription(action)) { 539 std::string type = desc->type(); 540 return BadLocalSdp(BadStateErrMsg(type, state()), err_desc); 541 } 542 if (secure_policy == cricket::SEC_REQUIRED && 543 !VerifyCrypto(desc->description())) { 544 return BadLocalSdp(kSdpWithoutCrypto, err_desc); 545 } 546 if (action == kAnswer && !VerifyMediaDescriptions( 547 desc->description(), remote_description()->description())) { 548 return BadLocalSdp(kMlineMismatch, err_desc); 549 } 550 551 // Update the initiator flag if this session is the initiator. 552 if (state() == STATE_INIT && action == kOffer) { 553 set_initiator(true); 554 } 555 556 // Update the MediaContentDescription crypto settings as per the policy set. 557 UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description()); 558 559 set_local_description(desc->description()->Copy()); 560 local_desc_.reset(desc_temp.release()); 561 562 // Transport and Media channels will be created only when offer is set. 563 if (action == kOffer && !CreateChannels(local_desc_->description())) { 564 // TODO(mallinath) - Handle CreateChannel failure, as new local description 565 // is applied. Restore back to old description. 566 return BadLocalSdp(kCreateChannelFailed, err_desc); 567 } 568 569 // Remove channel and transport proxies, if MediaContentDescription is 570 // rejected. 571 RemoveUnusedChannelsAndTransports(local_desc_->description()); 572 573 if (!UpdateSessionState(action, cricket::CS_LOCAL, 574 local_desc_->description(), err_desc)) { 575 return false; 576 } 577 // Kick starting the ice candidates allocation. 578 StartCandidatesAllocation(); 579 580 // Update state and SSRC of local MediaStreams and DataChannels based on the 581 // local session description. 582 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get()); 583 584 if (error() != cricket::BaseSession::ERROR_NONE) { 585 return BadLocalSdp(SessionErrorMsg(error()), err_desc); 586 } 587 return true; 588 } 589 590 bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, 591 std::string* err_desc) { 592 cricket::SecureMediaPolicy secure_policy = 593 webrtc_session_desc_factory_->secure(); 594 // Takes the ownership of |desc| regardless of the result. 595 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc); 596 597 if (error() != cricket::BaseSession::ERROR_NONE) { 598 return BadRemoteSdp(SessionErrorMsg(error()), err_desc); 599 } 600 601 if (!desc || !desc->description()) { 602 return BadRemoteSdp(kInvalidSdp, err_desc); 603 } 604 605 if (!VerifyBundleSettings(desc->description())) { 606 return BadRemoteSdp(kBundleWithoutRtcpMux, err_desc); 607 } 608 609 Action action = GetAction(desc->type()); 610 if (!ExpectSetRemoteDescription(action)) { 611 std::string type = desc->type(); 612 return BadRemoteSdp(BadStateErrMsg(type, state()), err_desc); 613 } 614 615 if (action == kAnswer && !VerifyMediaDescriptions( 616 desc->description(), local_description()->description())) { 617 return BadRemoteSdp(kMlineMismatch, err_desc); 618 } 619 620 if (secure_policy == cricket::SEC_REQUIRED && 621 !VerifyCrypto(desc->description())) { 622 return BadRemoteSdp(kSdpWithoutCrypto, err_desc); 623 } 624 625 // Transport and Media channels will be created only when offer is set. 626 if (action == kOffer && !CreateChannels(desc->description())) { 627 // TODO(mallinath) - Handle CreateChannel failure, as new local description 628 // is applied. Restore back to old description. 629 return BadRemoteSdp(kCreateChannelFailed, err_desc); 630 } 631 632 // Remove channel and transport proxies, if MediaContentDescription is 633 // rejected. 634 RemoveUnusedChannelsAndTransports(desc->description()); 635 636 // NOTE: Candidates allocation will be initiated only when SetLocalDescription 637 // is called. 638 set_remote_description(desc->description()->Copy()); 639 if (!UpdateSessionState(action, cricket::CS_REMOTE, 640 desc->description(), err_desc)) { 641 return false; 642 } 643 644 // Update remote MediaStreams. 645 mediastream_signaling_->OnRemoteDescriptionChanged(desc); 646 if (local_description() && !UseCandidatesInSessionDescription(desc)) { 647 return BadRemoteSdp(kInvalidCandidates, err_desc); 648 } 649 650 // Copy all saved candidates. 651 CopySavedCandidates(desc); 652 // We retain all received candidates. 653 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( 654 remote_desc_.get(), desc); 655 // Check if this new SessionDescription contains new ice ufrag and password 656 // that indicates the remote peer requests ice restart. 657 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(), 658 desc); 659 remote_desc_.reset(desc_temp.release()); 660 if (error() != cricket::BaseSession::ERROR_NONE) { 661 return BadRemoteSdp(SessionErrorMsg(error()), err_desc); 662 } 663 return true; 664 } 665 666 bool WebRtcSession::UpdateSessionState( 667 Action action, cricket::ContentSource source, 668 const cricket::SessionDescription* desc, 669 std::string* err_desc) { 670 // If there's already a pending error then no state transition should happen. 671 // But all call-sites should be verifying this before calling us! 672 ASSERT(error() == cricket::BaseSession::ERROR_NONE); 673 if (action == kOffer) { 674 if (!PushdownTransportDescription(source, cricket::CA_OFFER)) { 675 return BadSdp(source, kPushDownOfferTDFailed, err_desc); 676 } 677 SetState(source == cricket::CS_LOCAL ? 678 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE); 679 if (error() != cricket::BaseSession::ERROR_NONE) { 680 return SetSessionStateFailed(source, error(), err_desc); 681 } 682 } else if (action == kPrAnswer) { 683 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER)) { 684 return BadSdp(source, kPushDownPranswerTDFailed, err_desc); 685 } 686 EnableChannels(); 687 SetState(source == cricket::CS_LOCAL ? 688 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT); 689 if (error() != cricket::BaseSession::ERROR_NONE) { 690 return SetSessionStateFailed(source, error(), err_desc); 691 } 692 } else if (action == kAnswer) { 693 if (!PushdownTransportDescription(source, cricket::CA_ANSWER)) { 694 return BadSdp(source, kPushDownAnswerTDFailed, err_desc); 695 } 696 MaybeEnableMuxingSupport(); 697 EnableChannels(); 698 SetState(source == cricket::CS_LOCAL ? 699 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT); 700 if (error() != cricket::BaseSession::ERROR_NONE) { 701 return SetSessionStateFailed(source, error(), err_desc); 702 } 703 } 704 return true; 705 } 706 707 WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) { 708 if (type == SessionDescriptionInterface::kOffer) { 709 return WebRtcSession::kOffer; 710 } else if (type == SessionDescriptionInterface::kPrAnswer) { 711 return WebRtcSession::kPrAnswer; 712 } else if (type == SessionDescriptionInterface::kAnswer) { 713 return WebRtcSession::kAnswer; 714 } 715 ASSERT(false && "unknown action type"); 716 return WebRtcSession::kOffer; 717 } 718 719 bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) { 720 if (state() == STATE_INIT) { 721 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added " 722 << "without any offer (local or remote) " 723 << "session description."; 724 return false; 725 } 726 727 if (!candidate) { 728 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL"; 729 return false; 730 } 731 732 if (!local_description() || !remote_description()) { 733 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, " 734 << "save the candidate for later use."; 735 saved_candidates_.push_back( 736 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), 737 candidate->candidate())); 738 return true; 739 } 740 741 // Add this candidate to the remote session description. 742 if (!remote_desc_->AddCandidate(candidate)) { 743 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used"; 744 return false; 745 } 746 747 return UseCandidatesInSessionDescription(remote_desc_.get()); 748 } 749 750 bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) { 751 if (GetLocalTrackId(ssrc, id)) { 752 if (GetRemoteTrackId(ssrc, id)) { 753 LOG(LS_WARNING) << "SSRC " << ssrc 754 << " exists in both local and remote descriptions"; 755 return true; // We return the remote track id. 756 } 757 return true; 758 } else { 759 return GetRemoteTrackId(ssrc, id); 760 } 761 } 762 763 bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) { 764 if (!BaseSession::local_description()) 765 return false; 766 return webrtc::GetTrackIdBySsrc( 767 BaseSession::local_description(), ssrc, track_id); 768 } 769 770 bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) { 771 if (!BaseSession::remote_description()) 772 return false; 773 return webrtc::GetTrackIdBySsrc( 774 BaseSession::remote_description(), ssrc, track_id); 775 } 776 777 std::string WebRtcSession::BadStateErrMsg( 778 const std::string& type, State state) { 779 std::ostringstream desc; 780 desc << "Called with type in wrong state, " 781 << "type: " << type << " state: " << GetStateString(state); 782 return desc.str(); 783 } 784 785 void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable, 786 cricket::AudioRenderer* renderer) { 787 ASSERT(signaling_thread()->IsCurrent()); 788 if (!voice_channel_) { 789 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists."; 790 return; 791 } 792 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) { 793 // SetRenderer() can fail if the ssrc does not match any playout channel. 794 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc; 795 return; 796 } 797 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) { 798 // Allow that SetOutputScaling fail if |enable| is false but assert 799 // otherwise. This in the normal case when the underlying media channel has 800 // already been deleted. 801 ASSERT(enable == false); 802 } 803 } 804 805 void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable, 806 const cricket::AudioOptions& options, 807 cricket::AudioRenderer* renderer) { 808 ASSERT(signaling_thread()->IsCurrent()); 809 if (!voice_channel_) { 810 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists."; 811 return; 812 } 813 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) { 814 // SetRenderer() can fail if the ssrc does not match any send channel. 815 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc; 816 return; 817 } 818 if (!voice_channel_->MuteStream(ssrc, !enable)) { 819 // Allow that MuteStream fail if |enable| is false but assert otherwise. 820 // This in the normal case when the underlying media channel has already 821 // been deleted. 822 ASSERT(enable == false); 823 return; 824 } 825 if (enable) 826 voice_channel_->SetChannelOptions(options); 827 } 828 829 bool WebRtcSession::SetCaptureDevice(uint32 ssrc, 830 cricket::VideoCapturer* camera) { 831 ASSERT(signaling_thread()->IsCurrent()); 832 833 if (!video_channel_.get()) { 834 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't 835 // support video. 836 LOG(LS_WARNING) << "Video not used in this call."; 837 return false; 838 } 839 if (!video_channel_->SetCapturer(ssrc, camera)) { 840 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise. 841 // This in the normal case when the underlying media channel has already 842 // been deleted. 843 ASSERT(camera == NULL); 844 return false; 845 } 846 return true; 847 } 848 849 void WebRtcSession::SetVideoPlayout(uint32 ssrc, 850 bool enable, 851 cricket::VideoRenderer* renderer) { 852 ASSERT(signaling_thread()->IsCurrent()); 853 if (!video_channel_) { 854 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists."; 855 return; 856 } 857 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) { 858 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise. 859 // This in the normal case when the underlying media channel has already 860 // been deleted. 861 ASSERT(renderer == NULL); 862 } 863 } 864 865 void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable, 866 const cricket::VideoOptions* options) { 867 ASSERT(signaling_thread()->IsCurrent()); 868 if (!video_channel_) { 869 LOG(LS_WARNING) << "SetVideoSend: No video channel exists."; 870 return; 871 } 872 if (!video_channel_->MuteStream(ssrc, !enable)) { 873 // Allow that MuteStream fail if |enable| is false but assert otherwise. 874 // This in the normal case when the underlying media channel has already 875 // been deleted. 876 ASSERT(enable == false); 877 return; 878 } 879 if (enable && options) 880 video_channel_->SetChannelOptions(*options); 881 } 882 883 bool WebRtcSession::CanInsertDtmf(const std::string& track_id) { 884 ASSERT(signaling_thread()->IsCurrent()); 885 if (!voice_channel_) { 886 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists."; 887 return false; 888 } 889 uint32 send_ssrc = 0; 890 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc 891 // exists. 892 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id, 893 &send_ssrc)) { 894 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id; 895 return false; 896 } 897 return voice_channel_->CanInsertDtmf(); 898 } 899 900 bool WebRtcSession::InsertDtmf(const std::string& track_id, 901 int code, int duration) { 902 ASSERT(signaling_thread()->IsCurrent()); 903 if (!voice_channel_) { 904 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists."; 905 return false; 906 } 907 uint32 send_ssrc = 0; 908 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(), 909 track_id, &send_ssrc))) { 910 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id; 911 return false; 912 } 913 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration, 914 cricket::DF_SEND)) { 915 LOG(LS_ERROR) << "Failed to insert DTMF to channel."; 916 return false; 917 } 918 return true; 919 } 920 921 sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() { 922 return &SignalVoiceChannelDestroyed; 923 } 924 925 talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel( 926 const std::string& label, 927 const DataChannelInit* config) { 928 if (state() == STATE_RECEIVEDTERMINATE) { 929 return NULL; 930 } 931 if (data_channel_type_ == cricket::DCT_NONE) { 932 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call."; 933 return NULL; 934 } 935 DataChannelInit new_config = config ? (*config) : DataChannelInit(); 936 937 if (data_channel_type_ == cricket::DCT_SCTP) { 938 if (new_config.id < 0) { 939 if (!mediastream_signaling_->AllocateSctpId(&new_config.id)) { 940 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel."; 941 return NULL; 942 } 943 } else if (!mediastream_signaling_->IsSctpIdAvailable(new_config.id)) { 944 LOG(LS_ERROR) << "Failed to create a SCTP data channel " 945 << "because the id is already in use or out of range."; 946 return NULL; 947 } 948 } 949 950 talk_base::scoped_refptr<DataChannel> channel( 951 DataChannel::Create(this, label, &new_config)); 952 if (channel == NULL) 953 return NULL; 954 if (!mediastream_signaling_->AddDataChannel(channel)) 955 return NULL; 956 if (data_channel_type_ == cricket::DCT_SCTP) { 957 if (config == NULL) { 958 LOG(LS_WARNING) << "Could not send data channel OPEN message" 959 << " because of NULL config."; 960 return NULL; 961 } 962 if (data_channel_.get()) { 963 channel->SetReceiveSsrc(new_config.id); 964 channel->SetSendSsrc(new_config.id); 965 channel->ConnectToDataSession(); 966 } 967 if (!config->negotiated) { 968 talk_base::Buffer *payload = new talk_base::Buffer; 969 if (!mediastream_signaling_->WriteDataChannelOpenMessage( 970 label, *config, payload)) { 971 LOG(LS_WARNING) << "Could not write data channel OPEN message"; 972 } 973 // SendControl may queue the message until the data channel's set up, 974 // or congestion clears. 975 channel->SendControl(payload); 976 } 977 } 978 return channel; 979 } 980 981 cricket::DataChannelType WebRtcSession::data_channel_type() const { 982 return data_channel_type_; 983 } 984 985 bool WebRtcSession::IceRestartPending() const { 986 return ice_restart_latch_->Get(); 987 } 988 989 void WebRtcSession::ResetIceRestartLatch() { 990 ice_restart_latch_->Reset(); 991 } 992 993 void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) { 994 SetIdentity(identity); 995 } 996 997 bool WebRtcSession::waiting_for_identity() const { 998 return webrtc_session_desc_factory_->waiting_for_identity(); 999 } 1000 1001 void WebRtcSession::SetIceConnectionState( 1002 PeerConnectionInterface::IceConnectionState state) { 1003 if (ice_connection_state_ == state) { 1004 return; 1005 } 1006 1007 // ASSERT that the requested transition is allowed. Note that 1008 // WebRtcSession does not implement "kIceConnectionClosed" (that is handled 1009 // within PeerConnection). This switch statement should compile away when 1010 // ASSERTs are disabled. 1011 switch (ice_connection_state_) { 1012 case PeerConnectionInterface::kIceConnectionNew: 1013 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking); 1014 break; 1015 case PeerConnectionInterface::kIceConnectionChecking: 1016 ASSERT(state == PeerConnectionInterface::kIceConnectionFailed || 1017 state == PeerConnectionInterface::kIceConnectionConnected); 1018 break; 1019 case PeerConnectionInterface::kIceConnectionConnected: 1020 ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected || 1021 state == PeerConnectionInterface::kIceConnectionChecking || 1022 state == PeerConnectionInterface::kIceConnectionCompleted); 1023 break; 1024 case PeerConnectionInterface::kIceConnectionCompleted: 1025 ASSERT(state == PeerConnectionInterface::kIceConnectionConnected || 1026 state == PeerConnectionInterface::kIceConnectionDisconnected); 1027 break; 1028 case PeerConnectionInterface::kIceConnectionFailed: 1029 ASSERT(state == PeerConnectionInterface::kIceConnectionNew); 1030 break; 1031 case PeerConnectionInterface::kIceConnectionDisconnected: 1032 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking || 1033 state == PeerConnectionInterface::kIceConnectionConnected || 1034 state == PeerConnectionInterface::kIceConnectionCompleted || 1035 state == PeerConnectionInterface::kIceConnectionFailed); 1036 break; 1037 case PeerConnectionInterface::kIceConnectionClosed: 1038 ASSERT(false); 1039 break; 1040 default: 1041 ASSERT(false); 1042 break; 1043 } 1044 1045 ice_connection_state_ = state; 1046 if (ice_observer_) { 1047 ice_observer_->OnIceConnectionChange(ice_connection_state_); 1048 } 1049 } 1050 1051 void WebRtcSession::OnTransportRequestSignaling( 1052 cricket::Transport* transport) { 1053 ASSERT(signaling_thread()->IsCurrent()); 1054 transport->OnSignalingReady(); 1055 if (ice_observer_) { 1056 ice_observer_->OnIceGatheringChange( 1057 PeerConnectionInterface::kIceGatheringGathering); 1058 } 1059 } 1060 1061 void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) { 1062 ASSERT(signaling_thread()->IsCurrent()); 1063 // start monitoring for the write state of the transport. 1064 OnTransportWritable(transport); 1065 } 1066 1067 void WebRtcSession::OnTransportWritable(cricket::Transport* transport) { 1068 ASSERT(signaling_thread()->IsCurrent()); 1069 // TODO(bemasc): Expose more API from Transport to detect when 1070 // candidate selection starts or stops, due to success or failure. 1071 if (transport->all_channels_writable()) { 1072 if (ice_connection_state_ == 1073 PeerConnectionInterface::kIceConnectionChecking || 1074 ice_connection_state_ == 1075 PeerConnectionInterface::kIceConnectionDisconnected) { 1076 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); 1077 } 1078 } else if (transport->HasChannels()) { 1079 // If the current state is Connected or Completed, then there were writable 1080 // channels but now there are not, so the next state must be Disconnected. 1081 if (ice_connection_state_ == 1082 PeerConnectionInterface::kIceConnectionConnected || 1083 ice_connection_state_ == 1084 PeerConnectionInterface::kIceConnectionCompleted) { 1085 SetIceConnectionState( 1086 PeerConnectionInterface::kIceConnectionDisconnected); 1087 } 1088 } 1089 } 1090 1091 void WebRtcSession::OnTransportProxyCandidatesReady( 1092 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) { 1093 ASSERT(signaling_thread()->IsCurrent()); 1094 ProcessNewLocalCandidate(proxy->content_name(), candidates); 1095 } 1096 1097 bool WebRtcSession::ExpectSetLocalDescription(Action action) { 1098 return ((action == kOffer && state() == STATE_INIT) || 1099 // update local offer 1100 (action == kOffer && state() == STATE_SENTINITIATE) || 1101 // update the current ongoing session. 1102 (action == kOffer && state() == STATE_RECEIVEDACCEPT) || 1103 (action == kOffer && state() == STATE_SENTACCEPT) || 1104 (action == kOffer && state() == STATE_INPROGRESS) || 1105 // accept remote offer 1106 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) || 1107 (action == kAnswer && state() == STATE_SENTPRACCEPT) || 1108 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) || 1109 (action == kPrAnswer && state() == STATE_SENTPRACCEPT)); 1110 } 1111 1112 bool WebRtcSession::ExpectSetRemoteDescription(Action action) { 1113 return ((action == kOffer && state() == STATE_INIT) || 1114 // update remote offer 1115 (action == kOffer && state() == STATE_RECEIVEDINITIATE) || 1116 // update the current ongoing session 1117 (action == kOffer && state() == STATE_RECEIVEDACCEPT) || 1118 (action == kOffer && state() == STATE_SENTACCEPT) || 1119 (action == kOffer && state() == STATE_INPROGRESS) || 1120 // accept local offer 1121 (action == kAnswer && state() == STATE_SENTINITIATE) || 1122 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) || 1123 (action == kPrAnswer && state() == STATE_SENTINITIATE) || 1124 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT)); 1125 } 1126 1127 void WebRtcSession::OnCandidatesAllocationDone() { 1128 ASSERT(signaling_thread()->IsCurrent()); 1129 if (ice_observer_) { 1130 ice_observer_->OnIceGatheringChange( 1131 PeerConnectionInterface::kIceGatheringComplete); 1132 ice_observer_->OnIceComplete(); 1133 } 1134 } 1135 1136 // Enabling voice and video channel. 1137 void WebRtcSession::EnableChannels() { 1138 if (voice_channel_ && !voice_channel_->enabled()) 1139 voice_channel_->Enable(true); 1140 1141 if (video_channel_ && !video_channel_->enabled()) 1142 video_channel_->Enable(true); 1143 1144 if (data_channel_.get() && !data_channel_->enabled()) 1145 data_channel_->Enable(true); 1146 } 1147 1148 void WebRtcSession::ProcessNewLocalCandidate( 1149 const std::string& content_name, 1150 const cricket::Candidates& candidates) { 1151 int sdp_mline_index; 1152 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) { 1153 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name " 1154 << content_name << " not found"; 1155 return; 1156 } 1157 1158 for (cricket::Candidates::const_iterator citer = candidates.begin(); 1159 citer != candidates.end(); ++citer) { 1160 // Use content_name as the candidate media id. 1161 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer); 1162 if (ice_observer_) { 1163 ice_observer_->OnIceCandidate(&candidate); 1164 } 1165 if (local_desc_) { 1166 local_desc_->AddCandidate(&candidate); 1167 } 1168 } 1169 } 1170 1171 // Returns the media index for a local ice candidate given the content name. 1172 bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name, 1173 int* sdp_mline_index) { 1174 if (!BaseSession::local_description() || !sdp_mline_index) 1175 return false; 1176 1177 bool content_found = false; 1178 const ContentInfos& contents = BaseSession::local_description()->contents(); 1179 for (size_t index = 0; index < contents.size(); ++index) { 1180 if (contents[index].name == content_name) { 1181 *sdp_mline_index = static_cast<int>(index); 1182 content_found = true; 1183 break; 1184 } 1185 } 1186 return content_found; 1187 } 1188 1189 bool WebRtcSession::UseCandidatesInSessionDescription( 1190 const SessionDescriptionInterface* remote_desc) { 1191 if (!remote_desc) 1192 return true; 1193 bool ret = true; 1194 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) { 1195 const IceCandidateCollection* candidates = remote_desc->candidates(m); 1196 for (size_t n = 0; n < candidates->count(); ++n) { 1197 ret = UseCandidate(candidates->at(n)); 1198 if (!ret) 1199 break; 1200 } 1201 } 1202 return ret; 1203 } 1204 1205 bool WebRtcSession::UseCandidate( 1206 const IceCandidateInterface* candidate) { 1207 1208 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index()); 1209 size_t remote_content_size = 1210 BaseSession::remote_description()->contents().size(); 1211 if (mediacontent_index >= remote_content_size) { 1212 LOG(LS_ERROR) 1213 << "UseRemoteCandidateInSession: Invalid candidate media index."; 1214 return false; 1215 } 1216 1217 cricket::ContentInfo content = 1218 BaseSession::remote_description()->contents()[mediacontent_index]; 1219 std::vector<cricket::Candidate> candidates; 1220 candidates.push_back(candidate->candidate()); 1221 // Invoking BaseSession method to handle remote candidates. 1222 std::string error; 1223 if (OnRemoteCandidates(content.name, candidates, &error)) { 1224 // Candidates successfully submitted for checking. 1225 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew || 1226 ice_connection_state_ == 1227 PeerConnectionInterface::kIceConnectionDisconnected) { 1228 // If state is New, then the session has just gotten its first remote ICE 1229 // candidates, so go to Checking. 1230 // If state is Disconnected, the session is re-using old candidates or 1231 // receiving additional ones, so go to Checking. 1232 // If state is Connected, stay Connected. 1233 // TODO(bemasc): If state is Connected, and the new candidates are for a 1234 // newly added transport, then the state actually _should_ move to 1235 // checking. Add a way to distinguish that case. 1236 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); 1237 } 1238 // TODO(bemasc): If state is Completed, go back to Connected. 1239 } else { 1240 LOG(LS_WARNING) << error; 1241 } 1242 return true; 1243 } 1244 1245 void WebRtcSession::RemoveUnusedChannelsAndTransports( 1246 const SessionDescription* desc) { 1247 const cricket::ContentInfo* voice_info = 1248 cricket::GetFirstAudioContent(desc); 1249 if ((!voice_info || voice_info->rejected) && voice_channel_) { 1250 mediastream_signaling_->OnAudioChannelClose(); 1251 SignalVoiceChannelDestroyed(); 1252 const std::string content_name = voice_channel_->content_name(); 1253 channel_manager_->DestroyVoiceChannel(voice_channel_.release()); 1254 DestroyTransportProxy(content_name); 1255 } 1256 1257 const cricket::ContentInfo* video_info = 1258 cricket::GetFirstVideoContent(desc); 1259 if ((!video_info || video_info->rejected) && video_channel_) { 1260 mediastream_signaling_->OnVideoChannelClose(); 1261 SignalVideoChannelDestroyed(); 1262 const std::string content_name = video_channel_->content_name(); 1263 channel_manager_->DestroyVideoChannel(video_channel_.release()); 1264 DestroyTransportProxy(content_name); 1265 } 1266 1267 const cricket::ContentInfo* data_info = 1268 cricket::GetFirstDataContent(desc); 1269 if ((!data_info || data_info->rejected) && data_channel_) { 1270 mediastream_signaling_->OnDataChannelClose(); 1271 SignalDataChannelDestroyed(); 1272 const std::string content_name = data_channel_->content_name(); 1273 channel_manager_->DestroyDataChannel(data_channel_.release()); 1274 DestroyTransportProxy(content_name); 1275 } 1276 } 1277 1278 // TODO(mallinath) - Add a correct error code if the channels are not creatued 1279 // due to BUNDLE is enabled but rtcp-mux is disabled. 1280 bool WebRtcSession::CreateChannels(const SessionDescription* desc) { 1281 // Disabling the BUNDLE flag in PortAllocator if offer disabled it. 1282 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE); 1283 if (state() == STATE_INIT && !bundle_enabled) { 1284 port_allocator()->set_flags(port_allocator()->flags() & 1285 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE); 1286 } 1287 1288 // Creating the media channels and transport proxies. 1289 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc); 1290 if (voice && !voice->rejected && !voice_channel_) { 1291 if (!CreateVoiceChannel(voice)) { 1292 LOG(LS_ERROR) << "Failed to create voice channel."; 1293 return false; 1294 } 1295 } 1296 1297 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc); 1298 if (video && !video->rejected && !video_channel_) { 1299 if (!CreateVideoChannel(video)) { 1300 LOG(LS_ERROR) << "Failed to create video channel."; 1301 return false; 1302 } 1303 } 1304 1305 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc); 1306 if (data_channel_type_ != cricket::DCT_NONE && 1307 data && !data->rejected && !data_channel_.get()) { 1308 if (!CreateDataChannel(data)) { 1309 LOG(LS_ERROR) << "Failed to create data channel."; 1310 return false; 1311 } 1312 } 1313 1314 return true; 1315 } 1316 1317 bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) { 1318 voice_channel_.reset(channel_manager_->CreateVoiceChannel( 1319 this, content->name, true)); 1320 return (voice_channel_ != NULL); 1321 } 1322 1323 bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) { 1324 video_channel_.reset(channel_manager_->CreateVideoChannel( 1325 this, content->name, true, voice_channel_.get())); 1326 return (video_channel_ != NULL); 1327 } 1328 1329 bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) { 1330 bool rtcp = (data_channel_type_ == cricket::DCT_RTP); 1331 data_channel_.reset(channel_manager_->CreateDataChannel( 1332 this, content->name, rtcp, data_channel_type_)); 1333 if (!data_channel_.get()) { 1334 return false; 1335 } 1336 data_channel_->SignalDataReceived.connect( 1337 this, &WebRtcSession::OnDataReceived); 1338 return true; 1339 } 1340 1341 void WebRtcSession::CopySavedCandidates( 1342 SessionDescriptionInterface* dest_desc) { 1343 if (!dest_desc) { 1344 ASSERT(false); 1345 return; 1346 } 1347 for (size_t i = 0; i < saved_candidates_.size(); ++i) { 1348 dest_desc->AddCandidate(saved_candidates_[i]); 1349 delete saved_candidates_[i]; 1350 } 1351 saved_candidates_.clear(); 1352 } 1353 1354 // Look for OPEN messages and set up data channels in response. 1355 void WebRtcSession::OnDataReceived( 1356 cricket::DataChannel* channel, 1357 const cricket::ReceiveDataParams& params, 1358 const talk_base::Buffer& payload) { 1359 if (params.type != cricket::DMT_CONTROL) { 1360 return; 1361 } 1362 1363 std::string label; 1364 DataChannelInit config; 1365 if (!mediastream_signaling_->ParseDataChannelOpenMessage( 1366 payload, &label, &config)) { 1367 LOG(LS_WARNING) << "Failed to parse data channel OPEN message."; 1368 return; 1369 } 1370 1371 config.negotiated = true; // This is the negotiation. 1372 1373 if (!mediastream_signaling_->AddDataChannelFromOpenMessage( 1374 label, config)) { 1375 LOG(LS_WARNING) << "Failed to create data channel from OPEN message."; 1376 return; 1377 } 1378 } 1379 1380 // Returns false if bundle is enabled and rtcp_mux is disabled. 1381 bool WebRtcSession::VerifyBundleSettings(const SessionDescription* desc) { 1382 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE); 1383 if (!bundle_enabled) 1384 return true; 1385 1386 const cricket::ContentGroup* bundle_group = 1387 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); 1388 ASSERT(bundle_group != NULL); 1389 1390 const cricket::ContentInfos& contents = desc->contents(); 1391 for (cricket::ContentInfos::const_iterator citer = contents.begin(); 1392 citer != contents.end(); ++citer) { 1393 const cricket::ContentInfo* content = (&*citer); 1394 ASSERT(content != NULL); 1395 if (bundle_group->HasContentName(content->name) && 1396 !content->rejected && content->type == cricket::NS_JINGLE_RTP) { 1397 if (!HasRtcpMuxEnabled(content)) 1398 return false; 1399 } 1400 } 1401 // RTCP-MUX is enabled in all the contents. 1402 return true; 1403 } 1404 1405 bool WebRtcSession::HasRtcpMuxEnabled( 1406 const cricket::ContentInfo* content) { 1407 const cricket::MediaContentDescription* description = 1408 static_cast<cricket::MediaContentDescription*>(content->description); 1409 return description->rtcp_mux(); 1410 } 1411 1412 } // namespace webrtc 1413