Home | History | Annotate | Download | only in host
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/cast_extension_session.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/json/json_reader.h"
      9 #include "base/json/json_writer.h"
     10 #include "base/logging.h"
     11 #include "base/synchronization/waitable_event.h"
     12 #include "net/url_request/url_request_context_getter.h"
     13 #include "remoting/host/cast_video_capturer_adapter.h"
     14 #include "remoting/host/chromium_port_allocator_factory.h"
     15 #include "remoting/host/client_session.h"
     16 #include "remoting/proto/control.pb.h"
     17 #include "remoting/protocol/client_stub.h"
     18 #include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h"
     19 #include "third_party/libjingle/source/talk/app/webrtc/test/fakeconstraints.h"
     20 #include "third_party/libjingle/source/talk/app/webrtc/videosourceinterface.h"
     21 
     22 namespace remoting {
     23 
     24 // Used as the type attribute of all Cast protocol::ExtensionMessages.
     25 const char kExtensionMessageType[] = "cast_message";
     26 
     27 // Top-level keys used in all extension messages between host and client.
     28 // Must keep synced with webapp.
     29 const char kTopLevelData[] = "chromoting_data";
     30 const char kTopLevelSubject[] = "subject";
     31 
     32 // Keys used to describe the subject of a cast extension message. WebRTC-related
     33 // message subjects are prepended with "webrtc_".
     34 // Must keep synced with webapp.
     35 const char kSubjectReady[] = "ready";
     36 const char kSubjectTest[] = "test";
     37 const char kSubjectNewCandidate[] = "webrtc_candidate";
     38 const char kSubjectOffer[] = "webrtc_offer";
     39 const char kSubjectAnswer[] = "webrtc_answer";
     40 
     41 // WebRTC headers used inside messages with subject = "webrtc_*".
     42 const char kWebRtcCandidate[] = "candidate";
     43 const char kWebRtcSessionDescType[] = "type";
     44 const char kWebRtcSessionDescSDP[] = "sdp";
     45 const char kWebRtcSDPMid[] = "sdpMid";
     46 const char kWebRtcSDPMLineIndex[] = "sdpMLineIndex";
     47 
     48 // Media labels used over the PeerConnection.
     49 const char kVideoLabel[] = "cast_video_label";
     50 const char kStreamLabel[] = "stream_label";
     51 
     52 // Default STUN server used to construct
     53 // webrtc::PeerConnectionInterface::RTCConfiguration for the PeerConnection.
     54 const char kDefaultStunURI[] = "stun:stun.l.google.com:19302";
     55 
     56 const char kWorkerThreadName[] = "CastExtensionSessionWorkerThread";
     57 
     58 // Interval between each call to PollPeerConnectionStats().
     59 const int kStatsLogIntervalSec = 10;
     60 
     61 // Minimum frame rate for video streaming over the PeerConnection in frames per
     62 // second, added as a media constraint when constructing the video source for
     63 // the Peer Connection.
     64 const int kMinFramesPerSecond = 5;
     65 
     66 // A webrtc::SetSessionDescriptionObserver implementation used to receive the
     67 // results of setting local and remote descriptions of the PeerConnection.
     68 class CastSetSessionDescriptionObserver
     69     : public webrtc::SetSessionDescriptionObserver {
     70  public:
     71   static CastSetSessionDescriptionObserver* Create() {
     72     return new rtc::RefCountedObject<CastSetSessionDescriptionObserver>();
     73   }
     74   virtual void OnSuccess() OVERRIDE {
     75     VLOG(1) << "Setting session description succeeded.";
     76   }
     77   virtual void OnFailure(const std::string& error) OVERRIDE {
     78     LOG(ERROR) << "Setting session description failed: " << error;
     79   }
     80 
     81  protected:
     82   CastSetSessionDescriptionObserver() {}
     83   virtual ~CastSetSessionDescriptionObserver() {}
     84 
     85   DISALLOW_COPY_AND_ASSIGN(CastSetSessionDescriptionObserver);
     86 };
     87 
     88 // A webrtc::CreateSessionDescriptionObserver implementation used to receive the
     89 // results of creating descriptions for this end of the PeerConnection.
     90 class CastCreateSessionDescriptionObserver
     91     : public webrtc::CreateSessionDescriptionObserver {
     92  public:
     93   static CastCreateSessionDescriptionObserver* Create(
     94       CastExtensionSession* session) {
     95     return new rtc::RefCountedObject<CastCreateSessionDescriptionObserver>(
     96         session);
     97   }
     98   virtual void OnSuccess(webrtc::SessionDescriptionInterface* desc) OVERRIDE {
     99     if (cast_extension_session_ == NULL) {
    100       LOG(ERROR)
    101           << "No CastExtensionSession. Creating session description succeeded.";
    102       return;
    103     }
    104     cast_extension_session_->OnCreateSessionDescription(desc);
    105   }
    106   virtual void OnFailure(const std::string& error) OVERRIDE {
    107     if (cast_extension_session_ == NULL) {
    108       LOG(ERROR)
    109           << "No CastExtensionSession. Creating session description failed.";
    110       return;
    111     }
    112     cast_extension_session_->OnCreateSessionDescriptionFailure(error);
    113   }
    114   void SetCastExtensionSession(CastExtensionSession* cast_extension_session) {
    115     cast_extension_session_ = cast_extension_session;
    116   }
    117 
    118  protected:
    119   explicit CastCreateSessionDescriptionObserver(CastExtensionSession* session)
    120       : cast_extension_session_(session) {}
    121   virtual ~CastCreateSessionDescriptionObserver() {}
    122 
    123  private:
    124   CastExtensionSession* cast_extension_session_;
    125 
    126   DISALLOW_COPY_AND_ASSIGN(CastCreateSessionDescriptionObserver);
    127 };
    128 
    129 // A webrtc::StatsObserver implementation used to receive statistics about the
    130 // current PeerConnection.
    131 class CastStatsObserver : public webrtc::StatsObserver {
    132  public:
    133   static CastStatsObserver* Create() {
    134     return new rtc::RefCountedObject<CastStatsObserver>();
    135   }
    136 
    137   virtual void OnComplete(
    138       const std::vector<webrtc::StatsReport>& reports) OVERRIDE {
    139     typedef webrtc::StatsReport::Values::iterator ValuesIterator;
    140 
    141     VLOG(1) << "Received " << reports.size() << " new StatsReports.";
    142 
    143     int index;
    144     std::vector<webrtc::StatsReport>::const_iterator it;
    145     for (it = reports.begin(), index = 0; it != reports.end(); ++it, ++index) {
    146       webrtc::StatsReport::Values v = it->values;
    147       VLOG(1) << "Report " << index << ":";
    148       for (ValuesIterator vIt = v.begin(); vIt != v.end(); ++vIt) {
    149         VLOG(1) << "Stat: " << vIt->name << "=" << vIt->value << ".";
    150       }
    151     }
    152   }
    153 
    154  protected:
    155   CastStatsObserver() {}
    156   virtual ~CastStatsObserver() {}
    157 
    158   DISALLOW_COPY_AND_ASSIGN(CastStatsObserver);
    159 };
    160 
    161 // TODO(aiguha): Fix PeerConnnection-related tear down crash caused by premature
    162 // destruction of cricket::CaptureManager (which occurs on releasing
    163 // |peer_conn_factory_|). See crbug.com/403840.
    164 CastExtensionSession::~CastExtensionSession() {
    165   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    166 
    167   // Explicitly clear |create_session_desc_observer_|'s pointer to |this|,
    168   // since the CastExtensionSession is destructing. Otherwise,
    169   // |create_session_desc_observer_| would be left with a dangling pointer.
    170   create_session_desc_observer_->SetCastExtensionSession(NULL);
    171 
    172   CleanupPeerConnection();
    173 }
    174 
    175 // static
    176 scoped_ptr<CastExtensionSession> CastExtensionSession::Create(
    177     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    178     scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
    179     const protocol::NetworkSettings& network_settings,
    180     ClientSessionControl* client_session_control,
    181     protocol::ClientStub* client_stub) {
    182   scoped_ptr<CastExtensionSession> cast_extension_session(
    183       new CastExtensionSession(caller_task_runner,
    184                                url_request_context_getter,
    185                                network_settings,
    186                                client_session_control,
    187                                client_stub));
    188   if (!cast_extension_session->WrapTasksAndSave()) {
    189     return scoped_ptr<CastExtensionSession>();
    190   }
    191   if (!cast_extension_session->InitializePeerConnection()) {
    192     return scoped_ptr<CastExtensionSession>();
    193   }
    194   return cast_extension_session.Pass();
    195 }
    196 
    197 void CastExtensionSession::OnCreateSessionDescription(
    198     webrtc::SessionDescriptionInterface* desc) {
    199   if (!caller_task_runner_->BelongsToCurrentThread()) {
    200     caller_task_runner_->PostTask(
    201         FROM_HERE,
    202         base::Bind(&CastExtensionSession::OnCreateSessionDescription,
    203                    base::Unretained(this),
    204                    desc));
    205     return;
    206   }
    207 
    208   peer_connection_->SetLocalDescription(
    209       CastSetSessionDescriptionObserver::Create(), desc);
    210 
    211   scoped_ptr<base::DictionaryValue> json(new base::DictionaryValue());
    212   json->SetString(kWebRtcSessionDescType, desc->type());
    213   std::string subject =
    214       (desc->type() == "offer") ? kSubjectOffer : kSubjectAnswer;
    215   std::string desc_str;
    216   desc->ToString(&desc_str);
    217   json->SetString(kWebRtcSessionDescSDP, desc_str);
    218   std::string json_str;
    219   if (!base::JSONWriter::Write(json.get(), &json_str)) {
    220     LOG(ERROR) << "Failed to serialize sdp message.";
    221     return;
    222   }
    223 
    224   SendMessageToClient(subject.c_str(), json_str);
    225 }
    226 
    227 void CastExtensionSession::OnCreateSessionDescriptionFailure(
    228     const std::string& error) {
    229   VLOG(1) << "Creating Session Description failed: " << error;
    230 }
    231 
    232 // TODO(aiguha): Support the case(s) where we've grabbed the capturer already,
    233 // but another extension reset the video pipeline. We should remove the
    234 // stream from the peer connection here, and then attempt to re-setup the
    235 // peer connection in the OnRenegotiationNeeded() callback.
    236 // See crbug.com/403843.
    237 void CastExtensionSession::OnCreateVideoCapturer(
    238     scoped_ptr<webrtc::DesktopCapturer>* capturer) {
    239   if (has_grabbed_capturer_) {
    240     LOG(ERROR) << "The video pipeline was reset unexpectedly.";
    241     has_grabbed_capturer_ = false;
    242     peer_connection_->RemoveStream(stream_.release());
    243     return;
    244   }
    245 
    246   if (received_offer_) {
    247     has_grabbed_capturer_ = true;
    248     if (SetupVideoStream(capturer->Pass())) {
    249       peer_connection_->CreateAnswer(create_session_desc_observer_, NULL);
    250     } else {
    251       has_grabbed_capturer_ = false;
    252       // Ignore the received offer, since we failed to setup a video stream.
    253       received_offer_ = false;
    254     }
    255     return;
    256   }
    257 }
    258 
    259 bool CastExtensionSession::ModifiesVideoPipeline() const {
    260   return true;
    261 }
    262 
    263 // Returns true if the |message| is a Cast ExtensionMessage, even if
    264 // it was badly formed or a resulting action failed. This is done so that
    265 // the host does not continue to attempt to pass |message| to other
    266 // HostExtensionSessions.
    267 bool CastExtensionSession::OnExtensionMessage(
    268     ClientSessionControl* client_session_control,
    269     protocol::ClientStub* client_stub,
    270     const protocol::ExtensionMessage& message) {
    271   if (message.type() != kExtensionMessageType) {
    272     return false;
    273   }
    274 
    275   scoped_ptr<base::Value> value(base::JSONReader::Read(message.data()));
    276   base::DictionaryValue* client_message;
    277   if (!(value && value->GetAsDictionary(&client_message))) {
    278     LOG(ERROR) << "Could not read cast extension message.";
    279     return true;
    280   }
    281 
    282   std::string subject;
    283   if (!client_message->GetString(kTopLevelSubject, &subject)) {
    284     LOG(ERROR) << "Invalid Cast Extension Message (missing subject header).";
    285     return true;
    286   }
    287 
    288   if (subject == kSubjectOffer && !received_offer_) {
    289     // Reset the video pipeline so we can grab the screen capturer and setup
    290     // a video stream.
    291     if (ParseAndSetRemoteDescription(client_message)) {
    292       received_offer_ = true;
    293       LOG(INFO) << "About to ResetVideoPipeline.";
    294       client_session_control_->ResetVideoPipeline();
    295 
    296     }
    297   } else if (subject == kSubjectAnswer) {
    298     ParseAndSetRemoteDescription(client_message);
    299   } else if (subject == kSubjectNewCandidate) {
    300     ParseAndAddICECandidate(client_message);
    301   } else {
    302     VLOG(1) << "Unexpected CastExtension Message: " << message.data();
    303   }
    304   return true;
    305 }
    306 
    307 // Private methods ------------------------------------------------------------
    308 
    309 CastExtensionSession::CastExtensionSession(
    310     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    311     scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
    312     const protocol::NetworkSettings& network_settings,
    313     ClientSessionControl* client_session_control,
    314     protocol::ClientStub* client_stub)
    315     : caller_task_runner_(caller_task_runner),
    316       url_request_context_getter_(url_request_context_getter),
    317       network_settings_(network_settings),
    318       client_session_control_(client_session_control),
    319       client_stub_(client_stub),
    320       stats_observer_(CastStatsObserver::Create()),
    321       received_offer_(false),
    322       has_grabbed_capturer_(false),
    323       signaling_thread_wrapper_(NULL),
    324       worker_thread_wrapper_(NULL),
    325       worker_thread_(kWorkerThreadName) {
    326   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    327   DCHECK(url_request_context_getter_.get());
    328   DCHECK(client_session_control_);
    329   DCHECK(client_stub_);
    330 
    331   // The worker thread is created with base::MessageLoop::TYPE_IO because
    332   // the PeerConnection performs some port allocation operations on this thread
    333   // that require it. See crbug.com/404013.
    334   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
    335   worker_thread_.StartWithOptions(options);
    336   worker_task_runner_ = worker_thread_.task_runner();
    337 }
    338 
    339 bool CastExtensionSession::ParseAndSetRemoteDescription(
    340     base::DictionaryValue* message) {
    341   DCHECK(peer_connection_.get() != NULL);
    342 
    343   base::DictionaryValue* message_data;
    344   if (!message->GetDictionary(kTopLevelData, &message_data)) {
    345     LOG(ERROR) << "Invalid Cast Extension Message (missing data).";
    346     return false;
    347   }
    348 
    349   std::string webrtc_type;
    350   if (!message_data->GetString(kWebRtcSessionDescType, &webrtc_type)) {
    351     LOG(ERROR)
    352         << "Invalid Cast Extension Message (missing webrtc type header).";
    353     return false;
    354   }
    355 
    356   std::string sdp;
    357   if (!message_data->GetString(kWebRtcSessionDescSDP, &sdp)) {
    358     LOG(ERROR) << "Invalid Cast Extension Message (missing webrtc sdp header).";
    359     return false;
    360   }
    361 
    362   webrtc::SdpParseError error;
    363   webrtc::SessionDescriptionInterface* session_description(
    364       webrtc::CreateSessionDescription(webrtc_type, sdp, &error));
    365 
    366   if (!session_description) {
    367     LOG(ERROR) << "Invalid Cast Extension Message (could not parse sdp).";
    368     VLOG(1) << "SdpParseError was: " << error.description;
    369     return false;
    370   }
    371 
    372   peer_connection_->SetRemoteDescription(
    373       CastSetSessionDescriptionObserver::Create(), session_description);
    374   return true;
    375 }
    376 
    377 bool CastExtensionSession::ParseAndAddICECandidate(
    378     base::DictionaryValue* message) {
    379   DCHECK(peer_connection_.get() != NULL);
    380 
    381   base::DictionaryValue* message_data;
    382   if (!message->GetDictionary(kTopLevelData, &message_data)) {
    383     LOG(ERROR) << "Invalid Cast Extension Message (missing data).";
    384     return false;
    385   }
    386 
    387   std::string candidate_str;
    388   std::string sdp_mid;
    389   int sdp_mlineindex = 0;
    390   if (!message_data->GetString(kWebRtcSDPMid, &sdp_mid) ||
    391       !message_data->GetInteger(kWebRtcSDPMLineIndex, &sdp_mlineindex) ||
    392       !message_data->GetString(kWebRtcCandidate, &candidate_str)) {
    393     LOG(ERROR) << "Invalid Cast Extension Message (could not parse).";
    394     return false;
    395   }
    396 
    397   rtc::scoped_ptr<webrtc::IceCandidateInterface> candidate(
    398       webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, candidate_str));
    399   if (!candidate.get()) {
    400     LOG(ERROR)
    401         << "Invalid Cast Extension Message (could not create candidate).";
    402     return false;
    403   }
    404 
    405   if (!peer_connection_->AddIceCandidate(candidate.get())) {
    406     LOG(ERROR) << "Failed to apply received ICE Candidate to PeerConnection.";
    407     return false;
    408   }
    409 
    410   VLOG(1) << "Received and Added ICE Candidate: " << candidate_str;
    411 
    412   return true;
    413 }
    414 
    415 bool CastExtensionSession::SendMessageToClient(const std::string& subject,
    416                                                const std::string& data) {
    417   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    418 
    419   if (client_stub_ == NULL) {
    420     LOG(ERROR) << "No Client Stub. Cannot send message to client.";
    421     return false;
    422   }
    423 
    424   base::DictionaryValue message_dict;
    425   message_dict.SetString(kTopLevelSubject, subject);
    426   message_dict.SetString(kTopLevelData, data);
    427   std::string message_json;
    428 
    429   if (!base::JSONWriter::Write(&message_dict, &message_json)) {
    430     LOG(ERROR) << "Failed to serialize JSON message.";
    431     return false;
    432   }
    433 
    434   protocol::ExtensionMessage message;
    435   message.set_type(kExtensionMessageType);
    436   message.set_data(message_json);
    437   client_stub_->DeliverHostMessage(message);
    438   return true;
    439 }
    440 
    441 void CastExtensionSession::EnsureTaskAndSetSend(rtc::Thread** ptr,
    442                                                 base::WaitableEvent* event) {
    443   jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
    444   jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true);
    445   *ptr = jingle_glue::JingleThreadWrapper::current();
    446 
    447   if (event != NULL) {
    448     event->Signal();
    449   }
    450 }
    451 
    452 bool CastExtensionSession::WrapTasksAndSave() {
    453   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    454 
    455   EnsureTaskAndSetSend(&signaling_thread_wrapper_);
    456   if (signaling_thread_wrapper_ == NULL)
    457     return false;
    458 
    459   base::WaitableEvent wrap_worker_thread_event(true, false);
    460   worker_task_runner_->PostTask(
    461       FROM_HERE,
    462       base::Bind(&CastExtensionSession::EnsureTaskAndSetSend,
    463                  base::Unretained(this),
    464                  &worker_thread_wrapper_,
    465                  &wrap_worker_thread_event));
    466   wrap_worker_thread_event.Wait();
    467 
    468   return (worker_thread_wrapper_ != NULL);
    469 }
    470 
    471 bool CastExtensionSession::InitializePeerConnection() {
    472   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    473   DCHECK(!peer_conn_factory_);
    474   DCHECK(!peer_connection_);
    475   DCHECK(worker_thread_wrapper_ != NULL);
    476   DCHECK(signaling_thread_wrapper_ != NULL);
    477 
    478   peer_conn_factory_ = webrtc::CreatePeerConnectionFactory(
    479       worker_thread_wrapper_, signaling_thread_wrapper_, NULL, NULL, NULL);
    480 
    481   if (!peer_conn_factory_.get()) {
    482     CleanupPeerConnection();
    483     return false;
    484   }
    485 
    486   VLOG(1) << "Created PeerConnectionFactory successfully.";
    487 
    488   webrtc::PeerConnectionInterface::IceServers servers;
    489   webrtc::PeerConnectionInterface::IceServer server;
    490   server.uri = kDefaultStunURI;
    491   servers.push_back(server);
    492   webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
    493   rtc_config.servers = servers;
    494 
    495   // DTLS-SRTP is the preferred encryption method. If set to kValueFalse, the
    496   // peer connection uses SDES. Disabling SDES as well will cause the peer
    497   // connection to fail to connect.
    498   // Note: For protection and unprotection of SRTP packets, the libjingle
    499   // ENABLE_EXTERNAL_AUTH flag must not be set.
    500   webrtc::FakeConstraints constraints;
    501   constraints.AddMandatory(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
    502                            webrtc::MediaConstraintsInterface::kValueTrue);
    503 
    504   rtc::scoped_refptr<webrtc::PortAllocatorFactoryInterface>
    505       port_allocator_factory = ChromiumPortAllocatorFactory::Create(
    506           network_settings_, url_request_context_getter_);
    507 
    508   peer_connection_ = peer_conn_factory_->CreatePeerConnection(
    509       rtc_config, &constraints, port_allocator_factory, NULL, this);
    510 
    511   if (!peer_connection_.get()) {
    512     CleanupPeerConnection();
    513     return false;
    514   }
    515 
    516   VLOG(1) << "Created PeerConnection successfully.";
    517 
    518   create_session_desc_observer_ =
    519       CastCreateSessionDescriptionObserver::Create(this);
    520 
    521   // Send a test message to the client. Then, notify the client to start
    522   // webrtc offer/answer negotiation.
    523   if (!SendMessageToClient(kSubjectTest, "Hello, client.") ||
    524       !SendMessageToClient(kSubjectReady, "Host ready to receive offers.")) {
    525     LOG(ERROR) << "Failed to send messages to client.";
    526     return false;
    527   }
    528 
    529   return true;
    530 }
    531 
    532 bool CastExtensionSession::SetupVideoStream(
    533     scoped_ptr<webrtc::DesktopCapturer> desktop_capturer) {
    534   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    535   DCHECK(desktop_capturer);
    536 
    537   if (stream_) {
    538     VLOG(1) << "Already added MediaStream. Aborting Setup.";
    539     return false;
    540   }
    541 
    542   scoped_ptr<CastVideoCapturerAdapter> cast_video_capturer_adapter(
    543       new CastVideoCapturerAdapter(desktop_capturer.Pass()));
    544 
    545   // Set video stream constraints.
    546   webrtc::FakeConstraints video_constraints;
    547   video_constraints.AddMandatory(
    548       webrtc::MediaConstraintsInterface::kMinFrameRate, kMinFramesPerSecond);
    549 
    550   rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track =
    551       peer_conn_factory_->CreateVideoTrack(
    552           kVideoLabel,
    553           peer_conn_factory_->CreateVideoSource(
    554               cast_video_capturer_adapter.release(), &video_constraints));
    555 
    556   stream_ = peer_conn_factory_->CreateLocalMediaStream(kStreamLabel);
    557 
    558   if (!stream_->AddTrack(video_track) ||
    559       !peer_connection_->AddStream(stream_, NULL)) {
    560     return false;
    561   }
    562 
    563   VLOG(1) << "Setup video stream successfully.";
    564 
    565   return true;
    566 }
    567 
    568 void CastExtensionSession::PollPeerConnectionStats() {
    569   if (!connection_active()) {
    570     VLOG(1) << "Cannot poll stats while PeerConnection is inactive.";
    571   }
    572   rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> video_track =
    573       stream_->FindVideoTrack(kVideoLabel);
    574   peer_connection_->GetStats(
    575       stats_observer_,
    576       video_track.release(),
    577       webrtc::PeerConnectionInterface::kStatsOutputLevelStandard);
    578 }
    579 
    580 void CastExtensionSession::CleanupPeerConnection() {
    581   peer_connection_->Close();
    582   peer_connection_ = NULL;
    583   stream_ = NULL;
    584   peer_conn_factory_ = NULL;
    585   worker_thread_.Stop();
    586 }
    587 
    588 bool CastExtensionSession::connection_active() const {
    589   return peer_connection_.get() != NULL;
    590 }
    591 
    592 // webrtc::PeerConnectionObserver implementation -------------------------------
    593 
    594 void CastExtensionSession::OnError() {
    595   VLOG(1) << "PeerConnectionObserver: an error occurred.";
    596 }
    597 
    598 void CastExtensionSession::OnSignalingChange(
    599     webrtc::PeerConnectionInterface::SignalingState new_state) {
    600   VLOG(1) << "PeerConnectionObserver: SignalingState changed to:" << new_state;
    601 }
    602 
    603 void CastExtensionSession::OnStateChange(
    604     webrtc::PeerConnectionObserver::StateType state_changed) {
    605   VLOG(1) << "PeerConnectionObserver: StateType changed to: " << state_changed;
    606 }
    607 
    608 void CastExtensionSession::OnAddStream(webrtc::MediaStreamInterface* stream) {
    609   VLOG(1) << "PeerConnectionObserver: stream added: " << stream->label();
    610 }
    611 
    612 void CastExtensionSession::OnRemoveStream(
    613     webrtc::MediaStreamInterface* stream) {
    614   VLOG(1) << "PeerConnectionObserver: stream removed: " << stream->label();
    615 }
    616 
    617 void CastExtensionSession::OnDataChannel(
    618     webrtc::DataChannelInterface* data_channel) {
    619   VLOG(1) << "PeerConnectionObserver: data channel: " << data_channel->label();
    620 }
    621 
    622 void CastExtensionSession::OnRenegotiationNeeded() {
    623   VLOG(1) << "PeerConnectionObserver: renegotiation needed.";
    624 }
    625 
    626 void CastExtensionSession::OnIceConnectionChange(
    627     webrtc::PeerConnectionInterface::IceConnectionState new_state) {
    628   VLOG(1) << "PeerConnectionObserver: IceConnectionState changed to: "
    629           << new_state;
    630 
    631   // TODO(aiguha): Maybe start timer only if enabled by command-line flag or
    632   // at a particular verbosity level.
    633   if (!stats_polling_timer_.IsRunning() &&
    634       new_state == webrtc::PeerConnectionInterface::kIceConnectionConnected) {
    635     stats_polling_timer_.Start(
    636         FROM_HERE,
    637         base::TimeDelta::FromSeconds(kStatsLogIntervalSec),
    638         this,
    639         &CastExtensionSession::PollPeerConnectionStats);
    640   }
    641 }
    642 
    643 void CastExtensionSession::OnIceGatheringChange(
    644     webrtc::PeerConnectionInterface::IceGatheringState new_state) {
    645   VLOG(1) << "PeerConnectionObserver: IceGatheringState changed to: "
    646           << new_state;
    647 }
    648 
    649 void CastExtensionSession::OnIceComplete() {
    650   VLOG(1) << "PeerConnectionObserver: all ICE candidates found.";
    651 }
    652 
    653 void CastExtensionSession::OnIceCandidate(
    654     const webrtc::IceCandidateInterface* candidate) {
    655   std::string candidate_str;
    656   if (!candidate->ToString(&candidate_str)) {
    657     LOG(ERROR) << "PeerConnectionObserver: failed to serialize candidate.";
    658     return;
    659   }
    660   scoped_ptr<base::DictionaryValue> json(new base::DictionaryValue());
    661   json->SetString(kWebRtcSDPMid, candidate->sdp_mid());
    662   json->SetInteger(kWebRtcSDPMLineIndex, candidate->sdp_mline_index());
    663   json->SetString(kWebRtcCandidate, candidate_str);
    664   std::string json_str;
    665   if (!base::JSONWriter::Write(json.get(), &json_str)) {
    666     LOG(ERROR) << "Failed to serialize candidate message.";
    667     return;
    668   }
    669   SendMessageToClient(kSubjectNewCandidate, json_str);
    670 }
    671 
    672 }  // namespace remoting
    673