Line data Source code
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 "PlatformDecoderModule.h"
11 : #include "MediaTimer.h"
12 : #include <deque>
13 :
14 : namespace mozilla {
15 :
16 : // We throttle our decrypt so that we don't decrypt more than a certain
17 : // duration of samples per second. This is to work around bugs in the
18 : // Widevine CDM. See bug 1338924 and bug 1342822.
19 0 : class DecryptThroughputLimit
20 : {
21 : public:
22 :
23 0 : explicit DecryptThroughputLimit(AbstractThread* aTargetThread)
24 0 : : mThrottleScheduler(aTargetThread)
25 : {
26 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>
33 0 : Throttle(MediaRawData* aSample)
34 : {
35 : // We should only have one decrypt request being processed at once.
36 0 : MOZ_RELEASE_ASSERT(!mThrottleScheduler.IsScheduled());
37 :
38 0 : const TimeDuration WindowSize = TimeDuration::FromSeconds(1.0);
39 0 : const TimeDuration MaxThroughput = TimeDuration::FromSeconds(2.0);
40 :
41 : // Forget decrypts that happened before the start of our window.
42 0 : const TimeStamp now = TimeStamp::Now();
43 0 : while (!mDecrypts.empty() && mDecrypts.front().mTimestamp < now - WindowSize) {
44 0 : 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 0 : TimeDuration sampleDuration = aSample->mDuration.ToTimeDuration();
50 0 : TimeDuration durationDecrypted = sampleDuration;
51 0 : for (const DecryptedJob& job : mDecrypts) {
52 0 : durationDecrypted += job.mSampleDuration;
53 : }
54 :
55 0 : 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 0 : mDecrypts.push_back(DecryptedJob({ now, sampleDuration }));
61 0 : return ThrottlePromise::CreateAndResolve(aSample, __func__);
62 : }
63 :
64 : // Otherwise, we need to delay until decrypting won't exceed our
65 : // throughput threshold.
66 :
67 0 : RefPtr<ThrottlePromise> p = mPromiseHolder.Ensure(__func__);
68 :
69 0 : TimeDuration delay = durationDecrypted - MaxThroughput;
70 0 : TimeStamp target = now + delay;
71 0 : RefPtr<MediaRawData> sample(aSample);
72 0 : mThrottleScheduler.Ensure(target,
73 0 : [this, sample, sampleDuration]() {
74 0 : mThrottleScheduler.CompleteRequest();
75 0 : mDecrypts.push_back(DecryptedJob({ TimeStamp::Now(), sampleDuration }));
76 0 : mPromiseHolder.Resolve(sample, __func__);
77 0 : },
78 0 : [] () {
79 0 : MOZ_DIAGNOSTIC_ASSERT(false);
80 0 : });
81 :
82 0 : return p;
83 : }
84 :
85 : void
86 0 : Flush()
87 : {
88 0 : mThrottleScheduler.Reset();
89 0 : mPromiseHolder.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
90 0 : }
91 :
92 : private:
93 : DelayedScheduler mThrottleScheduler;
94 : MozPromiseHolder<ThrottlePromise> mPromiseHolder;
95 :
96 : struct DecryptedJob
97 : {
98 : TimeStamp mTimestamp;
99 : TimeDuration mSampleDuration;
100 : };
101 : std::deque<DecryptedJob> mDecrypts;
102 : };
103 :
104 : }
105 :
106 : #endif // DecryptThroughputLimit_h
|