PeerConnectionCtx.cpp (21911B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "PeerConnectionCtx.h" 6 7 #include "PeerConnectionImpl.h" 8 #include "WebrtcGlobalChild.h" 9 #include "WebrtcGlobalInformation.h" 10 #include "WebrtcGlobalStatsHistory.h" 11 #include "api/audio/audio_mixer.h" 12 #include "api/audio_codecs/builtin_audio_decoder_factory.h" 13 #include "call/audio_state.h" 14 #include "common/browser_logging/CSFLog.h" 15 #include "common/browser_logging/WebRtcLog.h" 16 #include "gmp-video-decode.h" // GMP_API_VIDEO_DECODER 17 #include "gmp-video-encode.h" // GMP_API_VIDEO_ENCODER 18 #include "libwebrtcglue/WebrtcTaskQueueWrapper.h" 19 #include "modules/audio_device/include/fake_audio_device.h" 20 #include "modules/audio_processing/include/aec_dump.h" 21 #include "modules/audio_processing/include/audio_processing.h" 22 #include "modules/rtp_rtcp/source/rtp_header_extensions.h" 23 #include "mozilla/Services.h" 24 #include "mozilla/StaticPtr.h" 25 #include "mozilla/UniquePtr.h" 26 #include "mozilla/dom/RTCStatsReportBinding.h" 27 #include "mozilla/glean/DomMediaWebrtcMetrics.h" 28 #include "nsCRTGlue.h" 29 #include "nsIEventTarget.h" 30 #include "nsIIOService.h" 31 #include "nsIObserver.h" 32 #include "nsIObserverService.h" 33 #include "nsServiceManagerUtils.h" // do_GetService 34 #include "transport/runnable_utils.h" 35 36 static const char* pccLogTag = "PeerConnectionCtx"; 37 #ifdef LOGTAG 38 # undef LOGTAG 39 #endif 40 #define LOGTAG pccLogTag 41 42 using namespace webrtc; 43 44 namespace { 45 class DummyAudioMixer : public AudioMixer { 46 public: 47 bool AddSource(Source*) override { return true; } 48 void RemoveSource(Source*) override {} 49 void Mix(size_t, AudioFrame*) override { MOZ_CRASH("Unexpected call"); } 50 }; 51 52 class DummyAudioProcessing : public AudioProcessing { 53 public: 54 int Initialize() override { 55 MOZ_CRASH("Unexpected call"); 56 return kNoError; 57 } 58 int Initialize(const ProcessingConfig&) override { return Initialize(); } 59 void ApplyConfig(const Config&) override { MOZ_CRASH("Unexpected call"); } 60 int proc_sample_rate_hz() const override { 61 MOZ_CRASH("Unexpected call"); 62 return 0; 63 } 64 int proc_split_sample_rate_hz() const override { 65 MOZ_CRASH("Unexpected call"); 66 return 0; 67 } 68 size_t num_input_channels() const override { 69 MOZ_CRASH("Unexpected call"); 70 return 0; 71 } 72 size_t num_proc_channels() const override { 73 MOZ_CRASH("Unexpected call"); 74 return 0; 75 } 76 size_t num_output_channels() const override { 77 MOZ_CRASH("Unexpected call"); 78 return 0; 79 } 80 size_t num_reverse_channels() const override { 81 MOZ_CRASH("Unexpected call"); 82 return 0; 83 } 84 void set_output_will_be_muted(bool) override { MOZ_CRASH("Unexpected call"); } 85 void SetRuntimeSetting(RuntimeSetting) override { 86 MOZ_CRASH("Unexpected call"); 87 } 88 bool PostRuntimeSetting(RuntimeSetting setting) override { return false; } 89 int ProcessStream(const int16_t* const, const StreamConfig&, 90 const StreamConfig&, int16_t* const) override { 91 MOZ_CRASH("Unexpected call"); 92 return kNoError; 93 } 94 int ProcessStream(const float* const*, const StreamConfig&, 95 const StreamConfig&, float* const*) override { 96 MOZ_CRASH("Unexpected call"); 97 return kNoError; 98 } 99 int ProcessReverseStream(const int16_t* const, const StreamConfig&, 100 const StreamConfig&, int16_t* const) override { 101 MOZ_CRASH("Unexpected call"); 102 return kNoError; 103 } 104 int ProcessReverseStream(const float* const*, const StreamConfig&, 105 const StreamConfig&, float* const*) override { 106 MOZ_CRASH("Unexpected call"); 107 return kNoError; 108 } 109 int AnalyzeReverseStream(const float* const*, const StreamConfig&) override { 110 MOZ_CRASH("Unexpected call"); 111 return kNoError; 112 } 113 bool GetLinearAecOutput( 114 webrtc::ArrayView<std::array<float, 160>>) const override { 115 MOZ_CRASH("Unexpected call"); 116 return false; 117 } 118 void set_stream_analog_level(int) override { MOZ_CRASH("Unexpected call"); } 119 int recommended_stream_analog_level() const override { 120 MOZ_CRASH("Unexpected call"); 121 return -1; 122 } 123 int set_stream_delay_ms(int) override { 124 MOZ_CRASH("Unexpected call"); 125 return kNoError; 126 } 127 int stream_delay_ms() const override { 128 MOZ_CRASH("Unexpected call"); 129 return 0; 130 } 131 void set_stream_key_pressed(bool) override { MOZ_CRASH("Unexpected call"); } 132 bool CreateAndAttachAecDump(absl::string_view, int64_t, 133 absl::Nonnull<TaskQueueBase*>) override { 134 MOZ_CRASH("Unexpected call"); 135 return false; 136 } 137 bool CreateAndAttachAecDump(FILE*, int64_t, 138 absl::Nonnull<TaskQueueBase*>) override { 139 MOZ_CRASH("Unexpected call"); 140 return false; 141 } 142 void AttachAecDump(std::unique_ptr<AecDump>) override { 143 MOZ_CRASH("Unexpected call"); 144 } 145 void DetachAecDump() override { MOZ_CRASH("Unexpected call"); } 146 AudioProcessingStats GetStatistics() override { 147 return AudioProcessingStats(); 148 } 149 AudioProcessingStats GetStatistics(bool) override { return GetStatistics(); } 150 AudioProcessing::Config GetConfig() const override { 151 MOZ_CRASH("Unexpected call"); 152 return Config(); 153 } 154 }; 155 } // namespace 156 157 namespace mozilla { 158 159 using namespace dom; 160 161 SharedWebrtcState::SharedWebrtcState( 162 RefPtr<AbstractThread> aCallWorkerThread, 163 webrtc::AudioState::Config&& aAudioStateConfig, 164 RefPtr<webrtc::AudioDecoderFactory> aAudioDecoderFactory, 165 UniquePtr<webrtc::FieldTrialsView> aTrials) 166 : mCallWorkerThread(std::move(aCallWorkerThread)), 167 mAudioStateConfig(std::move(aAudioStateConfig)), 168 mAudioDecoderFactory(std::move(aAudioDecoderFactory)), 169 mTrials(std::move(aTrials)) {} 170 171 SharedWebrtcState::~SharedWebrtcState() = default; 172 173 class PeerConnectionCtxObserver : public nsIObserver { 174 public: 175 NS_DECL_ISUPPORTS 176 177 PeerConnectionCtxObserver() {} 178 179 void Init() { 180 nsCOMPtr<nsIObserverService> observerService = 181 services::GetObserverService(); 182 if (!observerService) return; 183 184 nsresult rv = NS_OK; 185 186 rv = observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, 187 false); 188 MOZ_ALWAYS_SUCCEEDS(rv); 189 rv = observerService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, 190 false); 191 MOZ_ALWAYS_SUCCEEDS(rv); 192 (void)rv; 193 } 194 195 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, 196 const char16_t* aData) override { 197 if (strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID) == 0) { 198 CSFLogDebug(LOGTAG, "Shutting down PeerConnectionCtx"); 199 PeerConnectionCtx::Destroy(); 200 201 nsCOMPtr<nsIObserverService> observerService = 202 services::GetObserverService(); 203 if (!observerService) return NS_ERROR_FAILURE; 204 205 nsresult rv = observerService->RemoveObserver( 206 this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); 207 MOZ_ALWAYS_SUCCEEDS(rv); 208 rv = observerService->RemoveObserver(this, 209 NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID); 210 MOZ_ALWAYS_SUCCEEDS(rv); 211 212 // Make sure we're not deleted while still inside ::Observe() 213 RefPtr<PeerConnectionCtxObserver> kungFuDeathGrip(this); 214 PeerConnectionCtx::gPeerConnectionCtxObserver = nullptr; 215 } 216 if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0) { 217 if (NS_strcmp(aData, u"" NS_IOSERVICE_OFFLINE) == 0) { 218 CSFLogDebug(LOGTAG, "Updating network state to offline"); 219 PeerConnectionCtx::UpdateNetworkState(false); 220 } else if (NS_strcmp(aData, u"" NS_IOSERVICE_ONLINE) == 0) { 221 CSFLogDebug(LOGTAG, "Updating network state to online"); 222 PeerConnectionCtx::UpdateNetworkState(true); 223 } else { 224 CSFLogDebug(LOGTAG, "Received unsupported network state event"); 225 MOZ_CRASH(); 226 } 227 } 228 return NS_OK; 229 } 230 231 private: 232 virtual ~PeerConnectionCtxObserver() { 233 nsCOMPtr<nsIObserverService> observerService = 234 services::GetObserverService(); 235 if (observerService) { 236 observerService->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); 237 observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID); 238 } 239 } 240 }; 241 242 NS_IMPL_ISUPPORTS(PeerConnectionCtxObserver, nsIObserver); 243 244 PeerConnectionCtx* PeerConnectionCtx::gInstance; 245 StaticRefPtr<PeerConnectionCtxObserver> 246 PeerConnectionCtx::gPeerConnectionCtxObserver; 247 248 nsresult PeerConnectionCtx::InitializeGlobal() { 249 MOZ_ASSERT(NS_IsMainThread()); 250 251 nsresult res; 252 253 if (!gInstance) { 254 CSFLogDebug(LOGTAG, "Creating PeerConnectionCtx"); 255 PeerConnectionCtx* ctx = new PeerConnectionCtx(); 256 257 res = ctx->Initialize(); 258 PR_ASSERT(NS_SUCCEEDED(res)); 259 if (!NS_SUCCEEDED(res)) return res; 260 261 gInstance = ctx; 262 263 if (!PeerConnectionCtx::gPeerConnectionCtxObserver) { 264 PeerConnectionCtx::gPeerConnectionCtxObserver = 265 new PeerConnectionCtxObserver(); 266 PeerConnectionCtx::gPeerConnectionCtxObserver->Init(); 267 } 268 } 269 270 return NS_OK; 271 } 272 273 PeerConnectionCtx* PeerConnectionCtx::GetInstance() { 274 MOZ_ASSERT(gInstance); 275 return gInstance; 276 } 277 278 bool PeerConnectionCtx::isActive() { return gInstance; } 279 280 void PeerConnectionCtx::Destroy() { 281 CSFLogDebug(LOGTAG, "%s", __FUNCTION__); 282 283 if (gInstance) { 284 // Null out gInstance first, so PeerConnectionImpl doesn't try to use it 285 // in Cleanup. 286 auto* instance = gInstance; 287 gInstance = nullptr; 288 instance->Cleanup(); 289 delete instance; 290 } 291 } 292 293 template <typename T> 294 static void RecordCommonRtpTelemetry(const T& list, const T& lastList, 295 const bool isRemote) { 296 for (const auto& s : list) { 297 const bool isAudio = s.mKind.Find(u"audio") != -1; 298 if (s.mPacketsLost.WasPassed() && s.mPacketsReceived.WasPassed()) { 299 if (const uint64_t total = 300 s.mPacketsLost.Value() + s.mPacketsReceived.Value()) { 301 // Because this is an integer and we would like some extra precision 302 // the unit is per mille (1/1000) instead of percent (1/100). 303 uint32_t sample = (s.mPacketsLost.Value() * 1000) / total; 304 if (isRemote) { 305 if (isAudio) { 306 glean::webrtc::audio_quality_outbound_packetloss_rate 307 .AccumulateSingleSample(sample); 308 } else { 309 glean::webrtc::video_quality_outbound_packetloss_rate 310 .AccumulateSingleSample(sample); 311 } 312 } else { 313 if (isAudio) { 314 glean::webrtc::audio_quality_inbound_packetloss_rate 315 .AccumulateSingleSample(sample); 316 } else { 317 glean::webrtc::video_quality_inbound_packetloss_rate 318 .AccumulateSingleSample(sample); 319 } 320 } 321 } 322 } 323 if (s.mJitter.WasPassed()) { 324 TimeDuration sample = 325 TimeDuration::FromMilliseconds(s.mJitter.Value() * 1000); 326 if (isRemote) { 327 if (isAudio) { 328 glean::webrtc::audio_quality_outbound_jitter.AccumulateRawDuration( 329 sample); 330 } else { 331 glean::webrtc::video_quality_outbound_jitter.AccumulateRawDuration( 332 sample); 333 } 334 } else { 335 if (isAudio) { 336 glean::webrtc::audio_quality_inbound_jitter.AccumulateRawDuration( 337 sample); 338 } else { 339 glean::webrtc::video_quality_inbound_jitter.AccumulateRawDuration( 340 sample); 341 } 342 } 343 } 344 } 345 } 346 347 // Telemetry reporting every second after start of first call. 348 // The threading model around the media pipelines is weird: 349 // - The pipelines are containers, 350 // - containers that are only safe on main thread, with members only safe on 351 // STS, 352 // - hence the there and back again approach. 353 354 void PeerConnectionCtx::DeliverStats( 355 UniquePtr<dom::RTCStatsReportInternal>&& aReport) { 356 // First, get reports from a second ago, if any, for calculations below 357 UniquePtr<dom::RTCStatsReportInternal> lastReport; 358 { 359 auto i = mLastReports.find(aReport->mPcid); 360 if (i != mLastReports.end()) { 361 lastReport = std::move(i->second); 362 } else { 363 lastReport = MakeUnique<dom::RTCStatsReportInternal>(); 364 } 365 } 366 // Record Telemetery 367 RecordCommonRtpTelemetry(aReport->mInboundRtpStreamStats, 368 lastReport->mInboundRtpStreamStats, false); 369 // Record bandwidth telemetry 370 for (const auto& s : aReport->mInboundRtpStreamStats) { 371 if (s.mBytesReceived.WasPassed()) { 372 const bool isAudio = s.mKind.Find(u"audio") != -1; 373 for (const auto& lastS : lastReport->mInboundRtpStreamStats) { 374 if (lastS.mId == s.mId) { 375 int32_t deltaMs = s.mTimestamp.Value() - lastS.mTimestamp.Value(); 376 // In theory we're called every second, so delta *should* be in that 377 // range. Small deltas could cause errors due to division 378 if (deltaMs < 500 || deltaMs > 60000 || 379 !lastS.mBytesReceived.WasPassed()) { 380 break; 381 } 382 // We could accumulate values until enough time has passed 383 // and then Accumulate() but this isn't that important 384 uint32_t sample = 385 ((s.mBytesReceived.Value() - lastS.mBytesReceived.Value()) * 8) / 386 deltaMs; 387 if (isAudio) { 388 glean::webrtc::audio_quality_inbound_bandwidth_kbits 389 .AccumulateSingleSample(sample); 390 } else { 391 glean::webrtc::video_quality_inbound_bandwidth_kbits 392 .AccumulateSingleSample(sample); 393 } 394 break; 395 } 396 } 397 } 398 } 399 RecordCommonRtpTelemetry(aReport->mRemoteInboundRtpStreamStats, 400 lastReport->mRemoteInboundRtpStreamStats, true); 401 for (const auto& s : aReport->mRemoteInboundRtpStreamStats) { 402 if (s.mRoundTripTime.WasPassed()) { 403 const bool isAudio = s.mKind.Find(u"audio") != -1; 404 TimeDuration sample = 405 TimeDuration::FromMilliseconds(s.mRoundTripTime.Value() * 1000); 406 if (isAudio) { 407 glean::webrtc::audio_quality_outbound_rtt.AccumulateRawDuration(sample); 408 } else { 409 glean::webrtc::video_quality_outbound_rtt.AccumulateRawDuration(sample); 410 } 411 } 412 } 413 414 mLastReports[aReport->mPcid] = std::move(aReport); 415 } 416 417 void PeerConnectionCtx::EverySecondTelemetryCallback_m(nsITimer* timer, 418 void* closure) { 419 MOZ_ASSERT(NS_IsMainThread()); 420 MOZ_ASSERT(PeerConnectionCtx::isActive()); 421 422 for (auto& idAndPc : GetInstance()->mPeerConnections) { 423 if (!idAndPc.second->IsClosed()) { 424 idAndPc.second->GetStats(nullptr, true) 425 ->Then( 426 GetMainThreadSerialEventTarget(), __func__, 427 [=](UniquePtr<dom::RTCStatsReportInternal>&& aReport) { 428 if (PeerConnectionCtx::isActive()) { 429 PeerConnectionCtx::GetInstance()->DeliverStats( 430 std::move(aReport)); 431 } 432 }, 433 [=](nsresult aError) {}); 434 idAndPc.second->CollectConduitTelemetryData(); 435 } 436 } 437 } 438 439 void PeerConnectionCtx::UpdateNetworkState(bool online) { 440 auto ctx = GetInstance(); 441 if (ctx->mPeerConnections.empty()) { 442 return; 443 } 444 for (auto pc : ctx->mPeerConnections) { 445 pc.second->UpdateNetworkState(online); 446 } 447 } 448 449 SharedWebrtcState* PeerConnectionCtx::GetSharedWebrtcState() const { 450 MOZ_ASSERT(NS_IsMainThread()); 451 return mSharedWebrtcState; 452 } 453 454 void PeerConnectionCtx::RemovePeerConnection(const std::string& aKey) { 455 MOZ_ASSERT(NS_IsMainThread()); 456 auto it = mPeerConnections.find(aKey); 457 if (it != mPeerConnections.end()) { 458 if (it->second->GetFinalStats() && !it->second->LongTermStatsIsDisabled()) { 459 WebrtcGlobalInformation::StashStats(*(it->second->GetFinalStats())); 460 } 461 nsAutoString pcId = NS_ConvertASCIItoUTF16(it->second->GetName().c_str()); 462 if (XRE_IsContentProcess()) { 463 if (auto* child = WebrtcGlobalChild::Get(); child) { 464 auto pcId = NS_ConvertASCIItoUTF16(it->second->GetName().c_str()); 465 child->SendPeerConnectionFinalStats(*(it->second->GetFinalStats())); 466 child->SendPeerConnectionDestroyed(pcId); 467 } 468 } else { 469 using Update = WebrtcGlobalInformation::PcTrackingUpdate; 470 auto update = Update::Remove(pcId); 471 auto finalStats = 472 MakeUnique<RTCStatsReportInternal>(*(it->second->GetFinalStats())); 473 WebrtcGlobalStatsHistory::Record(std::move(finalStats)); 474 WebrtcGlobalInformation::PeerConnectionTracking(update); 475 } 476 477 mPeerConnections.erase(it); 478 if (mPeerConnections.empty()) { 479 mSharedWebrtcState = nullptr; 480 StopTelemetryTimer(); 481 } 482 } 483 } 484 485 void PeerConnectionCtx::AddPeerConnection(const std::string& aKey, 486 PeerConnectionImpl* aPeerConnection) { 487 MOZ_ASSERT(NS_IsMainThread()); 488 MOZ_ASSERT(mPeerConnections.count(aKey) == 0, 489 "PeerConnection with this key should not already exist"); 490 if (mPeerConnections.empty()) { 491 AudioState::Config audioStateConfig; 492 audioStateConfig.audio_mixer = 493 new webrtc::RefCountedObject<DummyAudioMixer>(); 494 audioStateConfig.audio_processing = 495 new webrtc::RefCountedObject<DummyAudioProcessing>(); 496 audioStateConfig.audio_device_module = 497 new webrtc::RefCountedObject<FakeAudioDeviceModule>(); 498 499 constexpr bool supportTailDispatch = true; 500 // This task queue is passed into libwebrtc by means of 501 // webrtc::TaskQueueBase::GetCurrent() while running on it. 502 // WebrtcCallWrapper guarantees that it outlives its webrtc::Call instance. 503 // Outside of libwebrtc it works as a regular TaskQueue. 504 auto callWorkerThread = CreateWebrtcTaskQueueWrapper( 505 GetMediaThreadPool(MediaThreadType::WEBRTC_CALL_THREAD), 506 "CallWorker"_ns, supportTailDispatch); 507 508 UniquePtr<webrtc::FieldTrialsView> trials = 509 WrapUnique(new MozTrialsConfig()); 510 511 mSharedWebrtcState = MakeAndAddRef<SharedWebrtcState>( 512 std::move(callWorkerThread), std::move(audioStateConfig), 513 already_AddRefed(CreateBuiltinAudioDecoderFactory().release()), 514 std::move(trials)); 515 StartTelemetryTimer(); 516 } 517 auto pcId = NS_ConvertASCIItoUTF16(aPeerConnection->GetName().c_str()); 518 if (XRE_IsContentProcess()) { 519 if (auto* child = WebrtcGlobalChild::Get(); child) { 520 child->SendPeerConnectionCreated( 521 pcId, aPeerConnection->LongTermStatsIsDisabled()); 522 } 523 } else { 524 using Update = WebrtcGlobalInformation::PcTrackingUpdate; 525 auto update = Update::Add(pcId, aPeerConnection->LongTermStatsIsDisabled()); 526 WebrtcGlobalInformation::PeerConnectionTracking(update); 527 } 528 mPeerConnections[aKey] = aPeerConnection; 529 } 530 531 PeerConnectionImpl* PeerConnectionCtx::GetPeerConnection( 532 const std::string& aKey) const { 533 MOZ_ASSERT(NS_IsMainThread()); 534 auto iterator = mPeerConnections.find(aKey); 535 if (iterator == mPeerConnections.end()) { 536 return nullptr; 537 } 538 return iterator->second; 539 } 540 541 void PeerConnectionCtx::ClearClosedStats() { 542 for (auto& [id, pc] : mPeerConnections) { 543 (void)id; 544 if (pc->IsClosed()) { 545 // Rare case 546 pc->DisableLongTermStats(); 547 } 548 } 549 } 550 551 PeerConnectionCtx::PeerConnectionCtx() 552 : mGMPReady(false), 553 mLogHandle(EnsureWebrtcLogging()), 554 mTransportHandler(MediaTransportHandler::Create()) {} 555 556 nsresult PeerConnectionCtx::Initialize() { 557 MOZ_ASSERT(NS_IsMainThread()); 558 initGMP(); 559 SdpRidAttributeList::kMaxRidLength = 560 webrtc::BaseRtpStringExtension::kMaxValueSizeBytes; 561 562 if (XRE_IsContentProcess()) { 563 WebrtcGlobalChild::Get(); 564 } 565 566 return NS_OK; 567 } 568 569 nsresult PeerConnectionCtx::StartTelemetryTimer() { 570 return NS_NewTimerWithFuncCallback(getter_AddRefs(mTelemetryTimer), 571 EverySecondTelemetryCallback_m, this, 1000, 572 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP, 573 "EverySecondTelemetryCallback_m"_ns); 574 } 575 576 void PeerConnectionCtx::StopTelemetryTimer() { 577 if (mTelemetryTimer) { 578 mTelemetryTimer->Cancel(); 579 mTelemetryTimer = nullptr; 580 } 581 } 582 583 static void GMPReady_m() { 584 if (PeerConnectionCtx::isActive()) { 585 PeerConnectionCtx::GetInstance()->onGMPReady(); 586 } 587 }; 588 589 static void GMPReady() { 590 GetMainThreadSerialEventTarget()->Dispatch(WrapRunnableNM(&GMPReady_m), 591 NS_DISPATCH_NORMAL); 592 }; 593 594 void PeerConnectionCtx::initGMP() { 595 mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1"); 596 597 if (!mGMPService) { 598 CSFLogError(LOGTAG, "%s failed to get the gecko-media-plugin-service", 599 __FUNCTION__); 600 return; 601 } 602 603 nsCOMPtr<nsIThread> thread; 604 nsresult rv = mGMPService->GetThread(getter_AddRefs(thread)); 605 606 if (NS_FAILED(rv)) { 607 mGMPService = nullptr; 608 CSFLogError(LOGTAG, 609 "%s failed to get the gecko-media-plugin thread, err=%u", 610 __FUNCTION__, static_cast<unsigned>(rv)); 611 return; 612 } 613 614 // presumes that all GMP dir scans have been queued for the GMPThread 615 thread->Dispatch(WrapRunnableNM(&GMPReady), NS_DISPATCH_NORMAL); 616 } 617 618 nsresult PeerConnectionCtx::Cleanup() { 619 CSFLogDebug(LOGTAG, "%s", __FUNCTION__); 620 MOZ_ASSERT(NS_IsMainThread()); 621 622 mQueuedJSEPOperations.Clear(); 623 mGMPService = nullptr; 624 mTransportHandler = nullptr; 625 for (auto& [id, pc] : mPeerConnections) { 626 (void)id; 627 pc->Close(); 628 } 629 mPeerConnections.clear(); 630 mSharedWebrtcState = nullptr; 631 return NS_OK; 632 } 633 634 void PeerConnectionCtx::queueJSEPOperation(nsIRunnable* aOperation) { 635 mQueuedJSEPOperations.AppendElement(aOperation); 636 } 637 638 void PeerConnectionCtx::onGMPReady() { 639 mGMPReady = true; 640 for (size_t i = 0; i < mQueuedJSEPOperations.Length(); ++i) { 641 mQueuedJSEPOperations[i]->Run(); 642 } 643 mQueuedJSEPOperations.Clear(); 644 } 645 646 } // namespace mozilla