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