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/peerconnection.h" 29 30 #include <vector> 31 32 #include "talk/app/webrtc/dtmfsender.h" 33 #include "talk/app/webrtc/jsepicecandidate.h" 34 #include "talk/app/webrtc/jsepsessiondescription.h" 35 #include "talk/app/webrtc/mediaconstraintsinterface.h" 36 #include "talk/app/webrtc/mediastreamhandler.h" 37 #include "talk/app/webrtc/streamcollection.h" 38 #include "talk/base/logging.h" 39 #include "talk/base/stringencode.h" 40 #include "talk/p2p/client/basicportallocator.h" 41 #include "talk/session/media/channelmanager.h" 42 43 namespace { 44 45 using webrtc::PeerConnectionInterface; 46 47 // The min number of tokens must present in Turn host uri. 48 // e.g. user (at) turn.example.org 49 static const size_t kTurnHostTokensNum = 2; 50 // Number of tokens must be preset when TURN uri has transport param. 51 static const size_t kTurnTransportTokensNum = 2; 52 // The default stun port. 53 static const int kDefaultStunPort = 3478; 54 static const int kDefaultStunTlsPort = 5349; 55 static const char kTransport[] = "transport"; 56 static const char kUdpTransportType[] = "udp"; 57 static const char kTcpTransportType[] = "tcp"; 58 59 // NOTE: Must be in the same order as the ServiceType enum. 60 static const char* kValidIceServiceTypes[] = { 61 "stun", "stuns", "turn", "turns", "invalid" }; 62 63 enum ServiceType { 64 STUN, // Indicates a STUN server. 65 STUNS, // Indicates a STUN server used with a TLS session. 66 TURN, // Indicates a TURN server 67 TURNS, // Indicates a TURN server used with a TLS session. 68 INVALID, // Unknown. 69 }; 70 71 enum { 72 MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0, 73 MSG_SET_SESSIONDESCRIPTION_FAILED, 74 MSG_GETSTATS, 75 }; 76 77 struct SetSessionDescriptionMsg : public talk_base::MessageData { 78 explicit SetSessionDescriptionMsg( 79 webrtc::SetSessionDescriptionObserver* observer) 80 : observer(observer) { 81 } 82 83 talk_base::scoped_refptr<webrtc::SetSessionDescriptionObserver> observer; 84 std::string error; 85 }; 86 87 struct GetStatsMsg : public talk_base::MessageData { 88 explicit GetStatsMsg(webrtc::StatsObserver* observer) 89 : observer(observer) { 90 } 91 webrtc::StatsReports reports; 92 talk_base::scoped_refptr<webrtc::StatsObserver> observer; 93 }; 94 95 // |in_str| should be of format 96 // stunURI = scheme ":" stun-host [ ":" stun-port ] 97 // scheme = "stun" / "stuns" 98 // stun-host = IP-literal / IPv4address / reg-name 99 // stun-port = *DIGIT 100 101 // draft-petithuguenin-behave-turn-uris-01 102 // turnURI = scheme ":" turn-host [ ":" turn-port ] 103 // turn-host = username@IP-literal / IPv4address / reg-name 104 bool GetServiceTypeAndHostnameFromUri(const std::string& in_str, 105 ServiceType* service_type, 106 std::string* hostname) { 107 std::string::size_type colonpos = in_str.find(':'); 108 if (colonpos == std::string::npos) { 109 return false; 110 } 111 std::string type = in_str.substr(0, colonpos); 112 for (size_t i = 0; i < ARRAY_SIZE(kValidIceServiceTypes); ++i) { 113 if (type.compare(kValidIceServiceTypes[i]) == 0) { 114 *service_type = static_cast<ServiceType>(i); 115 break; 116 } 117 } 118 if (*service_type == INVALID) { 119 return false; 120 } 121 *hostname = in_str.substr(colonpos + 1, std::string::npos); 122 return true; 123 } 124 125 // This method parses IPv6 and IPv4 literal strings, along with hostnames in 126 // standard hostname:port format. 127 // Consider following formats as correct. 128 // |hostname:port|, |[IPV6 address]:port|, |IPv4 address|:port, 129 // |hostname|, |[IPv6 address]|, |IPv4 address| 130 bool ParseHostnameAndPortFromString(const std::string& in_str, 131 std::string* host, 132 int* port) { 133 if (in_str.at(0) == '[') { 134 std::string::size_type closebracket = in_str.rfind(']'); 135 if (closebracket != std::string::npos) { 136 *host = in_str.substr(1, closebracket - 1); 137 std::string::size_type colonpos = in_str.find(':', closebracket); 138 if (std::string::npos != colonpos) { 139 if (!talk_base::FromString( 140 in_str.substr(closebracket + 2, std::string::npos), port)) { 141 return false; 142 } 143 } 144 } else { 145 return false; 146 } 147 } else { 148 std::string::size_type colonpos = in_str.find(':'); 149 if (std::string::npos != colonpos) { 150 *host = in_str.substr(0, colonpos); 151 if (!talk_base::FromString( 152 in_str.substr(colonpos + 1, std::string::npos), port)) { 153 return false; 154 } 155 } else { 156 *host = in_str; 157 } 158 } 159 return true; 160 } 161 162 typedef webrtc::PortAllocatorFactoryInterface::StunConfiguration 163 StunConfiguration; 164 typedef webrtc::PortAllocatorFactoryInterface::TurnConfiguration 165 TurnConfiguration; 166 167 bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, 168 std::vector<StunConfiguration>* stun_config, 169 std::vector<TurnConfiguration>* turn_config) { 170 // draft-nandakumar-rtcweb-stun-uri-01 171 // stunURI = scheme ":" stun-host [ ":" stun-port ] 172 // scheme = "stun" / "stuns" 173 // stun-host = IP-literal / IPv4address / reg-name 174 // stun-port = *DIGIT 175 176 // draft-petithuguenin-behave-turn-uris-01 177 // turnURI = scheme ":" turn-host [ ":" turn-port ] 178 // [ "?transport=" transport ] 179 // scheme = "turn" / "turns" 180 // transport = "udp" / "tcp" / transport-ext 181 // transport-ext = 1*unreserved 182 // turn-host = IP-literal / IPv4address / reg-name 183 // turn-port = *DIGIT 184 for (size_t i = 0; i < configuration.size(); ++i) { 185 webrtc::PeerConnectionInterface::IceServer server = configuration[i]; 186 if (server.uri.empty()) { 187 LOG(WARNING) << "Empty uri."; 188 continue; 189 } 190 std::vector<std::string> tokens; 191 std::string turn_transport_type = kUdpTransportType; 192 talk_base::tokenize(server.uri, '?', &tokens); 193 std::string uri_without_transport = tokens[0]; 194 // Let's look into transport= param, if it exists. 195 if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present. 196 std::string uri_transport_param = tokens[1]; 197 talk_base::tokenize(uri_transport_param, '=', &tokens); 198 if (tokens[0] == kTransport) { 199 // As per above grammar transport param will be consist of lower case 200 // letters. 201 if (tokens[1] != kUdpTransportType && tokens[1] != kTcpTransportType) { 202 LOG(LS_WARNING) << "Transport param should always be udp or tcp."; 203 continue; 204 } 205 turn_transport_type = tokens[1]; 206 } 207 } 208 209 std::string hoststring; 210 ServiceType service_type = INVALID; 211 if (!GetServiceTypeAndHostnameFromUri(uri_without_transport, 212 &service_type, 213 &hoststring)) { 214 LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " 215 << uri_without_transport; 216 continue; 217 } 218 219 // Let's break hostname. 220 tokens.clear(); 221 talk_base::tokenize(hoststring, '@', &tokens); 222 hoststring = tokens[0]; 223 if (tokens.size() == kTurnHostTokensNum) { 224 server.username = talk_base::s_url_decode(tokens[0]); 225 hoststring = tokens[1]; 226 } 227 228 int port = kDefaultStunPort; 229 if (service_type == TURNS) { 230 port = kDefaultStunTlsPort; 231 turn_transport_type = kTcpTransportType; 232 } 233 234 std::string address; 235 if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { 236 LOG(WARNING) << "Invalid Hostname format: " << uri_without_transport; 237 continue; 238 } 239 240 241 if (port <= 0 || port > 0xffff) { 242 LOG(WARNING) << "Invalid port: " << port; 243 continue; 244 } 245 246 switch (service_type) { 247 case STUN: 248 case STUNS: 249 stun_config->push_back(StunConfiguration(address, port)); 250 break; 251 case TURN: 252 case TURNS: { 253 if (server.username.empty()) { 254 // Turn url example from the spec |url:"turn:user (at) turn.example.org"|. 255 std::vector<std::string> turn_tokens; 256 talk_base::tokenize(address, '@', &turn_tokens); 257 if (turn_tokens.size() == kTurnHostTokensNum) { 258 server.username = talk_base::s_url_decode(turn_tokens[0]); 259 address = turn_tokens[1]; 260 } 261 } 262 263 bool secure = (service_type == TURNS); 264 265 turn_config->push_back(TurnConfiguration(address, port, 266 server.username, 267 server.password, 268 turn_transport_type, 269 secure)); 270 break; 271 } 272 case INVALID: 273 default: 274 LOG(WARNING) << "Configuration not supported: " << server.uri; 275 return false; 276 } 277 } 278 return true; 279 } 280 281 // Check if we can send |new_stream| on a PeerConnection. 282 // Currently only one audio but multiple video track is supported per 283 // PeerConnection. 284 bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams, 285 webrtc::MediaStreamInterface* new_stream) { 286 if (!new_stream || !current_streams) 287 return false; 288 if (current_streams->find(new_stream->label()) != NULL) { 289 LOG(LS_ERROR) << "MediaStream with label " << new_stream->label() 290 << " is already added."; 291 return false; 292 } 293 294 return true; 295 } 296 297 } // namespace 298 299 namespace webrtc { 300 301 PeerConnection::PeerConnection(PeerConnectionFactory* factory) 302 : factory_(factory), 303 observer_(NULL), 304 uma_observer_(NULL), 305 signaling_state_(kStable), 306 ice_state_(kIceNew), 307 ice_connection_state_(kIceConnectionNew), 308 ice_gathering_state_(kIceGatheringNew) { 309 } 310 311 PeerConnection::~PeerConnection() { 312 if (mediastream_signaling_) 313 mediastream_signaling_->TearDown(); 314 if (stream_handler_container_) 315 stream_handler_container_->TearDown(); 316 } 317 318 bool PeerConnection::Initialize( 319 const PeerConnectionInterface::RTCConfiguration& configuration, 320 const MediaConstraintsInterface* constraints, 321 PortAllocatorFactoryInterface* allocator_factory, 322 DTLSIdentityServiceInterface* dtls_identity_service, 323 PeerConnectionObserver* observer) { 324 std::vector<PortAllocatorFactoryInterface::StunConfiguration> stun_config; 325 std::vector<PortAllocatorFactoryInterface::TurnConfiguration> turn_config; 326 if (!ParseIceServers(configuration.servers, &stun_config, &turn_config)) { 327 return false; 328 } 329 330 return DoInitialize(configuration.type, stun_config, turn_config, constraints, 331 allocator_factory, dtls_identity_service, observer); 332 } 333 334 bool PeerConnection::DoInitialize( 335 IceTransportsType type, 336 const StunConfigurations& stun_config, 337 const TurnConfigurations& turn_config, 338 const MediaConstraintsInterface* constraints, 339 webrtc::PortAllocatorFactoryInterface* allocator_factory, 340 DTLSIdentityServiceInterface* dtls_identity_service, 341 PeerConnectionObserver* observer) { 342 ASSERT(observer != NULL); 343 if (!observer) 344 return false; 345 observer_ = observer; 346 port_allocator_.reset( 347 allocator_factory->CreatePortAllocator(stun_config, turn_config)); 348 349 // To handle both internal and externally created port allocator, we will 350 // enable BUNDLE here. 351 int portallocator_flags = cricket::PORTALLOCATOR_ENABLE_BUNDLE | 352 cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | 353 cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET; 354 bool value; 355 if (FindConstraint( 356 constraints, 357 MediaConstraintsInterface::kEnableIPv6, 358 &value, NULL) && value) { 359 portallocator_flags |= cricket::PORTALLOCATOR_ENABLE_IPV6; 360 } 361 362 port_allocator_->set_flags(portallocator_flags); 363 // No step delay is used while allocating ports. 364 port_allocator_->set_step_delay(cricket::kMinimumStepDelay); 365 366 mediastream_signaling_.reset(new MediaStreamSignaling( 367 factory_->signaling_thread(), this, factory_->channel_manager())); 368 369 session_.reset(new WebRtcSession(factory_->channel_manager(), 370 factory_->signaling_thread(), 371 factory_->worker_thread(), 372 port_allocator_.get(), 373 mediastream_signaling_.get())); 374 stream_handler_container_.reset(new MediaStreamHandlerContainer( 375 session_.get(), session_.get())); 376 stats_.set_session(session_.get()); 377 378 // Initialize the WebRtcSession. It creates transport channels etc. 379 if (!session_->Initialize(factory_->options(), constraints, 380 dtls_identity_service, type)) 381 return false; 382 383 // Register PeerConnection as receiver of local ice candidates. 384 // All the callbacks will be posted to the application from PeerConnection. 385 session_->RegisterIceObserver(this); 386 session_->SignalState.connect(this, &PeerConnection::OnSessionStateChange); 387 return true; 388 } 389 390 talk_base::scoped_refptr<StreamCollectionInterface> 391 PeerConnection::local_streams() { 392 return mediastream_signaling_->local_streams(); 393 } 394 395 talk_base::scoped_refptr<StreamCollectionInterface> 396 PeerConnection::remote_streams() { 397 return mediastream_signaling_->remote_streams(); 398 } 399 400 bool PeerConnection::AddStream(MediaStreamInterface* local_stream, 401 const MediaConstraintsInterface* constraints) { 402 if (IsClosed()) { 403 return false; 404 } 405 if (!CanAddLocalMediaStream(mediastream_signaling_->local_streams(), 406 local_stream)) 407 return false; 408 409 // TODO(perkj): Implement support for MediaConstraints in AddStream. 410 if (!mediastream_signaling_->AddLocalStream(local_stream)) { 411 return false; 412 } 413 stats_.AddStream(local_stream); 414 observer_->OnRenegotiationNeeded(); 415 return true; 416 } 417 418 void PeerConnection::RemoveStream(MediaStreamInterface* local_stream) { 419 mediastream_signaling_->RemoveLocalStream(local_stream); 420 if (IsClosed()) { 421 return; 422 } 423 observer_->OnRenegotiationNeeded(); 424 } 425 426 talk_base::scoped_refptr<DtmfSenderInterface> PeerConnection::CreateDtmfSender( 427 AudioTrackInterface* track) { 428 if (!track) { 429 LOG(LS_ERROR) << "CreateDtmfSender - track is NULL."; 430 return NULL; 431 } 432 if (!mediastream_signaling_->local_streams()->FindAudioTrack(track->id())) { 433 LOG(LS_ERROR) << "CreateDtmfSender is called with a non local audio track."; 434 return NULL; 435 } 436 437 talk_base::scoped_refptr<DtmfSenderInterface> sender( 438 DtmfSender::Create(track, signaling_thread(), session_.get())); 439 if (!sender.get()) { 440 LOG(LS_ERROR) << "CreateDtmfSender failed on DtmfSender::Create."; 441 return NULL; 442 } 443 return DtmfSenderProxy::Create(signaling_thread(), sender.get()); 444 } 445 446 bool PeerConnection::GetStats(StatsObserver* observer, 447 MediaStreamTrackInterface* track, 448 StatsOutputLevel level) { 449 if (!VERIFY(observer != NULL)) { 450 LOG(LS_ERROR) << "GetStats - observer is NULL."; 451 return false; 452 } 453 454 stats_.UpdateStats(level); 455 talk_base::scoped_ptr<GetStatsMsg> msg(new GetStatsMsg(observer)); 456 if (!stats_.GetStats(track, &(msg->reports))) { 457 return false; 458 } 459 signaling_thread()->Post(this, MSG_GETSTATS, msg.release()); 460 return true; 461 } 462 463 PeerConnectionInterface::SignalingState PeerConnection::signaling_state() { 464 return signaling_state_; 465 } 466 467 PeerConnectionInterface::IceState PeerConnection::ice_state() { 468 return ice_state_; 469 } 470 471 PeerConnectionInterface::IceConnectionState 472 PeerConnection::ice_connection_state() { 473 return ice_connection_state_; 474 } 475 476 PeerConnectionInterface::IceGatheringState 477 PeerConnection::ice_gathering_state() { 478 return ice_gathering_state_; 479 } 480 481 talk_base::scoped_refptr<DataChannelInterface> 482 PeerConnection::CreateDataChannel( 483 const std::string& label, 484 const DataChannelInit* config) { 485 bool first_datachannel = !mediastream_signaling_->HasDataChannels(); 486 487 talk_base::scoped_ptr<InternalDataChannelInit> internal_config; 488 if (config) { 489 internal_config.reset(new InternalDataChannelInit(*config)); 490 } 491 talk_base::scoped_refptr<DataChannelInterface> channel( 492 session_->CreateDataChannel(label, internal_config.get())); 493 if (!channel.get()) 494 return NULL; 495 496 // Trigger the onRenegotiationNeeded event for every new RTP DataChannel, or 497 // the first SCTP DataChannel. 498 if (session_->data_channel_type() == cricket::DCT_RTP || first_datachannel) { 499 observer_->OnRenegotiationNeeded(); 500 } 501 502 return DataChannelProxy::Create(signaling_thread(), channel.get()); 503 } 504 505 void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer, 506 const MediaConstraintsInterface* constraints) { 507 if (!VERIFY(observer != NULL)) { 508 LOG(LS_ERROR) << "CreateOffer - observer is NULL."; 509 return; 510 } 511 session_->CreateOffer(observer, constraints); 512 } 513 514 void PeerConnection::CreateAnswer( 515 CreateSessionDescriptionObserver* observer, 516 const MediaConstraintsInterface* constraints) { 517 if (!VERIFY(observer != NULL)) { 518 LOG(LS_ERROR) << "CreateAnswer - observer is NULL."; 519 return; 520 } 521 session_->CreateAnswer(observer, constraints); 522 } 523 524 void PeerConnection::SetLocalDescription( 525 SetSessionDescriptionObserver* observer, 526 SessionDescriptionInterface* desc) { 527 if (!VERIFY(observer != NULL)) { 528 LOG(LS_ERROR) << "SetLocalDescription - observer is NULL."; 529 return; 530 } 531 if (!desc) { 532 PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL."); 533 return; 534 } 535 // Update stats here so that we have the most recent stats for tracks and 536 // streams that might be removed by updating the session description. 537 stats_.UpdateStats(kStatsOutputLevelStandard); 538 std::string error; 539 if (!session_->SetLocalDescription(desc, &error)) { 540 PostSetSessionDescriptionFailure(observer, error); 541 return; 542 } 543 SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); 544 signaling_thread()->Post(this, MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg); 545 } 546 547 void PeerConnection::SetRemoteDescription( 548 SetSessionDescriptionObserver* observer, 549 SessionDescriptionInterface* desc) { 550 if (!VERIFY(observer != NULL)) { 551 LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL."; 552 return; 553 } 554 if (!desc) { 555 PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL."); 556 return; 557 } 558 // Update stats here so that we have the most recent stats for tracks and 559 // streams that might be removed by updating the session description. 560 stats_.UpdateStats(kStatsOutputLevelStandard); 561 std::string error; 562 if (!session_->SetRemoteDescription(desc, &error)) { 563 PostSetSessionDescriptionFailure(observer, error); 564 return; 565 } 566 SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); 567 signaling_thread()->Post(this, MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg); 568 } 569 570 void PeerConnection::PostSetSessionDescriptionFailure( 571 SetSessionDescriptionObserver* observer, 572 const std::string& error) { 573 SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); 574 msg->error = error; 575 signaling_thread()->Post(this, MSG_SET_SESSIONDESCRIPTION_FAILED, msg); 576 } 577 578 bool PeerConnection::UpdateIce(const IceServers& configuration, 579 const MediaConstraintsInterface* constraints) { 580 return false; 581 } 582 583 bool PeerConnection::UpdateIce(const RTCConfiguration& config) { 584 if (port_allocator_) { 585 std::vector<PortAllocatorFactoryInterface::StunConfiguration> stuns; 586 std::vector<PortAllocatorFactoryInterface::TurnConfiguration> turns; 587 if (!ParseIceServers(config.servers, &stuns, &turns)) { 588 return false; 589 } 590 591 std::vector<talk_base::SocketAddress> stun_hosts; 592 typedef std::vector<StunConfiguration>::const_iterator StunIt; 593 for (StunIt stun_it = stuns.begin(); stun_it != stuns.end(); ++stun_it) { 594 stun_hosts.push_back(stun_it->server); 595 } 596 597 talk_base::SocketAddress stun_addr; 598 if (!stun_hosts.empty()) { 599 stun_addr = stun_hosts.front(); 600 LOG(LS_INFO) << "UpdateIce: StunServer Address: " << stun_addr.ToString(); 601 } 602 603 for (size_t i = 0; i < turns.size(); ++i) { 604 cricket::RelayCredentials credentials(turns[i].username, 605 turns[i].password); 606 cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); 607 cricket::ProtocolType protocol; 608 if (cricket::StringToProto(turns[i].transport_type.c_str(), &protocol)) { 609 relay_server.ports.push_back(cricket::ProtocolAddress( 610 turns[i].server, protocol, turns[i].secure)); 611 relay_server.credentials = credentials; 612 LOG(LS_INFO) << "UpdateIce: TurnServer Address: " 613 << turns[i].server.ToString(); 614 } else { 615 LOG(LS_WARNING) << "Ignoring TURN server " << turns[i].server << ". " 616 << "Reason= Incorrect " << turns[i].transport_type 617 << " transport parameter."; 618 } 619 } 620 } 621 return session_->UpdateIce(config.type); 622 } 623 624 bool PeerConnection::AddIceCandidate( 625 const IceCandidateInterface* ice_candidate) { 626 return session_->ProcessIceMessage(ice_candidate); 627 } 628 629 void PeerConnection::RegisterUMAObserver(UMAObserver* observer) { 630 uma_observer_ = observer; 631 // Send information about IPv4/IPv6 status. 632 if (uma_observer_ && port_allocator_) { 633 if (port_allocator_->flags() & cricket::PORTALLOCATOR_ENABLE_IPV6) { 634 uma_observer_->IncrementCounter(kPeerConnection_IPv6); 635 } else { 636 uma_observer_->IncrementCounter(kPeerConnection_IPv4); 637 } 638 } 639 } 640 641 const SessionDescriptionInterface* PeerConnection::local_description() const { 642 return session_->local_description(); 643 } 644 645 const SessionDescriptionInterface* PeerConnection::remote_description() const { 646 return session_->remote_description(); 647 } 648 649 void PeerConnection::Close() { 650 // Update stats here so that we have the most recent stats for tracks and 651 // streams before the channels are closed. 652 stats_.UpdateStats(kStatsOutputLevelStandard); 653 654 session_->Terminate(); 655 } 656 657 void PeerConnection::OnSessionStateChange(cricket::BaseSession* /*session*/, 658 cricket::BaseSession::State state) { 659 switch (state) { 660 case cricket::BaseSession::STATE_INIT: 661 ChangeSignalingState(PeerConnectionInterface::kStable); 662 break; 663 case cricket::BaseSession::STATE_SENTINITIATE: 664 ChangeSignalingState(PeerConnectionInterface::kHaveLocalOffer); 665 break; 666 case cricket::BaseSession::STATE_SENTPRACCEPT: 667 ChangeSignalingState(PeerConnectionInterface::kHaveLocalPrAnswer); 668 break; 669 case cricket::BaseSession::STATE_RECEIVEDINITIATE: 670 ChangeSignalingState(PeerConnectionInterface::kHaveRemoteOffer); 671 break; 672 case cricket::BaseSession::STATE_RECEIVEDPRACCEPT: 673 ChangeSignalingState(PeerConnectionInterface::kHaveRemotePrAnswer); 674 break; 675 case cricket::BaseSession::STATE_SENTACCEPT: 676 case cricket::BaseSession::STATE_RECEIVEDACCEPT: 677 ChangeSignalingState(PeerConnectionInterface::kStable); 678 break; 679 case cricket::BaseSession::STATE_RECEIVEDTERMINATE: 680 ChangeSignalingState(PeerConnectionInterface::kClosed); 681 break; 682 default: 683 break; 684 } 685 } 686 687 void PeerConnection::OnMessage(talk_base::Message* msg) { 688 switch (msg->message_id) { 689 case MSG_SET_SESSIONDESCRIPTION_SUCCESS: { 690 SetSessionDescriptionMsg* param = 691 static_cast<SetSessionDescriptionMsg*>(msg->pdata); 692 param->observer->OnSuccess(); 693 delete param; 694 break; 695 } 696 case MSG_SET_SESSIONDESCRIPTION_FAILED: { 697 SetSessionDescriptionMsg* param = 698 static_cast<SetSessionDescriptionMsg*>(msg->pdata); 699 param->observer->OnFailure(param->error); 700 delete param; 701 break; 702 } 703 case MSG_GETSTATS: { 704 GetStatsMsg* param = static_cast<GetStatsMsg*>(msg->pdata); 705 param->observer->OnComplete(param->reports); 706 delete param; 707 break; 708 } 709 default: 710 ASSERT(false && "Not implemented"); 711 break; 712 } 713 } 714 715 void PeerConnection::OnAddRemoteStream(MediaStreamInterface* stream) { 716 stats_.AddStream(stream); 717 observer_->OnAddStream(stream); 718 } 719 720 void PeerConnection::OnRemoveRemoteStream(MediaStreamInterface* stream) { 721 stream_handler_container_->RemoveRemoteStream(stream); 722 observer_->OnRemoveStream(stream); 723 } 724 725 void PeerConnection::OnAddDataChannel(DataChannelInterface* data_channel) { 726 observer_->OnDataChannel(DataChannelProxy::Create(signaling_thread(), 727 data_channel)); 728 } 729 730 void PeerConnection::OnAddRemoteAudioTrack(MediaStreamInterface* stream, 731 AudioTrackInterface* audio_track, 732 uint32 ssrc) { 733 stream_handler_container_->AddRemoteAudioTrack(stream, audio_track, ssrc); 734 } 735 736 void PeerConnection::OnAddRemoteVideoTrack(MediaStreamInterface* stream, 737 VideoTrackInterface* video_track, 738 uint32 ssrc) { 739 stream_handler_container_->AddRemoteVideoTrack(stream, video_track, ssrc); 740 } 741 742 void PeerConnection::OnRemoveRemoteAudioTrack( 743 MediaStreamInterface* stream, 744 AudioTrackInterface* audio_track) { 745 stream_handler_container_->RemoveRemoteTrack(stream, audio_track); 746 } 747 748 void PeerConnection::OnRemoveRemoteVideoTrack( 749 MediaStreamInterface* stream, 750 VideoTrackInterface* video_track) { 751 stream_handler_container_->RemoveRemoteTrack(stream, video_track); 752 } 753 void PeerConnection::OnAddLocalAudioTrack(MediaStreamInterface* stream, 754 AudioTrackInterface* audio_track, 755 uint32 ssrc) { 756 stream_handler_container_->AddLocalAudioTrack(stream, audio_track, ssrc); 757 stats_.AddLocalAudioTrack(audio_track, ssrc); 758 } 759 void PeerConnection::OnAddLocalVideoTrack(MediaStreamInterface* stream, 760 VideoTrackInterface* video_track, 761 uint32 ssrc) { 762 stream_handler_container_->AddLocalVideoTrack(stream, video_track, ssrc); 763 } 764 765 void PeerConnection::OnRemoveLocalAudioTrack(MediaStreamInterface* stream, 766 AudioTrackInterface* audio_track, 767 uint32 ssrc) { 768 stream_handler_container_->RemoveLocalTrack(stream, audio_track); 769 stats_.RemoveLocalAudioTrack(audio_track, ssrc); 770 } 771 772 void PeerConnection::OnRemoveLocalVideoTrack(MediaStreamInterface* stream, 773 VideoTrackInterface* video_track) { 774 stream_handler_container_->RemoveLocalTrack(stream, video_track); 775 } 776 777 void PeerConnection::OnRemoveLocalStream(MediaStreamInterface* stream) { 778 stream_handler_container_->RemoveLocalStream(stream); 779 } 780 781 void PeerConnection::OnIceConnectionChange( 782 PeerConnectionInterface::IceConnectionState new_state) { 783 ASSERT(signaling_thread()->IsCurrent()); 784 ice_connection_state_ = new_state; 785 observer_->OnIceConnectionChange(ice_connection_state_); 786 } 787 788 void PeerConnection::OnIceGatheringChange( 789 PeerConnectionInterface::IceGatheringState new_state) { 790 ASSERT(signaling_thread()->IsCurrent()); 791 if (IsClosed()) { 792 return; 793 } 794 ice_gathering_state_ = new_state; 795 observer_->OnIceGatheringChange(ice_gathering_state_); 796 } 797 798 void PeerConnection::OnIceCandidate(const IceCandidateInterface* candidate) { 799 ASSERT(signaling_thread()->IsCurrent()); 800 observer_->OnIceCandidate(candidate); 801 } 802 803 void PeerConnection::OnIceComplete() { 804 ASSERT(signaling_thread()->IsCurrent()); 805 observer_->OnIceComplete(); 806 } 807 808 void PeerConnection::ChangeSignalingState( 809 PeerConnectionInterface::SignalingState signaling_state) { 810 signaling_state_ = signaling_state; 811 if (signaling_state == kClosed) { 812 ice_connection_state_ = kIceConnectionClosed; 813 observer_->OnIceConnectionChange(ice_connection_state_); 814 if (ice_gathering_state_ != kIceGatheringComplete) { 815 ice_gathering_state_ = kIceGatheringComplete; 816 observer_->OnIceGatheringChange(ice_gathering_state_); 817 } 818 } 819 observer_->OnSignalingChange(signaling_state_); 820 observer_->OnStateChange(PeerConnectionObserver::kSignalingState); 821 } 822 823 } // namespace webrtc 824