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