tor-browser

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

DecryptThroughputLimit.h (3737B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef DecryptThroughputLimit_h
      8 #define DecryptThroughputLimit_h
      9 
     10 #include <deque>
     11 
     12 #include "MediaTimer.h"
     13 #include "PlatformDecoderModule.h"
     14 #include "mozilla/Assertions.h"
     15 
     16 namespace mozilla {
     17 
     18 // We throttle our decrypt so that we don't decrypt more than a certain
     19 // duration of samples per second. This is to work around bugs in the
     20 // Widevine CDM. See bugs 1338924, 1342822, 1718223.
     21 class DecryptThroughputLimit {
     22 public:
     23  explicit DecryptThroughputLimit(nsISerialEventTarget* aTargetThread,
     24                                  uint32_t aMaxThroughputMs)
     25      : mThrottleScheduler(aTargetThread),
     26        mMaxThroughput(aMaxThroughputMs / 1000.0) {}
     27 
     28  typedef MozPromise<RefPtr<MediaRawData>, MediaResult, true> ThrottlePromise;
     29 
     30  // Resolves promise after a delay if necessary in order to reduce the
     31  // throughput of samples sent through the CDM for decryption.
     32  RefPtr<ThrottlePromise> Throttle(MediaRawData* aSample) {
     33    // We should only have one decrypt request being processed at once.
     34    MOZ_RELEASE_ASSERT(!mThrottleScheduler.IsScheduled());
     35 
     36    const TimeDuration WindowSize = TimeDuration::FromSeconds(0.1);
     37    const TimeDuration MaxThroughput =
     38        TimeDuration::FromSeconds(mMaxThroughput);
     39 
     40    // Forget decrypts that happened before the start of our window.
     41    const TimeStamp now = TimeStamp::Now();
     42    while (!mDecrypts.empty() &&
     43           mDecrypts.front().mTimestamp < now - WindowSize) {
     44      mDecrypts.pop_front();
     45    }
     46 
     47    // How much time duration of the media would we have decrypted inside the
     48    // time window if we did decrypt this block?
     49    TimeDuration sampleDuration = aSample->mDuration.ToTimeDuration();
     50    TimeDuration durationDecrypted = sampleDuration;
     51    for (const DecryptedJob& job : mDecrypts) {
     52      durationDecrypted += job.mSampleDuration;
     53    }
     54 
     55    if (durationDecrypted < MaxThroughput) {
     56      // If we decrypted a sample of this duration, we would *not* have
     57      // decrypted more than our threshold for max throughput, over the
     58      // preceding wall time window. So we're safe to proceed with this
     59      // decrypt.
     60      mDecrypts.push_back(DecryptedJob({now, sampleDuration}));
     61      return ThrottlePromise::CreateAndResolve(aSample, __func__);
     62    }
     63 
     64    // Otherwise, we need to delay until decrypting won't exceed our
     65    // throughput threshold.
     66 
     67    RefPtr<ThrottlePromise> p = mPromiseHolder.Ensure(__func__);
     68 
     69    TimeDuration delay = durationDecrypted - MaxThroughput;
     70    TimeStamp target = now + delay;
     71    RefPtr<MediaRawData> sample(aSample);
     72    mThrottleScheduler.Ensure(
     73        target,
     74        [this, sample, sampleDuration]() {
     75          mThrottleScheduler.CompleteRequest();
     76          mDecrypts.push_back(DecryptedJob({TimeStamp::Now(), sampleDuration}));
     77          mPromiseHolder.Resolve(sample, __func__);
     78        },
     79        []() {
     80          MOZ_DIAGNOSTIC_CRASH("DecryptThroughputLimit::Throttle reject");
     81        });
     82 
     83    return p;
     84  }
     85 
     86  void Flush() {
     87    mThrottleScheduler.Reset();
     88    mPromiseHolder.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     89  }
     90 
     91 private:
     92  DelayedScheduler<TimeStamp> mThrottleScheduler;
     93  MozPromiseHolder<ThrottlePromise> mPromiseHolder;
     94 
     95  double mMaxThroughput;
     96 
     97  struct DecryptedJob {
     98    TimeStamp mTimestamp;
     99    TimeDuration mSampleDuration;
    100  };
    101  std::deque<DecryptedJob> mDecrypts;
    102 };
    103 
    104 }  // namespace mozilla
    105 
    106 #endif  // DecryptThroughputLimit_h