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