Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
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
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "EMEDecoderModule.h"
8 : #include "EMEVideoDecoder.h"
9 : #include "GMPDecoderModule.h"
10 : #include "GMPService.h"
11 : #include "MP4Decoder.h"
12 : #include "MediaInfo.h"
13 : #include "MediaPrefs.h"
14 : #include "PDMFactory.h"
15 : #include "gmp-decryption.h"
16 : #include "mozIGeckoMediaPluginService.h"
17 : #include "mozilla/CDMProxy.h"
18 : #include "mozilla/EMEUtils.h"
19 : #include "mozilla/Unused.h"
20 : #include "nsAutoPtr.h"
21 : #include "nsClassHashtable.h"
22 : #include "nsServiceManagerUtils.h"
23 : #include "DecryptThroughputLimit.h"
24 :
25 : namespace mozilla {
26 :
27 : typedef MozPromiseRequestHolder<DecryptPromise> DecryptPromiseRequestHolder;
28 : extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
29 :
30 0 : class EMEDecryptor : public MediaDataDecoder
31 : {
32 : public:
33 0 : EMEDecryptor(MediaDataDecoder* aDecoder, CDMProxy* aProxy,
34 : TaskQueue* aDecodeTaskQueue, TrackInfo::TrackType aType,
35 : MediaEventProducer<TrackInfo::TrackType>* aOnWaitingForKey)
36 0 : : mDecoder(aDecoder)
37 : , mTaskQueue(aDecodeTaskQueue)
38 : , mProxy(aProxy)
39 : , mSamplesWaitingForKey(
40 0 : new SamplesWaitingForKey(mProxy, aType, aOnWaitingForKey))
41 : , mThroughputLimiter(aDecodeTaskQueue)
42 0 : , mIsShutdown(false)
43 : {
44 0 : }
45 :
46 0 : RefPtr<InitPromise> Init() override
47 : {
48 0 : MOZ_ASSERT(!mIsShutdown);
49 0 : return mDecoder->Init();
50 : }
51 :
52 0 : RefPtr<DecodePromise> Decode(MediaRawData* aSample) override
53 : {
54 0 : MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
55 0 : MOZ_RELEASE_ASSERT(mDecrypts.Count() == 0,
56 : "Can only process one sample at a time");
57 0 : RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
58 :
59 0 : RefPtr<EMEDecryptor> self = this;
60 0 : mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)
61 0 : ->Then(mTaskQueue, __func__,
62 0 : [self, this](RefPtr<MediaRawData> aSample) {
63 0 : mKeyRequest.Complete();
64 0 : ThrottleDecode(aSample);
65 0 : },
66 0 : [self, this]() {
67 0 : mKeyRequest.Complete();
68 0 : })
69 0 : ->Track(mKeyRequest);
70 :
71 0 : return p;
72 : }
73 :
74 0 : void ThrottleDecode(MediaRawData* aSample)
75 : {
76 0 : RefPtr<EMEDecryptor> self = this;
77 0 : mThroughputLimiter.Throttle(aSample)
78 0 : ->Then(mTaskQueue, __func__,
79 0 : [self, this] (RefPtr<MediaRawData> aSample) {
80 0 : mThrottleRequest.Complete();
81 0 : AttemptDecode(aSample);
82 0 : },
83 0 : [self, this]() {
84 0 : mThrottleRequest.Complete();
85 0 : })
86 0 : ->Track(mThrottleRequest);
87 0 : }
88 :
89 0 : void AttemptDecode(MediaRawData* aSample)
90 : {
91 0 : MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
92 0 : if (mIsShutdown) {
93 0 : NS_WARNING("EME encrypted sample arrived after shutdown");
94 0 : mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
95 0 : return;
96 : }
97 :
98 0 : nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter());
99 0 : mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
100 0 : writer->mCrypto.mSessionIds);
101 :
102 0 : mDecrypts.Put(aSample, new DecryptPromiseRequestHolder());
103 0 : mProxy->Decrypt(aSample)
104 0 : ->Then(mTaskQueue, __func__, this,
105 : &EMEDecryptor::Decrypted,
106 0 : &EMEDecryptor::Decrypted)
107 0 : ->Track(*mDecrypts.Get(aSample));
108 : }
109 :
110 0 : void Decrypted(const DecryptResult& aDecrypted)
111 : {
112 0 : MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
113 0 : MOZ_ASSERT(aDecrypted.mSample);
114 :
115 0 : nsAutoPtr<DecryptPromiseRequestHolder> holder;
116 0 : mDecrypts.Remove(aDecrypted.mSample, &holder);
117 0 : if (holder) {
118 0 : holder->Complete();
119 : } else {
120 : // Decryption is not in the list of decrypt operations waiting
121 : // for a result. It must have been flushed or drained. Ignore result.
122 0 : return;
123 : }
124 :
125 0 : if (mIsShutdown) {
126 0 : NS_WARNING("EME decrypted sample arrived after shutdown");
127 0 : return;
128 : }
129 :
130 0 : if (aDecrypted.mStatus == eme::NoKeyErr) {
131 : // Key became unusable after we sent the sample to CDM to decrypt.
132 : // Call Decode() again, so that the sample is enqueued for decryption
133 : // if the key becomes usable again.
134 0 : AttemptDecode(aDecrypted.mSample);
135 0 : } else if (aDecrypted.mStatus != eme::Ok) {
136 0 : mDecodePromise.RejectIfExists(
137 0 : MediaResult(
138 : NS_ERROR_DOM_MEDIA_FATAL_ERR,
139 0 : RESULT_DETAIL("decrypted.mStatus=%u", uint32_t(aDecrypted.mStatus))),
140 0 : __func__);
141 : } else {
142 0 : MOZ_ASSERT(!mIsShutdown);
143 : // The sample is no longer encrypted, so clear its crypto metadata.
144 0 : UniquePtr<MediaRawDataWriter> writer(aDecrypted.mSample->CreateWriter());
145 0 : writer->mCrypto = CryptoSample();
146 0 : RefPtr<EMEDecryptor> self = this;
147 0 : mDecoder->Decode(aDecrypted.mSample)
148 0 : ->Then(mTaskQueue, __func__,
149 0 : [self, this](const DecodedData& aResults) {
150 0 : mDecodeRequest.Complete();
151 0 : mDecodePromise.ResolveIfExists(aResults, __func__);
152 0 : },
153 0 : [self, this](const MediaResult& aError) {
154 0 : mDecodeRequest.Complete();
155 0 : mDecodePromise.RejectIfExists(aError, __func__);
156 0 : })
157 0 : ->Track(mDecodeRequest);
158 : }
159 : }
160 :
161 0 : RefPtr<FlushPromise> Flush() override
162 : {
163 0 : MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
164 0 : MOZ_ASSERT(!mIsShutdown);
165 0 : mKeyRequest.DisconnectIfExists();
166 0 : mThrottleRequest.DisconnectIfExists();
167 0 : mDecodeRequest.DisconnectIfExists();
168 0 : mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
169 0 : mThroughputLimiter.Flush();
170 0 : for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) {
171 0 : nsAutoPtr<DecryptPromiseRequestHolder>& holder = iter.Data();
172 0 : holder->DisconnectIfExists();
173 0 : iter.Remove();
174 : }
175 0 : RefPtr<SamplesWaitingForKey> k = mSamplesWaitingForKey;
176 0 : return mDecoder->Flush()->Then(
177 : mTaskQueue, __func__,
178 0 : [k]() {
179 0 : k->Flush();
180 0 : return FlushPromise::CreateAndResolve(true, __func__);
181 0 : });
182 : }
183 :
184 0 : RefPtr<DecodePromise> Drain() override
185 : {
186 0 : MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
187 0 : MOZ_ASSERT(!mIsShutdown);
188 0 : MOZ_ASSERT(mDecodePromise.IsEmpty() && !mDecodeRequest.Exists(),
189 : "Must wait for decoding to complete");
190 0 : for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) {
191 0 : nsAutoPtr<DecryptPromiseRequestHolder>& holder = iter.Data();
192 0 : holder->DisconnectIfExists();
193 0 : iter.Remove();
194 : }
195 0 : return mDecoder->Drain();
196 : }
197 :
198 0 : RefPtr<ShutdownPromise> Shutdown() override
199 : {
200 0 : MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
201 0 : MOZ_ASSERT(!mIsShutdown);
202 0 : mIsShutdown = true;
203 0 : mSamplesWaitingForKey = nullptr;
204 0 : RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
205 0 : mProxy = nullptr;
206 0 : return decoder->Shutdown();
207 : }
208 :
209 0 : const char* GetDescriptionName() const override
210 : {
211 0 : return mDecoder->GetDescriptionName();
212 : }
213 :
214 0 : ConversionRequired NeedsConversion() const override
215 : {
216 0 : return mDecoder->NeedsConversion();
217 : }
218 :
219 : private:
220 : RefPtr<MediaDataDecoder> mDecoder;
221 : RefPtr<TaskQueue> mTaskQueue;
222 : RefPtr<CDMProxy> mProxy;
223 : nsClassHashtable<nsRefPtrHashKey<MediaRawData>, DecryptPromiseRequestHolder>
224 : mDecrypts;
225 : RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
226 : MozPromiseRequestHolder<SamplesWaitingForKey::WaitForKeyPromise> mKeyRequest;
227 : DecryptThroughputLimit mThroughputLimiter;
228 : MozPromiseRequestHolder<DecryptThroughputLimit::ThrottlePromise> mThrottleRequest;
229 : MozPromiseHolder<DecodePromise> mDecodePromise;
230 : MozPromiseHolder<DecodePromise> mDrainPromise;
231 : MozPromiseHolder<FlushPromise> mFlushPromise;
232 : MozPromiseRequestHolder<DecodePromise> mDecodeRequest;
233 :
234 : bool mIsShutdown;
235 : };
236 :
237 0 : EMEMediaDataDecoderProxy::EMEMediaDataDecoderProxy(
238 : already_AddRefed<AbstractThread> aProxyThread,
239 : CDMProxy* aProxy,
240 0 : const CreateDecoderParams& aParams)
241 0 : : MediaDataDecoderProxy(Move(aProxyThread))
242 0 : , mTaskQueue(AbstractThread::GetCurrent()->AsTaskQueue())
243 : , mSamplesWaitingForKey(
244 : new SamplesWaitingForKey(aProxy,
245 0 : aParams.mType,
246 0 : aParams.mOnWaitingForKeyEvent))
247 0 : , mProxy(aProxy)
248 : {
249 0 : }
250 :
251 0 : EMEMediaDataDecoderProxy::EMEMediaDataDecoderProxy(
252 : const CreateDecoderParams& aParams,
253 : already_AddRefed<MediaDataDecoder> aProxyDecoder,
254 0 : CDMProxy* aProxy)
255 0 : : MediaDataDecoderProxy(Move(aProxyDecoder))
256 0 : , mTaskQueue(AbstractThread::GetCurrent()->AsTaskQueue())
257 : , mSamplesWaitingForKey(
258 : new SamplesWaitingForKey(aProxy,
259 0 : aParams.mType,
260 0 : aParams.mOnWaitingForKeyEvent))
261 0 : , mProxy(aProxy)
262 : {
263 0 : }
264 :
265 : RefPtr<MediaDataDecoder::DecodePromise>
266 0 : EMEMediaDataDecoderProxy::Decode(MediaRawData* aSample)
267 : {
268 0 : RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
269 :
270 0 : RefPtr<EMEMediaDataDecoderProxy> self = this;
271 0 : mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)
272 0 : ->Then(mTaskQueue, __func__,
273 0 : [self, this](RefPtr<MediaRawData> aSample) {
274 0 : mKeyRequest.Complete();
275 :
276 0 : nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter());
277 0 : mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
278 0 : writer->mCrypto.mSessionIds);
279 0 : MediaDataDecoderProxy::Decode(aSample)
280 0 : ->Then(mTaskQueue, __func__,
281 0 : [self, this](const DecodedData& aResults) {
282 0 : mDecodeRequest.Complete();
283 0 : mDecodePromise.Resolve(aResults, __func__);
284 0 : },
285 0 : [self, this](const MediaResult& aError) {
286 0 : mDecodeRequest.Complete();
287 0 : mDecodePromise.Reject(aError, __func__);
288 0 : })
289 0 : ->Track(mDecodeRequest);
290 0 : },
291 0 : [self, this]() {
292 0 : mKeyRequest.Complete();
293 0 : MOZ_CRASH("Should never get here");
294 0 : })
295 0 : ->Track(mKeyRequest);
296 :
297 0 : return p;
298 : }
299 :
300 : RefPtr<MediaDataDecoder::FlushPromise>
301 0 : EMEMediaDataDecoderProxy::Flush()
302 : {
303 0 : mKeyRequest.DisconnectIfExists();
304 0 : mDecodeRequest.DisconnectIfExists();
305 0 : mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
306 0 : return MediaDataDecoderProxy::Flush();
307 : }
308 :
309 : RefPtr<ShutdownPromise>
310 0 : EMEMediaDataDecoderProxy::Shutdown()
311 : {
312 0 : mSamplesWaitingForKey = nullptr;
313 0 : mProxy = nullptr;
314 0 : return MediaDataDecoderProxy::Shutdown();
315 : }
316 :
317 0 : EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy, PDMFactory* aPDM)
318 : : mProxy(aProxy)
319 0 : , mPDM(aPDM)
320 : {
321 0 : }
322 :
323 0 : EMEDecoderModule::~EMEDecoderModule()
324 : {
325 0 : }
326 :
327 : static already_AddRefed<MediaDataDecoderProxy>
328 0 : CreateDecoderWrapper(CDMProxy* aProxy, const CreateDecoderParams& aParams)
329 : {
330 : RefPtr<gmp::GeckoMediaPluginService> s(
331 0 : gmp::GeckoMediaPluginService::GetGeckoMediaPluginService());
332 0 : if (!s) {
333 0 : return nullptr;
334 : }
335 0 : RefPtr<AbstractThread> thread(s->GetAbstractGMPThread());
336 0 : if (!thread) {
337 0 : return nullptr;
338 : }
339 : RefPtr<MediaDataDecoderProxy> decoder(new EMEMediaDataDecoderProxy(
340 0 : thread.forget(), aProxy, aParams));
341 0 : return decoder.forget();
342 : }
343 :
344 : already_AddRefed<MediaDataDecoder>
345 0 : EMEDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
346 : {
347 0 : MOZ_ASSERT(aParams.mConfig.mCrypto.mValid);
348 :
349 0 : if (MediaPrefs::EMEBlankVideo()) {
350 0 : EME_LOG("EMEDecoderModule::CreateVideoDecoder() creating a blank decoder.");
351 0 : RefPtr<PlatformDecoderModule> m(CreateBlankDecoderModule());
352 0 : return m->CreateVideoDecoder(aParams);
353 : }
354 :
355 0 : if (SupportsMimeType(aParams.mConfig.mMimeType, nullptr)) {
356 : // GMP decodes. Assume that means it can decrypt too.
357 : RefPtr<MediaDataDecoderProxy> wrapper =
358 0 : CreateDecoderWrapper(mProxy, aParams);
359 0 : auto params = GMPVideoDecoderParams(aParams);
360 0 : if (MediaPrefs::EMEChromiumAPIEnabled()) {
361 0 : wrapper->SetProxyTarget(new ChromiumCDMVideoDecoder(params, mProxy));
362 : } else {
363 0 : wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy, params));
364 : }
365 0 : return wrapper.forget();
366 : }
367 :
368 0 : MOZ_ASSERT(mPDM);
369 0 : RefPtr<MediaDataDecoder> decoder(mPDM->CreateDecoder(aParams));
370 0 : if (!decoder) {
371 0 : return nullptr;
372 : }
373 :
374 : RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(
375 0 : decoder, mProxy, AbstractThread::GetCurrent()->AsTaskQueue(),
376 0 : aParams.mType, aParams.mOnWaitingForKeyEvent));
377 0 : return emeDecoder.forget();
378 : }
379 :
380 : already_AddRefed<MediaDataDecoder>
381 0 : EMEDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
382 : {
383 0 : MOZ_ASSERT(aParams.mConfig.mCrypto.mValid);
384 :
385 : // We don't support using the GMP to decode audio.
386 0 : MOZ_ASSERT(!SupportsMimeType(aParams.mConfig.mMimeType, nullptr));
387 0 : MOZ_ASSERT(mPDM);
388 :
389 0 : if (MediaPrefs::EMEBlankAudio()) {
390 0 : EME_LOG("EMEDecoderModule::CreateAudioDecoder() creating a blank decoder.");
391 0 : RefPtr<PlatformDecoderModule> m(CreateBlankDecoderModule());
392 0 : return m->CreateAudioDecoder(aParams);
393 : }
394 :
395 0 : RefPtr<MediaDataDecoder> decoder(mPDM->CreateDecoder(aParams));
396 0 : if (!decoder) {
397 0 : return nullptr;
398 : }
399 :
400 : RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(
401 0 : decoder, mProxy, AbstractThread::GetCurrent()->AsTaskQueue(),
402 0 : aParams.mType, aParams.mOnWaitingForKeyEvent));
403 0 : return emeDecoder.forget();
404 : }
405 :
406 : bool
407 0 : EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType,
408 : DecoderDoctorDiagnostics* aDiagnostics) const
409 : {
410 0 : Maybe<nsCString> gmp;
411 0 : gmp.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
412 0 : return GMPDecoderModule::SupportsMimeType(aMimeType, gmp);
413 : }
414 :
415 : } // namespace mozilla
|