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 "EMEAudioDecoder.h"
9 #include "EMEVideoDecoder.h"
10 #include "MediaDataDecoderProxy.h"
11 #include "mozIGeckoMediaPluginService.h"
12 #include "mozilla/CDMProxy.h"
13 #include "mozilla/Unused.h"
14 #include "nsAutoPtr.h"
15 #include "nsServiceManagerUtils.h"
16 #include "MediaInfo.h"
17 #include "nsClassHashtable.h"
18 #include "GMPDecoderModule.h"
19 #include "MP4Decoder.h"
20 
21 namespace mozilla {
22 
23 typedef MozPromiseRequestHolder<CDMProxy::DecryptPromise> DecryptPromiseRequestHolder;
24 
25 class EMEDecryptor : public MediaDataDecoder {
26 
27 public:
28 
EMEDecryptor(MediaDataDecoder * aDecoder,MediaDataDecoderCallback * aCallback,CDMProxy * aProxy,TaskQueue * aDecodeTaskQueue)29   EMEDecryptor(MediaDataDecoder* aDecoder,
30                MediaDataDecoderCallback* aCallback,
31                CDMProxy* aProxy,
32                TaskQueue* aDecodeTaskQueue)
33     : mDecoder(aDecoder)
34     , mCallback(aCallback)
35     , mTaskQueue(aDecodeTaskQueue)
36     , mProxy(aProxy)
37     , mSamplesWaitingForKey(new SamplesWaitingForKey(this, this->mCallback,
38                                                      mTaskQueue, mProxy))
39     , mIsShutdown(false)
40   {
41   }
42 
Init()43   RefPtr<InitPromise> Init() override {
44     MOZ_ASSERT(!mIsShutdown);
45     return mDecoder->Init();
46   }
47 
Input(MediaRawData * aSample)48   void Input(MediaRawData* aSample) override {
49     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
50     if (mIsShutdown) {
51       NS_WARNING("EME encrypted sample arrived after shutdown");
52       return;
53     }
54     if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
55       return;
56     }
57 
58     nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter());
59     mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
60                                   writer->mCrypto.mSessionIds);
61 
62     mDecrypts.Put(aSample, new DecryptPromiseRequestHolder());
63     mDecrypts.Get(aSample)->Begin(mProxy->Decrypt(aSample)->Then(
64       mTaskQueue, __func__, this,
65       &EMEDecryptor::Decrypted,
66       &EMEDecryptor::Decrypted));
67     return;
68   }
69 
Decrypted(const DecryptResult & aDecrypted)70   void Decrypted(const DecryptResult& aDecrypted) {
71     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
72     MOZ_ASSERT(aDecrypted.mSample);
73 
74     nsAutoPtr<DecryptPromiseRequestHolder> holder;
75     mDecrypts.RemoveAndForget(aDecrypted.mSample, holder);
76     if (holder) {
77       holder->Complete();
78     } else {
79       // Decryption is not in the list of decrypt operations waiting
80       // for a result. It must have been flushed or drained. Ignore result.
81       return;
82     }
83 
84     if (mIsShutdown) {
85       NS_WARNING("EME decrypted sample arrived after shutdown");
86       return;
87     }
88 
89     if (aDecrypted.mStatus == NoKeyErr) {
90       // Key became unusable after we sent the sample to CDM to decrypt.
91       // Call Input() again, so that the sample is enqueued for decryption
92       // if the key becomes usable again.
93       Input(aDecrypted.mSample);
94     } else if (aDecrypted.mStatus != Ok) {
95       if (mCallback) {
96         mCallback->Error(MediaResult(
97           NS_ERROR_DOM_MEDIA_FATAL_ERR,
98           RESULT_DETAIL("decrypted.mStatus=%u", uint32_t(aDecrypted.mStatus))));
99       }
100     } else {
101       MOZ_ASSERT(!mIsShutdown);
102       // The Adobe GMP AAC decoder gets confused if we pass it non-encrypted
103       // samples with valid crypto data. So clear the crypto data, since the
104       // sample should be decrypted now anyway. If we don't do this and we're
105       // using the Adobe GMP for unencrypted decoding of data that is decrypted
106       // by gmp-clearkey, decoding will fail.
107       UniquePtr<MediaRawDataWriter> writer(aDecrypted.mSample->CreateWriter());
108       writer->mCrypto = CryptoSample();
109       mDecoder->Input(aDecrypted.mSample);
110     }
111   }
112 
Flush()113   void Flush() override {
114     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
115     MOZ_ASSERT(!mIsShutdown);
116     for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) {
117       nsAutoPtr<DecryptPromiseRequestHolder>& holder = iter.Data();
118       holder->DisconnectIfExists();
119       iter.Remove();
120     }
121     mDecoder->Flush();
122     mSamplesWaitingForKey->Flush();
123   }
124 
Drain()125   void Drain() override {
126     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
127     MOZ_ASSERT(!mIsShutdown);
128     for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) {
129       nsAutoPtr<DecryptPromiseRequestHolder>& holder = iter.Data();
130       holder->DisconnectIfExists();
131       iter.Remove();
132     }
133     mDecoder->Drain();
134   }
135 
Shutdown()136   void Shutdown() override {
137     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
138     MOZ_ASSERT(!mIsShutdown);
139     mIsShutdown = true;
140     mDecoder->Shutdown();
141     mSamplesWaitingForKey->BreakCycles();
142     mSamplesWaitingForKey = nullptr;
143     mDecoder = nullptr;
144     mProxy = nullptr;
145     mCallback = nullptr;
146   }
147 
GetDescriptionName() const148   const char* GetDescriptionName() const override {
149     return mDecoder->GetDescriptionName();
150   }
151 
152 private:
153 
154   RefPtr<MediaDataDecoder> mDecoder;
155   MediaDataDecoderCallback* mCallback;
156   RefPtr<TaskQueue> mTaskQueue;
157   RefPtr<CDMProxy> mProxy;
158   nsClassHashtable<nsRefPtrHashKey<MediaRawData>, DecryptPromiseRequestHolder> mDecrypts;
159   RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
160   bool mIsShutdown;
161 };
162 
163 class EMEMediaDataDecoderProxy : public MediaDataDecoderProxy {
164 public:
EMEMediaDataDecoderProxy(already_AddRefed<AbstractThread> aProxyThread,MediaDataDecoderCallback * aCallback,CDMProxy * aProxy,TaskQueue * aTaskQueue)165   EMEMediaDataDecoderProxy(already_AddRefed<AbstractThread> aProxyThread,
166                            MediaDataDecoderCallback* aCallback,
167                            CDMProxy* aProxy,
168                            TaskQueue* aTaskQueue)
169    : MediaDataDecoderProxy(Move(aProxyThread), aCallback)
170    , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
171                                                     aTaskQueue, aProxy))
172    , mProxy(aProxy)
173   {
174   }
175 
176   void Input(MediaRawData* aSample) override;
177   void Shutdown() override;
178 
179 private:
180   RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
181   RefPtr<CDMProxy> mProxy;
182 };
183 
184 void
Input(MediaRawData * aSample)185 EMEMediaDataDecoderProxy::Input(MediaRawData* aSample)
186 {
187   if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
188     return;
189   }
190 
191   nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter());
192   mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
193                                 writer->mCrypto.mSessionIds);
194 
195   MediaDataDecoderProxy::Input(aSample);
196 }
197 
198 void
Shutdown()199 EMEMediaDataDecoderProxy::Shutdown()
200 {
201   MediaDataDecoderProxy::Shutdown();
202 
203   mSamplesWaitingForKey->BreakCycles();
204   mSamplesWaitingForKey = nullptr;
205   mProxy = nullptr;
206 }
207 
EMEDecoderModule(CDMProxy * aProxy,PDMFactory * aPDM)208 EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy, PDMFactory* aPDM)
209   : mProxy(aProxy)
210   , mPDM(aPDM)
211 {
212 }
213 
~EMEDecoderModule()214 EMEDecoderModule::~EMEDecoderModule()
215 {
216 }
217 
218 static already_AddRefed<MediaDataDecoderProxy>
CreateDecoderWrapper(MediaDataDecoderCallback * aCallback,CDMProxy * aProxy,TaskQueue * aTaskQueue)219 CreateDecoderWrapper(MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, TaskQueue* aTaskQueue)
220 {
221   RefPtr<gmp::GeckoMediaPluginService> s(gmp::GeckoMediaPluginService::GetGeckoMediaPluginService());
222   if (!s) {
223     return nullptr;
224   }
225   RefPtr<AbstractThread> thread(s->GetAbstractGMPThread());
226   if (!thread) {
227     return nullptr;
228   }
229   RefPtr<MediaDataDecoderProxy> decoder(
230     new EMEMediaDataDecoderProxy(thread.forget(), aCallback, aProxy, aTaskQueue));
231   return decoder.forget();
232 }
233 
234 already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const CreateDecoderParams & aParams)235 EMEDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
236 {
237   MOZ_ASSERT(aParams.mConfig.mCrypto.mValid);
238 
239   if (SupportsMimeType(aParams.mConfig.mMimeType, nullptr)) {
240     // GMP decodes. Assume that means it can decrypt too.
241     RefPtr<MediaDataDecoderProxy> wrapper =
242       CreateDecoderWrapper(aParams.mCallback, mProxy, aParams.mTaskQueue);
243     auto params = GMPVideoDecoderParams(aParams).WithCallback(wrapper);
244     wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy, params));
245     return wrapper.forget();
246   }
247 
248   MOZ_ASSERT(mPDM);
249   RefPtr<MediaDataDecoder> decoder(mPDM->CreateDecoder(aParams));
250   if (!decoder) {
251     return nullptr;
252   }
253 
254   RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
255                                                        aParams.mCallback,
256                                                        mProxy,
257                                                        AbstractThread::GetCurrent()->AsTaskQueue()));
258   return emeDecoder.forget();
259 }
260 
261 already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const CreateDecoderParams & aParams)262 EMEDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
263 {
264   MOZ_ASSERT(aParams.mConfig.mCrypto.mValid);
265 
266   if (SupportsMimeType(aParams.mConfig.mMimeType, nullptr)) {
267     // GMP decodes. Assume that means it can decrypt too.
268     RefPtr<MediaDataDecoderProxy> wrapper =
269       CreateDecoderWrapper(aParams.mCallback, mProxy, aParams.mTaskQueue);
270     auto gmpParams = GMPAudioDecoderParams(aParams).WithCallback(wrapper);
271     wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy, gmpParams));
272     return wrapper.forget();
273   }
274 
275   MOZ_ASSERT(mPDM);
276   RefPtr<MediaDataDecoder> decoder(mPDM->CreateDecoder(aParams));
277   if (!decoder) {
278     return nullptr;
279   }
280 
281   RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
282                                                        aParams.mCallback,
283                                                        mProxy,
284                                                        AbstractThread::GetCurrent()->AsTaskQueue()));
285   return emeDecoder.forget();
286 }
287 
288 PlatformDecoderModule::ConversionRequired
DecoderNeedsConversion(const TrackInfo & aConfig) const289 EMEDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
290 {
291   if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) {
292     return ConversionRequired::kNeedAVCC;
293   } else {
294     return ConversionRequired::kNeedNone;
295   }
296 }
297 
298 bool
SupportsMimeType(const nsACString & aMimeType,DecoderDoctorDiagnostics * aDiagnostics) const299 EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType,
300                                    DecoderDoctorDiagnostics* aDiagnostics) const
301 {
302   Maybe<nsCString> gmp;
303   gmp.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
304   return GMPDecoderModule::SupportsMimeType(aMimeType, gmp);
305 }
306 
307 } // namespace mozilla
308