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