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