conductor.cc (21719B)
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 "examples/peerconnection/client/conductor.h" 12 13 #include <cstddef> 14 #include <memory> 15 #include <optional> 16 #include <string> 17 #include <utility> 18 #include <vector> 19 20 #include "absl/base/nullability.h" 21 #include "absl/memory/memory.h" 22 #include "api/audio_codecs/builtin_audio_decoder_factory.h" 23 #include "api/audio_codecs/builtin_audio_encoder_factory.h" 24 #include "api/audio_options.h" 25 #include "api/create_modular_peer_connection_factory.h" 26 #include "api/enable_media.h" 27 #include "api/environment/environment.h" 28 #include "api/jsep.h" 29 #include "api/make_ref_counted.h" 30 #include "api/media_stream_interface.h" 31 #include "api/peer_connection_interface.h" 32 #include "api/rtc_error.h" 33 #include "api/rtp_receiver_interface.h" 34 #include "api/rtp_sender_interface.h" 35 #include "api/scoped_refptr.h" 36 #include "api/task_queue/task_queue_factory.h" 37 #include "api/test/create_frame_generator.h" 38 #include "api/video/video_frame.h" 39 #include "api/video/video_source_interface.h" 40 #include "api/video_codecs/video_decoder_factory_template.h" 41 #include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h" 42 #include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h" 43 #include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h" 44 #include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h" 45 #include "api/video_codecs/video_encoder_factory_template.h" 46 #include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h" 47 #include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h" 48 #include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h" 49 #include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h" 50 #include "examples/peerconnection/client/defaults.h" 51 #include "examples/peerconnection/client/main_wnd.h" 52 #include "examples/peerconnection/client/peer_connection_client.h" 53 #include "json/reader.h" 54 #include "json/value.h" 55 #include "json/writer.h" 56 #include "modules/video_capture/video_capture.h" 57 #include "modules/video_capture/video_capture_factory.h" 58 #include "pc/video_track_source.h" 59 #include "rtc_base/checks.h" 60 #include "rtc_base/logging.h" 61 #include "rtc_base/strings/json.h" 62 #include "rtc_base/thread.h" 63 #include "system_wrappers/include/clock.h" 64 #include "test/frame_generator_capturer.h" 65 #include "test/platform_video_capturer.h" 66 #include "test/test_video_capturer.h" 67 68 namespace { 69 using webrtc::test::TestVideoCapturer; 70 71 // Names used for a IceCandidate JSON object. 72 const char kCandidateSdpMidName[] = "sdpMid"; 73 const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex"; 74 const char kCandidateSdpName[] = "candidate"; 75 76 // Names used for a SessionDescription JSON object. 77 const char kSessionDescriptionTypeName[] = "type"; 78 const char kSessionDescriptionSdpName[] = "sdp"; 79 80 class DummySetSessionDescriptionObserver 81 : public webrtc::SetSessionDescriptionObserver { 82 public: 83 static webrtc::scoped_refptr<DummySetSessionDescriptionObserver> Create() { 84 return webrtc::make_ref_counted<DummySetSessionDescriptionObserver>(); 85 } 86 void OnSuccess() override { RTC_LOG(LS_INFO) << __FUNCTION__; } 87 void OnFailure(webrtc::RTCError error) override { 88 RTC_LOG(LS_INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " 89 << error.message(); 90 } 91 }; 92 93 std::unique_ptr<TestVideoCapturer> CreateCapturer( 94 webrtc::TaskQueueFactory& task_queue_factory) { 95 const size_t kWidth = 640; 96 const size_t kHeight = 480; 97 const size_t kFps = 30; 98 std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> info( 99 webrtc::VideoCaptureFactory::CreateDeviceInfo()); 100 if (!info) { 101 return nullptr; 102 } 103 int num_devices = info->NumberOfDevices(); 104 for (int i = 0; i < num_devices; ++i) { 105 std::unique_ptr<TestVideoCapturer> capturer = 106 webrtc::test::CreateVideoCapturer(kWidth, kHeight, kFps, i); 107 if (capturer) { 108 return capturer; 109 } 110 } 111 auto frame_generator = webrtc::test::CreateSquareFrameGenerator( 112 kWidth, kHeight, std::nullopt, std::nullopt); 113 return std::make_unique<webrtc::test::FrameGeneratorCapturer>( 114 webrtc::Clock::GetRealTimeClock(), std::move(frame_generator), kFps, 115 task_queue_factory); 116 } 117 class CapturerTrackSource : public webrtc::VideoTrackSource { 118 public: 119 static webrtc::scoped_refptr<CapturerTrackSource> Create( 120 webrtc::TaskQueueFactory& task_queue_factory) { 121 std::unique_ptr<TestVideoCapturer> capturer = 122 CreateCapturer(task_queue_factory); 123 if (capturer) { 124 capturer->Start(); 125 return webrtc::make_ref_counted<CapturerTrackSource>(std::move(capturer)); 126 } 127 return nullptr; 128 } 129 130 protected: 131 explicit CapturerTrackSource(std::unique_ptr<TestVideoCapturer> capturer) 132 : VideoTrackSource(/*remote=*/false), capturer_(std::move(capturer)) {} 133 134 private: 135 webrtc::VideoSourceInterface<webrtc::VideoFrame>* source() override { 136 return capturer_.get(); 137 } 138 139 std::unique_ptr<TestVideoCapturer> capturer_; 140 }; 141 142 } // namespace 143 144 Conductor::Conductor(const webrtc::Environment& env, 145 PeerConnectionClient* absl_nonnull client, 146 MainWindow* absl_nonnull main_wnd) 147 : peer_id_(-1), 148 loopback_(false), 149 env_(env), 150 client_(client), 151 main_wnd_(main_wnd) { 152 client_->RegisterObserver(this); 153 main_wnd->RegisterObserver(this); 154 } 155 156 Conductor::~Conductor() { 157 RTC_DCHECK(!peer_connection_); 158 } 159 160 bool Conductor::connection_active() const { 161 return peer_connection_ != nullptr; 162 } 163 164 void Conductor::Close() { 165 client_->SignOut(); 166 DeletePeerConnection(); 167 } 168 169 bool Conductor::InitializePeerConnection() { 170 RTC_DCHECK(!peer_connection_factory_); 171 RTC_DCHECK(!peer_connection_); 172 173 if (!signaling_thread_) { 174 signaling_thread_ = webrtc::Thread::CreateWithSocketServer(); 175 signaling_thread_->Start(); 176 } 177 178 webrtc::PeerConnectionFactoryDependencies deps; 179 deps.signaling_thread = signaling_thread_.get(); 180 deps.env = env_, 181 deps.audio_encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory(); 182 deps.audio_decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); 183 deps.video_encoder_factory = 184 std::make_unique<webrtc::VideoEncoderFactoryTemplate< 185 webrtc::LibvpxVp8EncoderTemplateAdapter, 186 webrtc::LibvpxVp9EncoderTemplateAdapter, 187 webrtc::OpenH264EncoderTemplateAdapter, 188 webrtc::LibaomAv1EncoderTemplateAdapter>>(); 189 deps.video_decoder_factory = 190 std::make_unique<webrtc::VideoDecoderFactoryTemplate< 191 webrtc::LibvpxVp8DecoderTemplateAdapter, 192 webrtc::LibvpxVp9DecoderTemplateAdapter, 193 webrtc::OpenH264DecoderTemplateAdapter, 194 webrtc::Dav1dDecoderTemplateAdapter>>(); 195 webrtc::EnableMedia(deps); 196 peer_connection_factory_ = 197 webrtc::CreateModularPeerConnectionFactory(std::move(deps)); 198 199 if (!peer_connection_factory_) { 200 main_wnd_->MessageBox("Error", "Failed to initialize PeerConnectionFactory", 201 true); 202 DeletePeerConnection(); 203 return false; 204 } 205 206 if (!CreatePeerConnection()) { 207 main_wnd_->MessageBox("Error", "CreatePeerConnection failed", true); 208 DeletePeerConnection(); 209 } 210 211 AddTracks(); 212 213 return peer_connection_ != nullptr; 214 } 215 216 bool Conductor::ReinitializePeerConnectionForLoopback() { 217 loopback_ = true; 218 std::vector<webrtc::scoped_refptr<webrtc::RtpSenderInterface>> senders = 219 peer_connection_->GetSenders(); 220 peer_connection_ = nullptr; 221 // Loopback is only possible if encryption is disabled. 222 webrtc::PeerConnectionFactoryInterface::Options options; 223 options.disable_encryption = true; 224 peer_connection_factory_->SetOptions(options); 225 if (CreatePeerConnection()) { 226 for (const auto& sender : senders) { 227 peer_connection_->AddTrack(sender->track(), sender->stream_ids()); 228 } 229 peer_connection_->CreateOffer( 230 this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); 231 } 232 options.disable_encryption = false; 233 peer_connection_factory_->SetOptions(options); 234 return peer_connection_ != nullptr; 235 } 236 237 bool Conductor::CreatePeerConnection() { 238 RTC_DCHECK(peer_connection_factory_); 239 RTC_DCHECK(!peer_connection_); 240 241 webrtc::PeerConnectionInterface::RTCConfiguration config; 242 config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; 243 webrtc::PeerConnectionInterface::IceServer server; 244 server.uri = GetPeerConnectionString(); 245 config.servers.push_back(server); 246 247 webrtc::PeerConnectionDependencies pc_dependencies(this); 248 auto error_or_peer_connection = 249 peer_connection_factory_->CreatePeerConnectionOrError( 250 config, std::move(pc_dependencies)); 251 if (error_or_peer_connection.ok()) { 252 peer_connection_ = std::move(error_or_peer_connection.value()); 253 } 254 return peer_connection_ != nullptr; 255 } 256 257 void Conductor::DeletePeerConnection() { 258 main_wnd_->StopLocalRenderer(); 259 main_wnd_->StopRemoteRenderer(); 260 peer_connection_ = nullptr; 261 peer_connection_factory_ = nullptr; 262 peer_id_ = -1; 263 loopback_ = false; 264 } 265 266 void Conductor::EnsureStreamingUI() { 267 RTC_DCHECK(peer_connection_); 268 if (main_wnd_->IsWindow()) { 269 if (main_wnd_->current_ui() != MainWindow::STREAMING) 270 main_wnd_->SwitchToStreamingUI(); 271 } 272 } 273 274 // 275 // PeerConnectionObserver implementation. 276 // 277 278 void Conductor::OnAddTrack( 279 webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver, 280 const std::vector<webrtc::scoped_refptr<webrtc::MediaStreamInterface>>& 281 streams) { 282 RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); 283 main_wnd_->QueueUIThreadCallback(NEW_TRACK_ADDED, 284 receiver->track().release()); 285 } 286 287 void Conductor::OnRemoveTrack( 288 webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) { 289 RTC_LOG(LS_INFO) << __FUNCTION__ << " " << receiver->id(); 290 main_wnd_->QueueUIThreadCallback(TRACK_REMOVED, receiver->track().release()); 291 } 292 293 void Conductor::OnIceCandidate(const webrtc::IceCandidate* candidate) { 294 RTC_LOG(LS_INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); 295 // For loopback test. To save some connecting delay. 296 if (loopback_) { 297 if (!peer_connection_->AddIceCandidate(candidate)) { 298 RTC_LOG(LS_WARNING) << "Failed to apply the received candidate"; 299 } 300 return; 301 } 302 303 Json::Value jmessage; 304 jmessage[kCandidateSdpMidName] = candidate->sdp_mid(); 305 jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index(); 306 jmessage[kCandidateSdpName] = candidate->ToString(); 307 308 Json::StreamWriterBuilder factory; 309 SendMessage(Json::writeString(factory, jmessage)); 310 } 311 312 // 313 // PeerConnectionClientObserver implementation. 314 // 315 316 void Conductor::OnSignedIn() { 317 RTC_LOG(LS_INFO) << __FUNCTION__; 318 main_wnd_->SwitchToPeerList(client_->peers()); 319 } 320 321 void Conductor::OnDisconnected() { 322 RTC_LOG(LS_INFO) << __FUNCTION__; 323 324 DeletePeerConnection(); 325 326 if (main_wnd_->IsWindow()) 327 main_wnd_->SwitchToConnectUI(); 328 } 329 330 void Conductor::OnPeerConnected(int id, const std::string& name) { 331 RTC_LOG(LS_INFO) << __FUNCTION__; 332 // Refresh the list if we're showing it. 333 if (main_wnd_->current_ui() == MainWindow::LIST_PEERS) 334 main_wnd_->SwitchToPeerList(client_->peers()); 335 } 336 337 void Conductor::OnPeerDisconnected(int id) { 338 RTC_LOG(LS_INFO) << __FUNCTION__; 339 if (id == peer_id_) { 340 RTC_LOG(LS_INFO) << "Our peer disconnected"; 341 main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_CLOSED, nullptr); 342 } else { 343 // Refresh the list if we're showing it. 344 if (main_wnd_->current_ui() == MainWindow::LIST_PEERS) 345 main_wnd_->SwitchToPeerList(client_->peers()); 346 } 347 } 348 349 void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { 350 RTC_DCHECK(peer_id_ == peer_id || peer_id_ == -1); 351 RTC_DCHECK(!message.empty()); 352 353 if (!peer_connection_) { 354 RTC_DCHECK(peer_id_ == -1); 355 peer_id_ = peer_id; 356 357 if (!InitializePeerConnection()) { 358 RTC_LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance"; 359 client_->SignOut(); 360 return; 361 } 362 } else if (peer_id != peer_id_) { 363 RTC_DCHECK(peer_id_ != -1); 364 RTC_LOG(LS_WARNING) 365 << "Received a message from unknown peer while already in a " 366 "conversation with a different peer."; 367 return; 368 } 369 370 Json::CharReaderBuilder factory; 371 std::unique_ptr<Json::CharReader> reader = 372 absl::WrapUnique(factory.newCharReader()); 373 Json::Value jmessage; 374 if (!reader->parse(message.data(), message.data() + message.length(), 375 &jmessage, nullptr)) { 376 RTC_LOG(LS_WARNING) << "Received unknown message. " << message; 377 return; 378 } 379 std::string type_str; 380 std::string json_object; 381 382 webrtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, 383 &type_str); 384 if (!type_str.empty()) { 385 if (type_str == "offer-loopback") { 386 // This is a loopback call. 387 // Recreate the peerconnection with DTLS disabled. 388 if (!ReinitializePeerConnectionForLoopback()) { 389 RTC_LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance"; 390 DeletePeerConnection(); 391 client_->SignOut(); 392 } 393 return; 394 } 395 std::optional<webrtc::SdpType> type_maybe = 396 webrtc::SdpTypeFromString(type_str); 397 if (!type_maybe) { 398 RTC_LOG(LS_ERROR) << "Unknown SDP type: " << type_str; 399 return; 400 } 401 webrtc::SdpType type = *type_maybe; 402 std::string sdp; 403 if (!webrtc::GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName, 404 &sdp)) { 405 RTC_LOG(LS_WARNING) 406 << "Can't parse received session description message."; 407 return; 408 } 409 webrtc::SdpParseError error; 410 std::unique_ptr<webrtc::SessionDescriptionInterface> session_description = 411 webrtc::CreateSessionDescription(type, sdp, &error); 412 if (!session_description) { 413 RTC_LOG(LS_WARNING) 414 << "Can't parse received session description message. " 415 "SdpParseError was: " 416 << error.description; 417 return; 418 } 419 RTC_LOG(LS_INFO) << " Received session description :" << message; 420 peer_connection_->SetRemoteDescription( 421 DummySetSessionDescriptionObserver::Create().get(), 422 session_description.release()); 423 if (type == webrtc::SdpType::kOffer) { 424 peer_connection_->CreateAnswer( 425 this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); 426 } 427 } else { 428 std::string sdp_mid; 429 int sdp_mlineindex = 0; 430 std::string sdp; 431 if (!webrtc::GetStringFromJsonObject(jmessage, kCandidateSdpMidName, 432 &sdp_mid) || 433 !webrtc::GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName, 434 &sdp_mlineindex) || 435 !webrtc::GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) { 436 RTC_LOG(LS_WARNING) << "Can't parse received message."; 437 return; 438 } 439 webrtc::SdpParseError error; 440 std::unique_ptr<webrtc::IceCandidate> candidate( 441 webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error)); 442 if (!candidate) { 443 RTC_LOG(LS_WARNING) << "Can't parse received candidate message. " 444 "SdpParseError was: " 445 << error.description; 446 return; 447 } 448 if (!peer_connection_->AddIceCandidate(candidate.get())) { 449 RTC_LOG(LS_WARNING) << "Failed to apply the received candidate"; 450 return; 451 } 452 RTC_LOG(LS_INFO) << " Received candidate :" << message; 453 } 454 } 455 456 void Conductor::OnMessageSent(int err) { 457 // Process the next pending message if any. 458 main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, nullptr); 459 } 460 461 void Conductor::OnServerConnectionFailure() { 462 main_wnd_->MessageBox("Error", ("Failed to connect to " + server_).c_str(), 463 true); 464 } 465 466 // 467 // MainWndCallback implementation. 468 // 469 470 void Conductor::StartLogin(const std::string& server, int port) { 471 if (client_->is_connected()) 472 return; 473 server_ = server; 474 client_->Connect(server, port, GetPeerName()); 475 } 476 477 void Conductor::DisconnectFromServer() { 478 if (client_->is_connected()) 479 client_->SignOut(); 480 } 481 482 void Conductor::ConnectToPeer(int peer_id) { 483 RTC_DCHECK(peer_id_ == -1); 484 RTC_DCHECK(peer_id != -1); 485 486 if (peer_connection_) { 487 main_wnd_->MessageBox( 488 "Error", "We only support connecting to one peer at a time", true); 489 return; 490 } 491 492 if (InitializePeerConnection()) { 493 peer_id_ = peer_id; 494 peer_connection_->CreateOffer( 495 this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); 496 } else { 497 main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true); 498 } 499 } 500 501 void Conductor::AddTracks() { 502 if (!peer_connection_->GetSenders().empty()) { 503 return; // Already added tracks. 504 } 505 506 webrtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track( 507 peer_connection_factory_->CreateAudioTrack( 508 kAudioLabel, 509 peer_connection_factory_->CreateAudioSource(webrtc::AudioOptions()) 510 .get())); 511 auto result_or_error = peer_connection_->AddTrack(audio_track, {kStreamId}); 512 if (!result_or_error.ok()) { 513 RTC_LOG(LS_ERROR) << "Failed to add audio track to PeerConnection: " 514 << result_or_error.error().message(); 515 } 516 517 webrtc::scoped_refptr<CapturerTrackSource> video_device = 518 CapturerTrackSource::Create(env_.task_queue_factory()); 519 if (video_device) { 520 webrtc::scoped_refptr<webrtc::VideoTrackInterface> video_track_( 521 peer_connection_factory_->CreateVideoTrack(video_device, kVideoLabel)); 522 main_wnd_->StartLocalRenderer(video_track_.get()); 523 524 result_or_error = peer_connection_->AddTrack(video_track_, {kStreamId}); 525 if (!result_or_error.ok()) { 526 RTC_LOG(LS_ERROR) << "Failed to add video track to PeerConnection: " 527 << result_or_error.error().message(); 528 } 529 } else { 530 RTC_LOG(LS_ERROR) << "OpenVideoCaptureDevice failed"; 531 } 532 533 main_wnd_->SwitchToStreamingUI(); 534 } 535 536 void Conductor::DisconnectFromCurrentPeer() { 537 RTC_LOG(LS_INFO) << __FUNCTION__; 538 if (peer_connection_) { 539 client_->SendHangUp(peer_id_); 540 DeletePeerConnection(); 541 } 542 543 if (main_wnd_->IsWindow()) 544 main_wnd_->SwitchToPeerList(client_->peers()); 545 } 546 547 void Conductor::UIThreadCallback(int msg_id, void* data) { 548 switch (msg_id) { 549 case PEER_CONNECTION_CLOSED: 550 RTC_LOG(LS_INFO) << "PEER_CONNECTION_CLOSED"; 551 DeletePeerConnection(); 552 553 if (main_wnd_->IsWindow()) { 554 if (client_->is_connected()) { 555 main_wnd_->SwitchToPeerList(client_->peers()); 556 } else { 557 main_wnd_->SwitchToConnectUI(); 558 } 559 } else { 560 DisconnectFromServer(); 561 } 562 break; 563 564 case SEND_MESSAGE_TO_PEER: { 565 RTC_LOG(LS_INFO) << "SEND_MESSAGE_TO_PEER"; 566 std::string* msg = reinterpret_cast<std::string*>(data); 567 if (msg) { 568 // For convenience, we always run the message through the queue. 569 // This way we can be sure that messages are sent to the server 570 // in the same order they were signaled without much hassle. 571 pending_messages_.push_back(msg); 572 } 573 574 if (!pending_messages_.empty() && !client_->IsSendingMessage()) { 575 msg = pending_messages_.front(); 576 pending_messages_.pop_front(); 577 578 if (!client_->SendToPeer(peer_id_, *msg) && peer_id_ != -1) { 579 RTC_LOG(LS_ERROR) << "SendToPeer failed"; 580 DisconnectFromServer(); 581 } 582 delete msg; 583 } 584 585 if (!peer_connection_) 586 peer_id_ = -1; 587 588 break; 589 } 590 591 case NEW_TRACK_ADDED: { 592 auto* track = reinterpret_cast<webrtc::MediaStreamTrackInterface*>(data); 593 if (track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { 594 auto* video_track = static_cast<webrtc::VideoTrackInterface*>(track); 595 main_wnd_->StartRemoteRenderer(video_track); 596 } 597 track->Release(); 598 break; 599 } 600 601 case TRACK_REMOVED: { 602 // Remote peer stopped sending a track. 603 auto* track = reinterpret_cast<webrtc::MediaStreamTrackInterface*>(data); 604 track->Release(); 605 break; 606 } 607 608 default: 609 RTC_DCHECK_NOTREACHED(); 610 break; 611 } 612 } 613 614 void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) { 615 peer_connection_->SetLocalDescription( 616 DummySetSessionDescriptionObserver::Create().get(), desc); 617 618 std::string sdp; 619 desc->ToString(&sdp); 620 621 // For loopback test. To save some connecting delay. 622 if (loopback_) { 623 // Replace message type from "offer" to "answer" 624 std::unique_ptr<webrtc::SessionDescriptionInterface> session_description = 625 webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp); 626 peer_connection_->SetRemoteDescription( 627 DummySetSessionDescriptionObserver::Create().get(), 628 session_description.release()); 629 return; 630 } 631 632 Json::Value jmessage; 633 jmessage[kSessionDescriptionTypeName] = 634 webrtc::SdpTypeToString(desc->GetType()); 635 jmessage[kSessionDescriptionSdpName] = sdp; 636 637 Json::StreamWriterBuilder factory; 638 SendMessage(Json::writeString(factory, jmessage)); 639 } 640 641 void Conductor::OnFailure(webrtc::RTCError error) { 642 RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); 643 } 644 645 void Conductor::SendMessage(const std::string& json_object) { 646 std::string* msg = new std::string(json_object); 647 main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg); 648 }