Home | History | Annotate | Download | only in client
      1 /*
      2  *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/examples/peerconnection/client/conductor.h"
     12 
     13 #include <utility>
     14 #include <vector>
     15 
     16 #include "talk/app/webrtc/videosourceinterface.h"
     17 #include "webrtc/examples/peerconnection/client/defaults.h"
     18 #include "talk/media/devices/devicemanager.h"
     19 #include "talk/app/webrtc/test/fakeconstraints.h"
     20 #include "webrtc/base/common.h"
     21 #include "webrtc/base/json.h"
     22 #include "webrtc/base/logging.h"
     23 
     24 // Names used for a IceCandidate JSON object.
     25 const char kCandidateSdpMidName[] = "sdpMid";
     26 const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex";
     27 const char kCandidateSdpName[] = "candidate";
     28 
     29 // Names used for a SessionDescription JSON object.
     30 const char kSessionDescriptionTypeName[] = "type";
     31 const char kSessionDescriptionSdpName[] = "sdp";
     32 
     33 #define DTLS_ON  true
     34 #define DTLS_OFF false
     35 
     36 class DummySetSessionDescriptionObserver
     37     : public webrtc::SetSessionDescriptionObserver {
     38  public:
     39   static DummySetSessionDescriptionObserver* Create() {
     40     return
     41         new rtc::RefCountedObject<DummySetSessionDescriptionObserver>();
     42   }
     43   virtual void OnSuccess() {
     44     LOG(INFO) << __FUNCTION__;
     45   }
     46   virtual void OnFailure(const std::string& error) {
     47     LOG(INFO) << __FUNCTION__ << " " << error;
     48   }
     49 
     50  protected:
     51   DummySetSessionDescriptionObserver() {}
     52   ~DummySetSessionDescriptionObserver() {}
     53 };
     54 
     55 Conductor::Conductor(PeerConnectionClient* client, MainWindow* main_wnd)
     56   : peer_id_(-1),
     57     loopback_(false),
     58     client_(client),
     59     main_wnd_(main_wnd) {
     60   client_->RegisterObserver(this);
     61   main_wnd->RegisterObserver(this);
     62 }
     63 
     64 Conductor::~Conductor() {
     65   ASSERT(peer_connection_.get() == NULL);
     66 }
     67 
     68 bool Conductor::connection_active() const {
     69   return peer_connection_.get() != NULL;
     70 }
     71 
     72 void Conductor::Close() {
     73   client_->SignOut();
     74   DeletePeerConnection();
     75 }
     76 
     77 bool Conductor::InitializePeerConnection() {
     78   ASSERT(peer_connection_factory_.get() == NULL);
     79   ASSERT(peer_connection_.get() == NULL);
     80 
     81   peer_connection_factory_  = webrtc::CreatePeerConnectionFactory();
     82 
     83   if (!peer_connection_factory_.get()) {
     84     main_wnd_->MessageBox("Error",
     85         "Failed to initialize PeerConnectionFactory", true);
     86     DeletePeerConnection();
     87     return false;
     88   }
     89 
     90   if (!CreatePeerConnection(DTLS_ON)) {
     91     main_wnd_->MessageBox("Error",
     92         "CreatePeerConnection failed", true);
     93     DeletePeerConnection();
     94   }
     95   AddStreams();
     96   return peer_connection_.get() != NULL;
     97 }
     98 
     99 bool Conductor::ReinitializePeerConnectionForLoopback() {
    100   loopback_ = true;
    101   rtc::scoped_refptr<webrtc::StreamCollectionInterface> streams(
    102       peer_connection_->local_streams());
    103   peer_connection_ = NULL;
    104   if (CreatePeerConnection(DTLS_OFF)) {
    105     for (size_t i = 0; i < streams->count(); ++i)
    106       peer_connection_->AddStream(streams->at(i));
    107     peer_connection_->CreateOffer(this, NULL);
    108   }
    109   return peer_connection_.get() != NULL;
    110 }
    111 
    112 bool Conductor::CreatePeerConnection(bool dtls) {
    113   ASSERT(peer_connection_factory_.get() != NULL);
    114   ASSERT(peer_connection_.get() == NULL);
    115 
    116   webrtc::PeerConnectionInterface::RTCConfiguration config;
    117   webrtc::PeerConnectionInterface::IceServer server;
    118   server.uri = GetPeerConnectionString();
    119   config.servers.push_back(server);
    120 
    121   webrtc::FakeConstraints constraints;
    122   if (dtls) {
    123     constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
    124                             "true");
    125   } else {
    126     constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
    127                             "false");
    128   }
    129 
    130   peer_connection_ = peer_connection_factory_->CreatePeerConnection(
    131       config, &constraints, NULL, NULL, this);
    132   return peer_connection_.get() != NULL;
    133 }
    134 
    135 void Conductor::DeletePeerConnection() {
    136   peer_connection_ = NULL;
    137   active_streams_.clear();
    138   main_wnd_->StopLocalRenderer();
    139   main_wnd_->StopRemoteRenderer();
    140   peer_connection_factory_ = NULL;
    141   peer_id_ = -1;
    142   loopback_ = false;
    143 }
    144 
    145 void Conductor::EnsureStreamingUI() {
    146   ASSERT(peer_connection_.get() != NULL);
    147   if (main_wnd_->IsWindow()) {
    148     if (main_wnd_->current_ui() != MainWindow::STREAMING)
    149       main_wnd_->SwitchToStreamingUI();
    150   }
    151 }
    152 
    153 //
    154 // PeerConnectionObserver implementation.
    155 //
    156 
    157 // Called when a remote stream is added
    158 void Conductor::OnAddStream(webrtc::MediaStreamInterface* stream) {
    159   LOG(INFO) << __FUNCTION__ << " " << stream->label();
    160 
    161   stream->AddRef();
    162   main_wnd_->QueueUIThreadCallback(NEW_STREAM_ADDED,
    163                                    stream);
    164 }
    165 
    166 void Conductor::OnRemoveStream(webrtc::MediaStreamInterface* stream) {
    167   LOG(INFO) << __FUNCTION__ << " " << stream->label();
    168   stream->AddRef();
    169   main_wnd_->QueueUIThreadCallback(STREAM_REMOVED,
    170                                    stream);
    171 }
    172 
    173 void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
    174   LOG(INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index();
    175   // For loopback test. To save some connecting delay.
    176   if (loopback_) {
    177     if (!peer_connection_->AddIceCandidate(candidate)) {
    178       LOG(WARNING) << "Failed to apply the received candidate";
    179     }
    180     return;
    181   }
    182 
    183   Json::StyledWriter writer;
    184   Json::Value jmessage;
    185 
    186   jmessage[kCandidateSdpMidName] = candidate->sdp_mid();
    187   jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index();
    188   std::string sdp;
    189   if (!candidate->ToString(&sdp)) {
    190     LOG(LS_ERROR) << "Failed to serialize candidate";
    191     return;
    192   }
    193   jmessage[kCandidateSdpName] = sdp;
    194   SendMessage(writer.write(jmessage));
    195 }
    196 
    197 //
    198 // PeerConnectionClientObserver implementation.
    199 //
    200 
    201 void Conductor::OnSignedIn() {
    202   LOG(INFO) << __FUNCTION__;
    203   main_wnd_->SwitchToPeerList(client_->peers());
    204 }
    205 
    206 void Conductor::OnDisconnected() {
    207   LOG(INFO) << __FUNCTION__;
    208 
    209   DeletePeerConnection();
    210 
    211   if (main_wnd_->IsWindow())
    212     main_wnd_->SwitchToConnectUI();
    213 }
    214 
    215 void Conductor::OnPeerConnected(int id, const std::string& name) {
    216   LOG(INFO) << __FUNCTION__;
    217   // Refresh the list if we're showing it.
    218   if (main_wnd_->current_ui() == MainWindow::LIST_PEERS)
    219     main_wnd_->SwitchToPeerList(client_->peers());
    220 }
    221 
    222 void Conductor::OnPeerDisconnected(int id) {
    223   LOG(INFO) << __FUNCTION__;
    224   if (id == peer_id_) {
    225     LOG(INFO) << "Our peer disconnected";
    226     main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_CLOSED, NULL);
    227   } else {
    228     // Refresh the list if we're showing it.
    229     if (main_wnd_->current_ui() == MainWindow::LIST_PEERS)
    230       main_wnd_->SwitchToPeerList(client_->peers());
    231   }
    232 }
    233 
    234 void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) {
    235   ASSERT(peer_id_ == peer_id || peer_id_ == -1);
    236   ASSERT(!message.empty());
    237 
    238   if (!peer_connection_.get()) {
    239     ASSERT(peer_id_ == -1);
    240     peer_id_ = peer_id;
    241 
    242     if (!InitializePeerConnection()) {
    243       LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance";
    244       client_->SignOut();
    245       return;
    246     }
    247   } else if (peer_id != peer_id_) {
    248     ASSERT(peer_id_ != -1);
    249     LOG(WARNING) << "Received a message from unknown peer while already in a "
    250                     "conversation with a different peer.";
    251     return;
    252   }
    253 
    254   Json::Reader reader;
    255   Json::Value jmessage;
    256   if (!reader.parse(message, jmessage)) {
    257     LOG(WARNING) << "Received unknown message. " << message;
    258     return;
    259   }
    260   std::string type;
    261   std::string json_object;
    262 
    263   rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type);
    264   if (!type.empty()) {
    265     if (type == "offer-loopback") {
    266       // This is a loopback call.
    267       // Recreate the peerconnection with DTLS disabled.
    268       if (!ReinitializePeerConnectionForLoopback()) {
    269         LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance";
    270         DeletePeerConnection();
    271         client_->SignOut();
    272       }
    273       return;
    274     }
    275 
    276     std::string sdp;
    277     if (!rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName,
    278                                       &sdp)) {
    279       LOG(WARNING) << "Can't parse received session description message.";
    280       return;
    281     }
    282     webrtc::SdpParseError error;
    283     webrtc::SessionDescriptionInterface* session_description(
    284         webrtc::CreateSessionDescription(type, sdp, &error));
    285     if (!session_description) {
    286       LOG(WARNING) << "Can't parse received session description message. "
    287           << "SdpParseError was: " << error.description;
    288       return;
    289     }
    290     LOG(INFO) << " Received session description :" << message;
    291     peer_connection_->SetRemoteDescription(
    292         DummySetSessionDescriptionObserver::Create(), session_description);
    293     if (session_description->type() ==
    294         webrtc::SessionDescriptionInterface::kOffer) {
    295       peer_connection_->CreateAnswer(this, NULL);
    296     }
    297     return;
    298   } else {
    299     std::string sdp_mid;
    300     int sdp_mlineindex = 0;
    301     std::string sdp;
    302     if (!rtc::GetStringFromJsonObject(jmessage, kCandidateSdpMidName,
    303                                       &sdp_mid) ||
    304         !rtc::GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName,
    305                                    &sdp_mlineindex) ||
    306         !rtc::GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) {
    307       LOG(WARNING) << "Can't parse received message.";
    308       return;
    309     }
    310     webrtc::SdpParseError error;
    311     rtc::scoped_ptr<webrtc::IceCandidateInterface> candidate(
    312         webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error));
    313     if (!candidate.get()) {
    314       LOG(WARNING) << "Can't parse received candidate message. "
    315           << "SdpParseError was: " << error.description;
    316       return;
    317     }
    318     if (!peer_connection_->AddIceCandidate(candidate.get())) {
    319       LOG(WARNING) << "Failed to apply the received candidate";
    320       return;
    321     }
    322     LOG(INFO) << " Received candidate :" << message;
    323     return;
    324   }
    325 }
    326 
    327 void Conductor::OnMessageSent(int err) {
    328   // Process the next pending message if any.
    329   main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, NULL);
    330 }
    331 
    332 void Conductor::OnServerConnectionFailure() {
    333     main_wnd_->MessageBox("Error", ("Failed to connect to " + server_).c_str(),
    334                           true);
    335 }
    336 
    337 //
    338 // MainWndCallback implementation.
    339 //
    340 
    341 void Conductor::StartLogin(const std::string& server, int port) {
    342   if (client_->is_connected())
    343     return;
    344   server_ = server;
    345   client_->Connect(server, port, GetPeerName());
    346 }
    347 
    348 void Conductor::DisconnectFromServer() {
    349   if (client_->is_connected())
    350     client_->SignOut();
    351 }
    352 
    353 void Conductor::ConnectToPeer(int peer_id) {
    354   ASSERT(peer_id_ == -1);
    355   ASSERT(peer_id != -1);
    356 
    357   if (peer_connection_.get()) {
    358     main_wnd_->MessageBox("Error",
    359         "We only support connecting to one peer at a time", true);
    360     return;
    361   }
    362 
    363   if (InitializePeerConnection()) {
    364     peer_id_ = peer_id;
    365     peer_connection_->CreateOffer(this, NULL);
    366   } else {
    367     main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
    368   }
    369 }
    370 
    371 cricket::VideoCapturer* Conductor::OpenVideoCaptureDevice() {
    372   rtc::scoped_ptr<cricket::DeviceManagerInterface> dev_manager(
    373       cricket::DeviceManagerFactory::Create());
    374   if (!dev_manager->Init()) {
    375     LOG(LS_ERROR) << "Can't create device manager";
    376     return NULL;
    377   }
    378   std::vector<cricket::Device> devs;
    379   if (!dev_manager->GetVideoCaptureDevices(&devs)) {
    380     LOG(LS_ERROR) << "Can't enumerate video devices";
    381     return NULL;
    382   }
    383   std::vector<cricket::Device>::iterator dev_it = devs.begin();
    384   cricket::VideoCapturer* capturer = NULL;
    385   for (; dev_it != devs.end(); ++dev_it) {
    386     capturer = dev_manager->CreateVideoCapturer(*dev_it);
    387     if (capturer != NULL)
    388       break;
    389   }
    390   return capturer;
    391 }
    392 
    393 void Conductor::AddStreams() {
    394   if (active_streams_.find(kStreamLabel) != active_streams_.end())
    395     return;  // Already added.
    396 
    397   rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
    398       peer_connection_factory_->CreateAudioTrack(
    399           kAudioLabel, peer_connection_factory_->CreateAudioSource(NULL)));
    400 
    401   rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
    402       peer_connection_factory_->CreateVideoTrack(
    403           kVideoLabel,
    404           peer_connection_factory_->CreateVideoSource(OpenVideoCaptureDevice(),
    405                                                       NULL)));
    406   main_wnd_->StartLocalRenderer(video_track);
    407 
    408   rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
    409       peer_connection_factory_->CreateLocalMediaStream(kStreamLabel);
    410 
    411   stream->AddTrack(audio_track);
    412   stream->AddTrack(video_track);
    413   if (!peer_connection_->AddStream(stream)) {
    414     LOG(LS_ERROR) << "Adding stream to PeerConnection failed";
    415   }
    416   typedef std::pair<std::string,
    417                     rtc::scoped_refptr<webrtc::MediaStreamInterface> >
    418       MediaStreamPair;
    419   active_streams_.insert(MediaStreamPair(stream->label(), stream));
    420   main_wnd_->SwitchToStreamingUI();
    421 }
    422 
    423 void Conductor::DisconnectFromCurrentPeer() {
    424   LOG(INFO) << __FUNCTION__;
    425   if (peer_connection_.get()) {
    426     client_->SendHangUp(peer_id_);
    427     DeletePeerConnection();
    428   }
    429 
    430   if (main_wnd_->IsWindow())
    431     main_wnd_->SwitchToPeerList(client_->peers());
    432 }
    433 
    434 void Conductor::UIThreadCallback(int msg_id, void* data) {
    435   switch (msg_id) {
    436     case PEER_CONNECTION_CLOSED:
    437       LOG(INFO) << "PEER_CONNECTION_CLOSED";
    438       DeletePeerConnection();
    439 
    440       ASSERT(active_streams_.empty());
    441 
    442       if (main_wnd_->IsWindow()) {
    443         if (client_->is_connected()) {
    444           main_wnd_->SwitchToPeerList(client_->peers());
    445         } else {
    446           main_wnd_->SwitchToConnectUI();
    447         }
    448       } else {
    449         DisconnectFromServer();
    450       }
    451       break;
    452 
    453     case SEND_MESSAGE_TO_PEER: {
    454       LOG(INFO) << "SEND_MESSAGE_TO_PEER";
    455       std::string* msg = reinterpret_cast<std::string*>(data);
    456       if (msg) {
    457         // For convenience, we always run the message through the queue.
    458         // This way we can be sure that messages are sent to the server
    459         // in the same order they were signaled without much hassle.
    460         pending_messages_.push_back(msg);
    461       }
    462 
    463       if (!pending_messages_.empty() && !client_->IsSendingMessage()) {
    464         msg = pending_messages_.front();
    465         pending_messages_.pop_front();
    466 
    467         if (!client_->SendToPeer(peer_id_, *msg) && peer_id_ != -1) {
    468           LOG(LS_ERROR) << "SendToPeer failed";
    469           DisconnectFromServer();
    470         }
    471         delete msg;
    472       }
    473 
    474       if (!peer_connection_.get())
    475         peer_id_ = -1;
    476 
    477       break;
    478     }
    479 
    480     case NEW_STREAM_ADDED: {
    481       webrtc::MediaStreamInterface* stream =
    482           reinterpret_cast<webrtc::MediaStreamInterface*>(
    483           data);
    484       webrtc::VideoTrackVector tracks = stream->GetVideoTracks();
    485       // Only render the first track.
    486       if (!tracks.empty()) {
    487         webrtc::VideoTrackInterface* track = tracks[0];
    488         main_wnd_->StartRemoteRenderer(track);
    489       }
    490       stream->Release();
    491       break;
    492     }
    493 
    494     case STREAM_REMOVED: {
    495       // Remote peer stopped sending a stream.
    496       webrtc::MediaStreamInterface* stream =
    497           reinterpret_cast<webrtc::MediaStreamInterface*>(
    498           data);
    499       stream->Release();
    500       break;
    501     }
    502 
    503     default:
    504       ASSERT(false);
    505       break;
    506   }
    507 }
    508 
    509 void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
    510   peer_connection_->SetLocalDescription(
    511       DummySetSessionDescriptionObserver::Create(), desc);
    512 
    513   std::string sdp;
    514   desc->ToString(&sdp);
    515 
    516   // For loopback test. To save some connecting delay.
    517   if (loopback_) {
    518     // Replace message type from "offer" to "answer"
    519     webrtc::SessionDescriptionInterface* session_description(
    520         webrtc::CreateSessionDescription("answer", sdp, nullptr));
    521     peer_connection_->SetRemoteDescription(
    522         DummySetSessionDescriptionObserver::Create(), session_description);
    523     return;
    524   }
    525 
    526   Json::StyledWriter writer;
    527   Json::Value jmessage;
    528   jmessage[kSessionDescriptionTypeName] = desc->type();
    529   jmessage[kSessionDescriptionSdpName] = sdp;
    530   SendMessage(writer.write(jmessage));
    531 }
    532 
    533 void Conductor::OnFailure(const std::string& error) {
    534     LOG(LERROR) << error;
    535 }
    536 
    537 void Conductor::SendMessage(const std::string& json_object) {
    538   std::string* msg = new std::string(json_object);
    539   main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg);
    540 }
    541