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:
Create()39   static DummySetSessionDescriptionObserver* Create() {
40     return
41         new rtc::RefCountedObject<DummySetSessionDescriptionObserver>();
42   }
OnSuccess()43   virtual void OnSuccess() {
44     LOG(INFO) << __FUNCTION__;
45   }
OnFailure(const std::string & error)46   virtual void OnFailure(const std::string& error) {
47     LOG(INFO) << __FUNCTION__ << " " << error;
48   }
49 
50  protected:
DummySetSessionDescriptionObserver()51   DummySetSessionDescriptionObserver() {}
~DummySetSessionDescriptionObserver()52   ~DummySetSessionDescriptionObserver() {}
53 };
54 
Conductor(PeerConnectionClient * client,MainWindow * main_wnd)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 
~Conductor()64 Conductor::~Conductor() {
65   ASSERT(peer_connection_.get() == NULL);
66 }
67 
connection_active() const68 bool Conductor::connection_active() const {
69   return peer_connection_.get() != NULL;
70 }
71 
Close()72 void Conductor::Close() {
73   client_->SignOut();
74   DeletePeerConnection();
75 }
76 
InitializePeerConnection()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 
ReinitializePeerConnectionForLoopback()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 
CreatePeerConnection(bool dtls)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 
DeletePeerConnection()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 
EnsureStreamingUI()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
OnAddStream(webrtc::MediaStreamInterface * stream)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 
OnRemoveStream(webrtc::MediaStreamInterface * stream)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 
OnIceCandidate(const webrtc::IceCandidateInterface * candidate)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 
OnSignedIn()201 void Conductor::OnSignedIn() {
202   LOG(INFO) << __FUNCTION__;
203   main_wnd_->SwitchToPeerList(client_->peers());
204 }
205 
OnDisconnected()206 void Conductor::OnDisconnected() {
207   LOG(INFO) << __FUNCTION__;
208 
209   DeletePeerConnection();
210 
211   if (main_wnd_->IsWindow())
212     main_wnd_->SwitchToConnectUI();
213 }
214 
OnPeerConnected(int id,const std::string & name)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 
OnPeerDisconnected(int id)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 
OnMessageFromPeer(int peer_id,const std::string & message)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 
OnMessageSent(int err)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 
OnServerConnectionFailure()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 
StartLogin(const std::string & server,int port)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 
DisconnectFromServer()348 void Conductor::DisconnectFromServer() {
349   if (client_->is_connected())
350     client_->SignOut();
351 }
352 
ConnectToPeer(int peer_id)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 
OpenVideoCaptureDevice()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 
AddStreams()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 
DisconnectFromCurrentPeer()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 
UIThreadCallback(int msg_id,void * data)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 
OnSuccess(webrtc::SessionDescriptionInterface * desc)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 
OnFailure(const std::string & error)533 void Conductor::OnFailure(const std::string& error) {
534     LOG(LERROR) << error;
535 }
536 
SendMessage(const std::string & json_object)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