diff options
Diffstat (limited to 'dom/media/platforms/agnostic/eme')
-rw-r--r-- | dom/media/platforms/agnostic/eme/EMEAudioDecoder.cpp | 44 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/EMEAudioDecoder.h | 37 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp | 307 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/EMEDecoderModule.h | 52 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp | 68 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/EMEVideoDecoder.h | 45 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/SamplesWaitingForKey.cpp | 86 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/SamplesWaitingForKey.h | 58 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/eme/moz.build | 23 |
9 files changed, 720 insertions, 0 deletions
diff --git a/dom/media/platforms/agnostic/eme/EMEAudioDecoder.cpp b/dom/media/platforms/agnostic/eme/EMEAudioDecoder.cpp new file mode 100644 index 000000000..25e502025 --- /dev/null +++ b/dom/media/platforms/agnostic/eme/EMEAudioDecoder.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "EMEAudioDecoder.h" +#include "mozilla/CDMProxy.h" + +namespace mozilla { + +void +EMEAudioCallbackAdapter::Error(GMPErr aErr) +{ + if (aErr == GMPNoKeyErr) { + // The GMP failed to decrypt a frame due to not having a key. This can + // happen if a key expires or a session is closed during playback. + NS_WARNING("GMP failed to decrypt due to lack of key"); + return; + } + AudioCallbackAdapter::Error(aErr); +} + +EMEAudioDecoder::EMEAudioDecoder(CDMProxy* aProxy, + const GMPAudioDecoderParams& aParams) + : GMPAudioDecoder(GMPAudioDecoderParams(aParams).WithAdapter( + new EMEAudioCallbackAdapter(aParams.mCallback))) + , mProxy(aProxy) +{} + +void +EMEAudioDecoder::InitTags(nsTArray<nsCString>& aTags) +{ + aTags.AppendElement(NS_LITERAL_CSTRING("aac")); + aTags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem())); +} + +nsCString +EMEAudioDecoder::GetNodeId() +{ + return mProxy->GetNodeId(); +} + +} // namespace mozilla diff --git a/dom/media/platforms/agnostic/eme/EMEAudioDecoder.h b/dom/media/platforms/agnostic/eme/EMEAudioDecoder.h new file mode 100644 index 000000000..c5944cf8c --- /dev/null +++ b/dom/media/platforms/agnostic/eme/EMEAudioDecoder.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef EMEAudioDecoder_h_ +#define EMEAudioDecoder_h_ + +#include "GMPAudioDecoder.h" +#include "PlatformDecoderModule.h" + +namespace mozilla { + +class EMEAudioCallbackAdapter : public AudioCallbackAdapter { +public: + explicit EMEAudioCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback) + : AudioCallbackAdapter(aCallback) + {} + + void Error(GMPErr aErr) override; +}; + +class EMEAudioDecoder : public GMPAudioDecoder { +public: + EMEAudioDecoder(CDMProxy* aProxy, const GMPAudioDecoderParams& aParams); + +private: + void InitTags(nsTArray<nsCString>& aTags) override; + nsCString GetNodeId() override; + + RefPtr<CDMProxy> mProxy; +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp new file mode 100644 index 000000000..7d523d926 --- /dev/null +++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "EMEDecoderModule.h" +#include "EMEAudioDecoder.h" +#include "EMEVideoDecoder.h" +#include "MediaDataDecoderProxy.h" +#include "mozIGeckoMediaPluginService.h" +#include "mozilla/CDMProxy.h" +#include "mozilla/Unused.h" +#include "nsAutoPtr.h" +#include "nsServiceManagerUtils.h" +#include "MediaInfo.h" +#include "nsClassHashtable.h" +#include "GMPDecoderModule.h" +#include "MP4Decoder.h" + +namespace mozilla { + +typedef MozPromiseRequestHolder<CDMProxy::DecryptPromise> DecryptPromiseRequestHolder; + +class EMEDecryptor : public MediaDataDecoder { + +public: + + EMEDecryptor(MediaDataDecoder* aDecoder, + MediaDataDecoderCallback* aCallback, + CDMProxy* aProxy, + TaskQueue* aDecodeTaskQueue) + : mDecoder(aDecoder) + , mCallback(aCallback) + , mTaskQueue(aDecodeTaskQueue) + , mProxy(aProxy) + , mSamplesWaitingForKey(new SamplesWaitingForKey(this, this->mCallback, + mTaskQueue, mProxy)) + , mIsShutdown(false) + { + } + + RefPtr<InitPromise> Init() override { + MOZ_ASSERT(!mIsShutdown); + return mDecoder->Init(); + } + + void Input(MediaRawData* aSample) override { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + if (mIsShutdown) { + NS_WARNING("EME encrypted sample arrived after shutdown"); + return; + } + if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) { + return; + } + + nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter()); + mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId, + writer->mCrypto.mSessionIds); + + mDecrypts.Put(aSample, new DecryptPromiseRequestHolder()); + mDecrypts.Get(aSample)->Begin(mProxy->Decrypt(aSample)->Then( + mTaskQueue, __func__, this, + &EMEDecryptor::Decrypted, + &EMEDecryptor::Decrypted)); + return; + } + + void Decrypted(const DecryptResult& aDecrypted) { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + MOZ_ASSERT(aDecrypted.mSample); + + nsAutoPtr<DecryptPromiseRequestHolder> holder; + mDecrypts.RemoveAndForget(aDecrypted.mSample, holder); + if (holder) { + holder->Complete(); + } else { + // Decryption is not in the list of decrypt operations waiting + // for a result. It must have been flushed or drained. Ignore result. + return; + } + + if (mIsShutdown) { + NS_WARNING("EME decrypted sample arrived after shutdown"); + return; + } + + if (aDecrypted.mStatus == NoKeyErr) { + // Key became unusable after we sent the sample to CDM to decrypt. + // Call Input() again, so that the sample is enqueued for decryption + // if the key becomes usable again. + Input(aDecrypted.mSample); + } else if (aDecrypted.mStatus != Ok) { + if (mCallback) { + mCallback->Error(MediaResult( + NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("decrypted.mStatus=%u", uint32_t(aDecrypted.mStatus)))); + } + } else { + MOZ_ASSERT(!mIsShutdown); + // The Adobe GMP AAC decoder gets confused if we pass it non-encrypted + // samples with valid crypto data. So clear the crypto data, since the + // sample should be decrypted now anyway. If we don't do this and we're + // using the Adobe GMP for unencrypted decoding of data that is decrypted + // by gmp-clearkey, decoding will fail. + UniquePtr<MediaRawDataWriter> writer(aDecrypted.mSample->CreateWriter()); + writer->mCrypto = CryptoSample(); + mDecoder->Input(aDecrypted.mSample); + } + } + + void Flush() override { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + MOZ_ASSERT(!mIsShutdown); + for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) { + nsAutoPtr<DecryptPromiseRequestHolder>& holder = iter.Data(); + holder->DisconnectIfExists(); + iter.Remove(); + } + mDecoder->Flush(); + mSamplesWaitingForKey->Flush(); + } + + void Drain() override { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + MOZ_ASSERT(!mIsShutdown); + for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) { + nsAutoPtr<DecryptPromiseRequestHolder>& holder = iter.Data(); + holder->DisconnectIfExists(); + iter.Remove(); + } + mDecoder->Drain(); + } + + void Shutdown() override { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + MOZ_ASSERT(!mIsShutdown); + mIsShutdown = true; + mDecoder->Shutdown(); + mSamplesWaitingForKey->BreakCycles(); + mSamplesWaitingForKey = nullptr; + mDecoder = nullptr; + mProxy = nullptr; + mCallback = nullptr; + } + + const char* GetDescriptionName() const override { + return mDecoder->GetDescriptionName(); + } + +private: + + RefPtr<MediaDataDecoder> mDecoder; + MediaDataDecoderCallback* mCallback; + RefPtr<TaskQueue> mTaskQueue; + RefPtr<CDMProxy> mProxy; + nsClassHashtable<nsRefPtrHashKey<MediaRawData>, DecryptPromiseRequestHolder> mDecrypts; + RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey; + bool mIsShutdown; +}; + +class EMEMediaDataDecoderProxy : public MediaDataDecoderProxy { +public: + EMEMediaDataDecoderProxy(already_AddRefed<AbstractThread> aProxyThread, + MediaDataDecoderCallback* aCallback, + CDMProxy* aProxy, + TaskQueue* aTaskQueue) + : MediaDataDecoderProxy(Move(aProxyThread), aCallback) + , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback, + aTaskQueue, aProxy)) + , mProxy(aProxy) + { + } + + void Input(MediaRawData* aSample) override; + void Shutdown() override; + +private: + RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey; + RefPtr<CDMProxy> mProxy; +}; + +void +EMEMediaDataDecoderProxy::Input(MediaRawData* aSample) +{ + if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) { + return; + } + + nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter()); + mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId, + writer->mCrypto.mSessionIds); + + MediaDataDecoderProxy::Input(aSample); +} + +void +EMEMediaDataDecoderProxy::Shutdown() +{ + MediaDataDecoderProxy::Shutdown(); + + mSamplesWaitingForKey->BreakCycles(); + mSamplesWaitingForKey = nullptr; + mProxy = nullptr; +} + +EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy, PDMFactory* aPDM) + : mProxy(aProxy) + , mPDM(aPDM) +{ +} + +EMEDecoderModule::~EMEDecoderModule() +{ +} + +static already_AddRefed<MediaDataDecoderProxy> +CreateDecoderWrapper(MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, TaskQueue* aTaskQueue) +{ + RefPtr<gmp::GeckoMediaPluginService> s(gmp::GeckoMediaPluginService::GetGeckoMediaPluginService()); + if (!s) { + return nullptr; + } + RefPtr<AbstractThread> thread(s->GetAbstractGMPThread()); + if (!thread) { + return nullptr; + } + RefPtr<MediaDataDecoderProxy> decoder( + new EMEMediaDataDecoderProxy(thread.forget(), aCallback, aProxy, aTaskQueue)); + return decoder.forget(); +} + +already_AddRefed<MediaDataDecoder> +EMEDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) +{ + MOZ_ASSERT(aParams.mConfig.mCrypto.mValid); + + if (SupportsMimeType(aParams.mConfig.mMimeType, nullptr)) { + // GMP decodes. Assume that means it can decrypt too. + RefPtr<MediaDataDecoderProxy> wrapper = + CreateDecoderWrapper(aParams.mCallback, mProxy, aParams.mTaskQueue); + auto params = GMPVideoDecoderParams(aParams).WithCallback(wrapper); + wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy, params)); + return wrapper.forget(); + } + + MOZ_ASSERT(mPDM); + RefPtr<MediaDataDecoder> decoder(mPDM->CreateDecoder(aParams)); + if (!decoder) { + return nullptr; + } + + RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder, + aParams.mCallback, + mProxy, + AbstractThread::GetCurrent()->AsTaskQueue())); + return emeDecoder.forget(); +} + +already_AddRefed<MediaDataDecoder> +EMEDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams) +{ + MOZ_ASSERT(aParams.mConfig.mCrypto.mValid); + + if (SupportsMimeType(aParams.mConfig.mMimeType, nullptr)) { + // GMP decodes. Assume that means it can decrypt too. + RefPtr<MediaDataDecoderProxy> wrapper = + CreateDecoderWrapper(aParams.mCallback, mProxy, aParams.mTaskQueue); + auto gmpParams = GMPAudioDecoderParams(aParams).WithCallback(wrapper); + wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy, gmpParams)); + return wrapper.forget(); + } + + MOZ_ASSERT(mPDM); + RefPtr<MediaDataDecoder> decoder(mPDM->CreateDecoder(aParams)); + if (!decoder) { + return nullptr; + } + + RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder, + aParams.mCallback, + mProxy, + AbstractThread::GetCurrent()->AsTaskQueue())); + return emeDecoder.forget(); +} + +PlatformDecoderModule::ConversionRequired +EMEDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const +{ + if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) { + return ConversionRequired::kNeedAVCC; + } else { + return ConversionRequired::kNeedNone; + } +} + +bool +EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const +{ + Maybe<nsCString> gmp; + gmp.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem())); + return GMPDecoderModule::SupportsMimeType(aMimeType, gmp); +} + +} // namespace mozilla diff --git a/dom/media/platforms/agnostic/eme/EMEDecoderModule.h b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h new file mode 100644 index 000000000..32074ae8c --- /dev/null +++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if !defined(EMEDecoderModule_h_) +#define EMEDecoderModule_h_ + +#include "PlatformDecoderModule.h" +#include "PDMFactory.h" +#include "gmp-decryption.h" + +namespace mozilla { + +class CDMProxy; + +class EMEDecoderModule : public PlatformDecoderModule { +private: + +public: + EMEDecoderModule(CDMProxy* aProxy, PDMFactory* aPDM); + + virtual ~EMEDecoderModule(); + +protected: + // Decode thread. + already_AddRefed<MediaDataDecoder> + CreateVideoDecoder(const CreateDecoderParams& aParams) override; + + // Decode thread. + already_AddRefed<MediaDataDecoder> + CreateAudioDecoder(const CreateDecoderParams& aParams) override; + + ConversionRequired + DecoderNeedsConversion(const TrackInfo& aConfig) const override; + + bool + SupportsMimeType(const nsACString& aMimeType, + DecoderDoctorDiagnostics* aDiagnostics) const override; + +private: + RefPtr<CDMProxy> mProxy; + // Will be null if CDM has decoding capability. + RefPtr<PDMFactory> mPDM; + // We run the PDM on its own task queue. + RefPtr<TaskQueue> mTaskQueue; +}; + +} // namespace mozilla + +#endif // EMEDecoderModule_h_ diff --git a/dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp new file mode 100644 index 000000000..bddb89660 --- /dev/null +++ b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "EMEVideoDecoder.h" +#include "GMPVideoEncodedFrameImpl.h" +#include "mozilla/CDMProxy.h" +#include "MediaData.h" +#include "MP4Decoder.h" +#include "VPXDecoder.h" + +namespace mozilla { + +void +EMEVideoCallbackAdapter::Error(GMPErr aErr) +{ + if (aErr == GMPNoKeyErr) { + // The GMP failed to decrypt a frame due to not having a key. This can + // happen if a key expires or a session is closed during playback. + NS_WARNING("GMP failed to decrypt due to lack of key"); + return; + } + VideoCallbackAdapter::Error(aErr); +} + +EMEVideoDecoder::EMEVideoDecoder(CDMProxy* aProxy, + const GMPVideoDecoderParams& aParams) + : GMPVideoDecoder(GMPVideoDecoderParams(aParams).WithAdapter( + new EMEVideoCallbackAdapter(aParams.mCallback, + VideoInfo(aParams.mConfig.mDisplay), + aParams.mImageContainer))) + , mProxy(aProxy) + , mDecryptorId(aProxy->GetDecryptorId()) +{} + +void +EMEVideoDecoder::InitTags(nsTArray<nsCString>& aTags) +{ + VideoInfo config = GetConfig(); + if (MP4Decoder::IsH264(config.mMimeType)) { + aTags.AppendElement(NS_LITERAL_CSTRING("h264")); + } else if (VPXDecoder::IsVP8(config.mMimeType)) { + aTags.AppendElement(NS_LITERAL_CSTRING("vp8")); + } else if (VPXDecoder::IsVP9(config.mMimeType)) { + aTags.AppendElement(NS_LITERAL_CSTRING("vp9")); + } + aTags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem())); +} + +nsCString +EMEVideoDecoder::GetNodeId() +{ + return mProxy->GetNodeId(); +} + +GMPUniquePtr<GMPVideoEncodedFrame> +EMEVideoDecoder::CreateFrame(MediaRawData* aSample) +{ + GMPUniquePtr<GMPVideoEncodedFrame> frame = GMPVideoDecoder::CreateFrame(aSample); + if (frame && aSample->mCrypto.mValid) { + static_cast<gmp::GMPVideoEncodedFrameImpl*>(frame.get())->InitCrypto(aSample->mCrypto); + } + return frame; +} + +} // namespace mozilla diff --git a/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h new file mode 100644 index 000000000..a0f23f867 --- /dev/null +++ b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef EMEVideoDecoder_h_ +#define EMEVideoDecoder_h_ + +#include "GMPVideoDecoder.h" +#include "PlatformDecoderModule.h" + +namespace mozilla { + +class CDMProxy; +class TaskQueue; + +class EMEVideoCallbackAdapter : public VideoCallbackAdapter { +public: + EMEVideoCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback, + VideoInfo aVideoInfo, + layers::ImageContainer* aImageContainer) + : VideoCallbackAdapter(aCallback, aVideoInfo, aImageContainer) + {} + + void Error(GMPErr aErr) override; +}; + +class EMEVideoDecoder : public GMPVideoDecoder { +public: + EMEVideoDecoder(CDMProxy* aProxy, const GMPVideoDecoderParams& aParams); + +private: + void InitTags(nsTArray<nsCString>& aTags) override; + nsCString GetNodeId() override; + uint32_t DecryptorId() const override { return mDecryptorId; } + GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample) override; + + RefPtr<CDMProxy> mProxy; + uint32_t mDecryptorId; +}; + +} // namespace mozilla + +#endif // EMEVideoDecoder_h_ diff --git a/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.cpp b/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.cpp new file mode 100644 index 000000000..58098c2b3 --- /dev/null +++ b/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.cpp @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SamplesWaitingForKey.h" +#include "mozilla/CDMProxy.h" +#include "mozilla/CDMCaps.h" +#include "MediaData.h" + +namespace mozilla { + +SamplesWaitingForKey::SamplesWaitingForKey(MediaDataDecoder* aDecoder, + MediaDataDecoderCallback* aCallback, + TaskQueue* aTaskQueue, + CDMProxy* aProxy) + : mMutex("SamplesWaitingForKey") + , mDecoder(aDecoder) + , mDecoderCallback(aCallback) + , mTaskQueue(aTaskQueue) + , mProxy(aProxy) +{ +} + +SamplesWaitingForKey::~SamplesWaitingForKey() +{ +} + +bool +SamplesWaitingForKey::WaitIfKeyNotUsable(MediaRawData* aSample) +{ + if (!aSample || !aSample->mCrypto.mValid || !mProxy) { + return false; + } + CDMCaps::AutoLock caps(mProxy->Capabilites()); + const auto& keyid = aSample->mCrypto.mKeyId; + if (!caps.IsKeyUsable(keyid)) { + { + MutexAutoLock lock(mMutex); + mSamples.AppendElement(aSample); + } + mDecoderCallback->WaitingForKey(); + caps.NotifyWhenKeyIdUsable(aSample->mCrypto.mKeyId, this); + return true; + } + return false; +} + +void +SamplesWaitingForKey::NotifyUsable(const CencKeyId& aKeyId) +{ + MutexAutoLock lock(mMutex); + size_t i = 0; + while (i < mSamples.Length()) { + if (aKeyId == mSamples[i]->mCrypto.mKeyId) { + RefPtr<nsIRunnable> task; + task = NewRunnableMethod<RefPtr<MediaRawData>>(mDecoder, + &MediaDataDecoder::Input, + RefPtr<MediaRawData>(mSamples[i])); + mSamples.RemoveElementAt(i); + mTaskQueue->Dispatch(task.forget()); + } else { + i++; + } + } +} + +void +SamplesWaitingForKey::Flush() +{ + MutexAutoLock lock(mMutex); + mSamples.Clear(); +} + +void +SamplesWaitingForKey::BreakCycles() +{ + MutexAutoLock lock(mMutex); + mDecoder = nullptr; + mTaskQueue = nullptr; + mProxy = nullptr; + mSamples.Clear(); +} + +} // namespace mozilla diff --git a/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.h b/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.h new file mode 100644 index 000000000..65bb14403 --- /dev/null +++ b/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef SamplesWaitingForKey_h_ +#define SamplesWaitingForKey_h_ + +#include "mozilla/TaskQueue.h" + +#include "PlatformDecoderModule.h" + +namespace mozilla { + +typedef nsTArray<uint8_t> CencKeyId; + +class CDMProxy; + +// Encapsulates the task of waiting for the CDMProxy to have the necessary +// keys to decrypt a given sample. +class SamplesWaitingForKey { +public: + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SamplesWaitingForKey) + + explicit SamplesWaitingForKey(MediaDataDecoder* aDecoder, + MediaDataDecoderCallback* aCallback, + TaskQueue* aTaskQueue, + CDMProxy* aProxy); + + // Returns true if we need to wait for a key to become usable. + // Will callback MediaDataDecoder::Input(aSample) on mDecoder once the + // sample is ready to be decrypted. The order of input samples is + // preserved. + bool WaitIfKeyNotUsable(MediaRawData* aSample); + + void NotifyUsable(const CencKeyId& aKeyId); + + void Flush(); + + void BreakCycles(); + +protected: + ~SamplesWaitingForKey(); + +private: + Mutex mMutex; + RefPtr<MediaDataDecoder> mDecoder; + MediaDataDecoderCallback* mDecoderCallback; + RefPtr<TaskQueue> mTaskQueue; + RefPtr<CDMProxy> mProxy; + nsTArray<RefPtr<MediaRawData>> mSamples; +}; + +} // namespace mozilla + +#endif // SamplesWaitingForKey_h_ diff --git a/dom/media/platforms/agnostic/eme/moz.build b/dom/media/platforms/agnostic/eme/moz.build new file mode 100644 index 000000000..78b76d4d4 --- /dev/null +++ b/dom/media/platforms/agnostic/eme/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS += [ + 'EMEAudioDecoder.h', + 'EMEDecoderModule.h', + 'EMEVideoDecoder.h', + 'SamplesWaitingForKey.h', +] + +UNIFIED_SOURCES += [ + 'EMEAudioDecoder.cpp', + 'EMEDecoderModule.cpp', + 'EMEVideoDecoder.cpp', + 'SamplesWaitingForKey.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' |