tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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