diff --git a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp index d9178ed240df..870668a9b05c 100644 --- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp @@ -2,471 +2,164 @@ * 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 -#include +#include +#include #include "ClearKeyDecryptionManager.h" -#include "ClearKeyUtils.h" -#include "ClearKeyStorage.h" -#include "ClearKeyPersistence.h" -#include "gmp-task-utils.h" - +#include "gmp-decryption.h" #include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" -using namespace mozilla; -using namespace std; - -class ClearKeyDecryptor +class ClearKeyDecryptor : public RefCounted { public: - ClearKeyDecryptor(GMPDecryptorCallback* aCallback, const Key& aKey); - ~ClearKeyDecryptor(); + MOZ_IMPLICIT ClearKeyDecryptor(); - void InitKey(); + void InitKey(const Key& aKey); + bool HasKey() const { return !!mKey.size(); } - void QueueDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata); - - uint32_t AddRef(); - uint32_t Release(); + GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, + GMPEncryptedBufferMetadata* aMetadata); const Key& DecryptionKey() const { return mKey; } private: - struct DecryptTask : public GMPTask - { - DecryptTask(ClearKeyDecryptor* aTarget, GMPBuffer* aBuffer, - GMPEncryptedBufferMetadata* aMetadata) - : mTarget(aTarget), mBuffer(aBuffer), mMetadata(aMetadata) { } - - virtual void Run() MOZ_OVERRIDE - { - mTarget->Decrypt(mBuffer, mMetadata); - } - - virtual void Destroy() MOZ_OVERRIDE { - delete this; - } - - virtual ~DecryptTask() { } - - ClearKeyDecryptor* mTarget; - GMPBuffer* mBuffer; - GMPEncryptedBufferMetadata* mMetadata; - }; - - struct DestroyTask : public GMPTask - { - explicit DestroyTask(ClearKeyDecryptor* aTarget) : mTarget(aTarget) { } - - virtual void Run() MOZ_OVERRIDE { - delete mTarget; - } - - virtual void Destroy() MOZ_OVERRIDE { - delete this; - } - - virtual ~DestroyTask() { } - - ClearKeyDecryptor* mTarget; - }; - - void Decrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata); - - uint32_t mRefCnt; - - GMPDecryptorCallback* mCallback; - GMPThread* mThread; + ~ClearKeyDecryptor(); Key mKey; }; + +/* static */ ClearKeyDecryptionManager* ClearKeyDecryptionManager::sInstance = nullptr; + +/* static */ ClearKeyDecryptionManager* +ClearKeyDecryptionManager::Get() +{ + if (!sInstance) { + sInstance = new ClearKeyDecryptionManager(); + } + return sInstance; +} + ClearKeyDecryptionManager::ClearKeyDecryptionManager() { - CK_LOGD("ClearKeyDecryptionManager ctor"); - - // The ClearKeyDecryptionManager maintains a self reference which is - // removed when the host is finished with the interface and calls - // DecryptingComplete(). We make ClearKeyDecryptionManager refcounted so - // that the tasks to that we dispatch to call functions on it won't end up - // derefing a null reference after DecryptingComplete() is called. - AddRef(); + CK_LOGD("ClearKeyDecryptionManager::ClearKeyDecryptionManager"); } ClearKeyDecryptionManager::~ClearKeyDecryptionManager() { - CK_LOGD("ClearKeyDecryptionManager dtor"); - MOZ_ASSERT(mRefCount == 1); + CK_LOGD("ClearKeyDecryptionManager::~ClearKeyDecryptionManager"); + + sInstance = nullptr; + + for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) { + it->second->Release(); + } + mDecryptors.clear(); } -void -ClearKeyDecryptionManager::Init(GMPDecryptorCallback* aCallback) +bool +ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const { - CK_LOGD("ClearKeyDecryptionManager::Init"); - mCallback = aCallback; - mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO | - GMP_EME_CAP_DECRYPT_VIDEO); - ClearKeyPersistence::EnsureInitialized(); + CK_LOGD("ClearKeyDecryptionManager::HasSeenKeyId"); + return mDecryptors.find(aKeyId) != mDecryptors.end(); } -void -ClearKeyDecryptionManager::CreateSession(uint32_t aCreateSessionToken, - uint32_t aPromiseId, - const char* aInitDataType, - uint32_t aInitDataTypeSize, - const uint8_t* aInitData, - uint32_t aInitDataSize, - GMPSessionType aSessionType) +bool +ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const { - CK_LOGD("ClearKeyDecryptionManager::CreateSession type:%s", aInitDataType); - - // initDataType must be "cenc". - if (strcmp("cenc", aInitDataType)) { - mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError, - nullptr /* message */, 0 /* messageLen */); - return; - } - - if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this, - aCreateSessionToken, - aPromiseId, - aInitData, - aInitDataSize, - aSessionType)) { - return; - } - - string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType); - MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end()); - - ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType); - session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize); - mSessions[sessionId] = session; - - const vector& sessionKeys = session->GetKeyIds(); - vector neededKeys; - for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) { - if (!Contains(mDecryptors, *it)) { - // Need to request this key ID from the client. - neededKeys.push_back(*it); - } else { - // We already have a key for this key ID. Mark as usable. - mCallback->KeyIdUsable(sessionId.c_str(), sessionId.length(), - &(*it)[0], it->size()); - } - } - - if (neededKeys.empty()) { - CK_LOGD("No keys needed from client."); - return; - } - - // Send a request for needed key data. - string request; - ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType); - mCallback->SessionMessage(&sessionId[0], sessionId.length(), - (uint8_t*)&request[0], request.length(), - "" /* destination url */, 0); + CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForKeyId"); + const auto& decryptor = mDecryptors.find(aKeyId); + return decryptor != mDecryptors.end() && !decryptor->second->HasKey(); } -void -ClearKeyDecryptionManager::LoadSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength) +bool +ClearKeyDecryptionManager::HasKeyForKeyId(const KeyId& aKeyId) const { - CK_LOGD("ClearKeyDecryptionManager::LoadSession"); - - if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) { - mCallback->ResolveLoadSessionPromise(aPromiseId, false); - return; - } - - if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this, - aPromiseId, - aSessionId, - aSessionIdLength)) { - return; - } - - string sid(aSessionId, aSessionId + aSessionIdLength); - if (!ClearKeyPersistence::IsPersistentSessionId(sid)) { - mCallback->ResolveLoadSessionPromise(aPromiseId, false); - return; - } - - // Callsback PersistentSessionDataLoaded with results... - ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId); + CK_LOGD("ClearKeyDecryptionManager::HasKeyForKeyId"); + const auto& decryptor = mDecryptors.find(aKeyId); + return decryptor != mDecryptors.end() && decryptor->second->HasKey(); } -void -ClearKeyDecryptionManager::PersistentSessionDataLoaded(GMPErr aStatus, - uint32_t aPromiseId, - const string& aSessionId, - const uint8_t* aKeyData, - uint32_t aKeyDataSize) +const Key& +ClearKeyDecryptionManager::GetDecryptionKey(const KeyId& aKeyId) { - if (GMP_FAILED(aStatus) || - Contains(mSessions, aSessionId) || - (aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) { - mCallback->ResolveLoadSessionPromise(aPromiseId, false); - return; - } - - ClearKeySession* session = new ClearKeySession(aSessionId, - mCallback, - kGMPPersistentSession); - mSessions[aSessionId] = session; - - uint32_t numKeys = aKeyDataSize / (2 * CLEARKEY_KEY_LEN); - for (uint32_t i = 0; i < numKeys; i ++) { - const uint8_t* base = aKeyData + 2 * CLEARKEY_KEY_LEN * i; - - KeyId keyId(base, base + CLEARKEY_KEY_LEN); - MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN); - - Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN); - MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN); - - session->AddKeyId(keyId); - - if (!Contains(mDecryptors, keyId)) { - mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, key); - } - mDecryptors[keyId]->AddRef(); - mCallback->KeyIdUsable(aSessionId.c_str(), aSessionId.size(), - &keyId[0], keyId.size()); - } - - mCallback->ResolveLoadSessionPromise(aPromiseId, true); + MOZ_ASSERT(HasKeyForKeyId(aKeyId)); + return mDecryptors[aKeyId]->DecryptionKey(); } void -ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength, - const uint8_t* aResponse, - uint32_t aResponseSize) +ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey) { - CK_LOGD("ClearKeyDecryptionManager::UpdateSession"); - string sessionId(aSessionId, aSessionId + aSessionIdLength); - - auto itr = mSessions.find(sessionId); - if (itr == mSessions.end() || !(itr->second)) { - CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession."); - mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0); - return; + CK_LOGD("ClearKeyDecryptionManager::InitKey"); + if (IsExpectingKeyForKeyId(aKeyId)) { + mDecryptors[aKeyId]->InitKey(aKey); } - ClearKeySession* session = itr->second; - - // Parse the response for any (key ID, key) pairs. - vector keyPairs; - if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) { - CK_LOGW("ClearKey CDM failed to parse JSON Web Key."); - mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0); - return; - } - - for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) { - KeyId& keyId = it->mKeyId; - - if (!Contains(mDecryptors, keyId)) { - mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, it->mKey); - mCallback->KeyIdUsable(aSessionId, aSessionIdLength, - &keyId[0], keyId.size()); - } - - mDecryptors[keyId]->AddRef(); - } - - if (session->Type() != kGMPPersistentSession) { - mCallback->ResolvePromise(aPromiseId); - return; - } - - // Store the keys on disk. We store a record whose name is the sessionId, - // and simply append each keyId followed by its key. - vector keydata; - Serialize(session, keydata); - GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId); - static const char* message = "Couldn't store cenc key init data"; - GMPTask* reject = WrapTask(mCallback, - &GMPDecryptorCallback::RejectPromise, - aPromiseId, - kGMPInvalidStateError, - message, - strlen(message)); - StoreData(sessionId, keydata, resolve, reject); } void -ClearKeyDecryptionManager::Serialize(const ClearKeySession* aSession, - std::vector& aOutKeyData) +ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId) { - const std::vector& keyIds = aSession->GetKeyIds(); - for (size_t i = 0; i < keyIds.size(); i++) { - const KeyId& keyId = keyIds[i]; - if (!Contains(mDecryptors, keyId)) { - continue; - } - MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN); - aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end()); - const Key& key = mDecryptors[keyId]->DecryptionKey(); - MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN); - aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end()); + CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId"); + if (!HasSeenKeyId(aKeyId)) { + mDecryptors[aKeyId] = new ClearKeyDecryptor(); } + mDecryptors[aKeyId]->AddRef(); } void -ClearKeyDecryptionManager::CloseSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength) +ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId) { - CK_LOGD("ClearKeyDecryptionManager::CloseSession"); - - string sessionId(aSessionId, aSessionId + aSessionIdLength); - auto itr = mSessions.find(sessionId); - if (itr == mSessions.end()) { - CK_LOGW("ClearKey CDM couldn't close non-existent session."); - mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0); - return; + CK_LOGD("ClearKeyDecryptionManager::ReleaseKeyId"); + ClearKeyDecryptor* decryptor = mDecryptors[aKeyId]; + if (!decryptor->Release()) { + mDecryptors.erase(aKeyId); } - - ClearKeySession* session = itr->second; - MOZ_ASSERT(session); - - ClearInMemorySessionData(session); - mCallback->ResolvePromise(aPromiseId); - mCallback->SessionClosed(aSessionId, aSessionIdLength); } -void -ClearKeyDecryptionManager::ClearInMemorySessionData(ClearKeySession* aSession) -{ - MOZ_ASSERT(aSession); - - const vector& keyIds = aSession->GetKeyIds(); - for (auto it = keyIds.begin(); it != keyIds.end(); it++) { - MOZ_ASSERT(Contains(mDecryptors, *it)); - if (!mDecryptors[*it]->Release()) { - mDecryptors.erase(*it); - mCallback->KeyIdNotUsable(aSession->Id().c_str(), aSession->Id().size(), - &(*it)[0], it->size()); - } - } - - mSessions.erase(aSession->Id()); - delete aSession; -} - -void -ClearKeyDecryptionManager::RemoveSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength) -{ - string sessionId(aSessionId, aSessionId + aSessionIdLength); - auto itr = mSessions.find(sessionId); - if (itr == mSessions.end()) { - CK_LOGW("ClearKey CDM couldn't remove non-existent session."); - mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0); - return; - } - - ClearKeySession* session = itr->second; - MOZ_ASSERT(session); - string sid = session->Id(); - bool isPersistent = session->Type() == kGMPPersistentSession; - ClearInMemorySessionData(session); - - if (!isPersistent) { - mCallback->ResolvePromise(aPromiseId); - return; - } - - ClearKeyPersistence::PersistentSessionRemoved(sid); - - // Overwrite the record storing the sessionId's key data with a zero - // length record to delete it. - vector emptyKeydata; - GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId); - static const char* message = "Could not remove session"; - GMPTask* reject = WrapTask(mCallback, - &GMPDecryptorCallback::RejectPromise, - aPromiseId, - kGMPInvalidAccessError, - message, - strlen(message)); - StoreData(sessionId, emptyKeydata, resolve, reject); -} - -void -ClearKeyDecryptionManager::SetServerCertificate(uint32_t aPromiseId, - const uint8_t* aServerCert, - uint32_t aServerCertSize) -{ - // ClearKey CDM doesn't support this method by spec. - CK_LOGD("ClearKeyDecryptionManager::SetServerCertificate"); - mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError, - nullptr /* message */, 0 /* messageLen */); -} - -void -ClearKeyDecryptionManager::Decrypt(GMPBuffer* aBuffer, +GMPErr +ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, GMPEncryptedBufferMetadata* aMetadata) { CK_LOGD("ClearKeyDecryptionManager::Decrypt"); KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize()); - if (!Contains(mDecryptors, keyId)) { - CK_LOGD("ClearKeyDecryptionManager::Decrypt GMPNoKeyErr"); - mCallback->Decrypted(aBuffer, GMPNoKeyErr); - return; + if (!HasKeyForKeyId(keyId)) { + return GMPNoKeyErr; } - mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata); + return mDecryptors[keyId]->Decrypt(aBuffer, aBufferSize, aMetadata); } -void -ClearKeyDecryptionManager::DecryptingComplete() +ClearKeyDecryptor::ClearKeyDecryptor() { - CK_LOGD("ClearKeyDecryptionManager::DecryptingComplete"); - - for (auto it = mSessions.begin(); it != mSessions.end(); it++) { - delete it->second; - } - mSessions.clear(); - - for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) { - delete it->second; - } - mDecryptors.clear(); - - Release(); + CK_LOGD("ClearKeyDecryptor ctor"); } -void -ClearKeyDecryptor::QueueDecrypt(GMPBuffer* aBuffer, - GMPEncryptedBufferMetadata* aMetadata) +ClearKeyDecryptor::~ClearKeyDecryptor() { - CK_LOGD("ClearKeyDecryptor::QueueDecrypt"); - mThread->Post(new DecryptTask(this, aBuffer, aMetadata)); + CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]); } void -ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer, +ClearKeyDecryptor::InitKey(const Key& aKey) +{ + mKey = aKey; +} + +GMPErr +ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, GMPEncryptedBufferMetadata* aMetadata) { - if (!mThread) { - mCallback->Decrypted(aBuffer, GMPGenericErr); - } - + CK_LOGD("ClearKeyDecryptor::Decrypt"); // If the sample is split up into multiple encrypted subsamples, we need to // stitch them into one continuous buffer for decryption. - vector tmp(aBuffer->Size()); + std::vector tmp(aBufferSize); if (aMetadata->NumSubsamples()) { // Take all encrypted parts of subsamples and stitch them into one // continuous encrypted buffer. - unsigned char* data = aBuffer->Data(); + unsigned char* data = aBuffer; unsigned char* iter = &tmp[0]; for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) { data += aMetadata->ClearBytes()[i]; @@ -480,11 +173,11 @@ ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer, tmp.resize((size_t)(iter - &tmp[0])); } else { - memcpy(&tmp[0], aBuffer->Data(), aBuffer->Size()); + memcpy(&tmp[0], aBuffer, aBufferSize); } MOZ_ASSERT(aMetadata->IVSize() == 8 || aMetadata->IVSize() == 16); - vector iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize()); + std::vector iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize()); iv.insert(iv.end(), CLEARKEY_KEY_LEN - aMetadata->IVSize(), 0); ClearKeyUtils::DecryptAES(mKey, tmp, iv); @@ -492,7 +185,7 @@ ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer, if (aMetadata->NumSubsamples()) { // Take the decrypted buffer, split up into subsamples, and insert those // subsamples back into their original position in the original buffer. - unsigned char* data = aBuffer->Data(); + unsigned char* data = aBuffer; unsigned char* iter = &tmp[0]; for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) { data += aMetadata->ClearBytes()[i]; @@ -504,52 +197,8 @@ ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer, iter += cipherBytes; } } else { - memcpy(aBuffer->Data(), &tmp[0], aBuffer->Size()); + memcpy(aBuffer, &tmp[0], aBufferSize); } - mCallback->Decrypted(aBuffer, GMPNoErr); -} - -ClearKeyDecryptor::ClearKeyDecryptor(GMPDecryptorCallback* aCallback, - const Key& aKey) - : mRefCnt(0) - , mCallback(aCallback) - , mThread(nullptr) - , mKey(aKey) -{ - if (GetPlatform()->createthread(&mThread) != GMPNoErr) { - CK_LOGD("failed to create thread in clearkey cdm"); - mThread = nullptr; - return; - } -} - -ClearKeyDecryptor::~ClearKeyDecryptor() -{ - CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]); -} - -uint32_t -ClearKeyDecryptor::AddRef() -{ - return ++mRefCnt; -} - -uint32_t -ClearKeyDecryptor::Release() -{ - uint32_t newCount = --mRefCnt; - if (!newCount) { - if (mThread) { - // Shutdown mThread. We cache a pointer to mThread, as the DestroyTask - // may run and delete |this| before Post() returns. - GMPThread* thread = mThread; - thread->Post(new DestroyTask(this)); - thread->Join(); - } else { - delete this; - } - } - - return newCount; + return GMPNoErr; } diff --git a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h index 3c6ef1b270c6..39d9f2ab7441 100644 --- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h +++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h @@ -2,79 +2,46 @@ * 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 __ClearKeyDecryptor_h__ -#define __ClearKeyDecryptor_h__ +#ifndef __ClearKeyDecryptionManager_h__ +#define __ClearKeyDecryptionManager_h__ #include -#include -#include -#include "ClearKeySession.h" #include "ClearKeyUtils.h" -#include "gmp-api/gmp-decryption.h" -#include "ScopedNSSTypes.h" #include "RefCounted.h" class ClearKeyDecryptor; -class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor - , public RefCounted + +class ClearKeyDecryptionManager : public RefCounted { -public: - ClearKeyDecryptionManager(); - - virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE; - - virtual void CreateSession(uint32_t aCreateSessionToken, - uint32_t aPromiseId, - const char* aInitDataType, - uint32_t aInitDataTypeSize, - const uint8_t* aInitData, - uint32_t aInitDataSize, - GMPSessionType aSessionType) MOZ_OVERRIDE; - - virtual void LoadSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength) MOZ_OVERRIDE; - - virtual void UpdateSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength, - const uint8_t* aResponse, - uint32_t aResponseSize) MOZ_OVERRIDE; - - virtual void CloseSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength) MOZ_OVERRIDE; - - virtual void RemoveSession(uint32_t aPromiseId, - const char* aSessionId, - uint32_t aSessionIdLength) MOZ_OVERRIDE; - - virtual void SetServerCertificate(uint32_t aPromiseId, - const uint8_t* aServerCert, - uint32_t aServerCertSize) MOZ_OVERRIDE; - - virtual void Decrypt(GMPBuffer* aBuffer, - GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE; - - virtual void DecryptingComplete() MOZ_OVERRIDE; - - void PersistentSessionDataLoaded(GMPErr aStatus, - uint32_t aPromiseId, - const std::string& aSessionId, - const uint8_t* aKeyData, - uint32_t aKeyDataSize); - private: + ClearKeyDecryptionManager(); ~ClearKeyDecryptionManager(); - void ClearInMemorySessionData(ClearKeySession* aSession); - void Serialize(const ClearKeySession* aSession, std::vector& aOutKeyData); + static ClearKeyDecryptionManager* sInstance; - GMPDecryptorCallback* mCallback; +public: + static ClearKeyDecryptionManager* Get(); + + bool HasSeenKeyId(const KeyId& aKeyId) const; + bool HasKeyForKeyId(const KeyId& aKeyId) const; + + const Key& GetDecryptionKey(const KeyId& aKeyId); + + // Create a decryptor for the given KeyId if one does not already exist. + void InitKey(KeyId aKeyId, Key aKey); + void ExpectKeyId(KeyId aKeyId); + void ReleaseKeyId(KeyId aKeyId); + + GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, + GMPEncryptedBufferMetadata* aMetadata); + + void Shutdown(); + +private: + bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const; std::map mDecryptors; - std::map mSessions; }; -#endif // __ClearKeyDecryptor_h__ +#endif // __ClearKeyDecryptionManager_h__ diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp index 369679bc0db7..fbaf0ab3d4bb 100644 --- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp @@ -5,7 +5,7 @@ #include "ClearKeyPersistence.h" #include "ClearKeyUtils.h" #include "ClearKeyStorage.h" -#include "ClearKeyDecryptionManager.h" +#include "ClearKeySessionManager.h" #include "mozilla/RefPtr.h" #include @@ -97,7 +97,7 @@ ClearKeyPersistence::GetNewSessionId(GMPSessionType aSessionType) class CreateSessionTask : public GMPTask { public: - CreateSessionTask(ClearKeyDecryptionManager* aTarget, + CreateSessionTask(ClearKeySessionManager* aTarget, uint32_t aCreateSessionToken, uint32_t aPromiseId, const uint8_t* aInitData, @@ -125,7 +125,7 @@ public: delete this; } private: - RefPtr mTarget; + RefPtr mTarget; uint32_t mCreateSessionToken; uint32_t mPromiseId; vector mInitData; @@ -134,7 +134,7 @@ private: /* static */ bool -ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance, +ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aCreateSessionToken, uint32_t aPromiseId, const uint8_t* aInitData, @@ -156,7 +156,7 @@ ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aIn class LoadSessionTask : public GMPTask { public: - LoadSessionTask(ClearKeyDecryptionManager* aTarget, + LoadSessionTask(ClearKeySessionManager* aTarget, uint32_t aPromiseId, const char* aSessionId, uint32_t aSessionIdLength) @@ -174,13 +174,13 @@ public: delete this; } private: - RefPtr mTarget; + RefPtr mTarget; uint32_t mPromiseId; string mSessionId; }; /* static */ bool -ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance, +ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aPromiseId, const char* aSessionId, uint32_t aSessionIdLength) @@ -204,7 +204,7 @@ ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId) class LoadSessionFromKeysTask : public ReadContinuation { public: - LoadSessionFromKeysTask(ClearKeyDecryptionManager* aTarget, + LoadSessionFromKeysTask(ClearKeySessionManager* aTarget, const string& aSessionId, uint32_t aPromiseId) : mTarget(aTarget) @@ -220,13 +220,13 @@ public: mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength); } private: - RefPtr mTarget; + RefPtr mTarget; string mSessionId; uint32_t mPromiseId; }; /* static */ void -ClearKeyPersistence::LoadSessionData(ClearKeyDecryptionManager* aInstance, +ClearKeyPersistence::LoadSessionData(ClearKeySessionManager* aInstance, const string& aSid, uint32_t aPromiseId) { diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.h b/media/gmp-clearkey/0.1/ClearKeyPersistence.h index f37442944819..1bd3800e0b45 100644 --- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h @@ -8,7 +8,7 @@ #include #include "gmp-decryption.h" -class ClearKeyDecryptionManager; +class ClearKeySessionManager; class ClearKeyPersistence { public: @@ -16,21 +16,21 @@ public: static std::string GetNewSessionId(GMPSessionType aSessionType); - static bool DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance, + static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aCreateSessionToken, uint32_t aPromiseId, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType); - static bool DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance, + static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aPromiseId, const char* aSessionId, uint32_t aSessionIdLength); static bool IsPersistentSessionId(const std::string& aSid); - static void LoadSessionData(ClearKeyDecryptionManager* aInstance, + static void LoadSessionData(ClearKeySessionManager* aInstance, const std::string& aSid, uint32_t aPromiseId); diff --git a/media/gmp-clearkey/0.1/ClearKeySession.h b/media/gmp-clearkey/0.1/ClearKeySession.h index 78c3ad3d4b3c..68f498aaf012 100644 --- a/media/gmp-clearkey/0.1/ClearKeySession.h +++ b/media/gmp-clearkey/0.1/ClearKeySession.h @@ -32,7 +32,7 @@ public: void AddKeyId(const KeyId& aKeyId); - const std::string Id() const { return mSessionId; } + const std::string& Id() const { return mSessionId; } private: const std::string mSessionId; diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp new file mode 100644 index 000000000000..6901e9bfd775 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp @@ -0,0 +1,393 @@ +/* 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 +#include + +#include "ClearKeyDecryptionManager.h" +#include "ClearKeySessionManager.h" +#include "ClearKeyUtils.h" +#include "ClearKeyStorage.h" +#include "ClearKeyPersistence.h" +#include "gmp-task-utils.h" + +#include "mozilla/Assertions.h" + +using namespace mozilla; +using namespace std; + +ClearKeySessionManager::ClearKeySessionManager() + : mDecryptionManager(ClearKeyDecryptionManager::Get()) +{ + CK_LOGD("ClearKeySessionManager ctor"); + AddRef(); + + if (GetPlatform()->createthread(&mThread) != GMPNoErr) { + CK_LOGD("failed to create thread in clearkey cdm"); + mThread = nullptr; + } +} + +ClearKeySessionManager::~ClearKeySessionManager() +{ + CK_LOGD("ClearKeySessionManager dtor"); + MOZ_ASSERT(!mRefCount); +} + +void +ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback) +{ + CK_LOGD("ClearKeySessionManager::Init"); + mCallback = aCallback; + mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO | + GMP_EME_CAP_DECRYPT_VIDEO); + ClearKeyPersistence::EnsureInitialized(); +} + +void +ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken, + uint32_t aPromiseId, + const char* aInitDataType, + uint32_t aInitDataTypeSize, + const uint8_t* aInitData, + uint32_t aInitDataSize, + GMPSessionType aSessionType) +{ + CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType); + + // initDataType must be "cenc". + if (strcmp("cenc", aInitDataType)) { + mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError, + nullptr /* message */, 0 /* messageLen */); + return; + } + + if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this, + aCreateSessionToken, + aPromiseId, + aInitData, + aInitDataSize, + aSessionType)) { + return; + } + + string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType); + MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end()); + + ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType); + session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize); + mSessions[sessionId] = session; + + const vector& sessionKeys = session->GetKeyIds(); + vector neededKeys; + for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) { + // Need to request this key ID from the client. We always send a key + // request, whether or not another session has sent a request with the same + // key ID. Otherwise a script can end up waiting for another script to + // respond to the request (which may not necessarily happen). + neededKeys.push_back(*it); + mDecryptionManager->ExpectKeyId(*it); + } + + if (neededKeys.empty()) { + CK_LOGD("No keys needed from client."); + return; + } + + // Send a request for needed key data. + string request; + ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType); + mCallback->SessionMessage(&sessionId[0], sessionId.length(), + (uint8_t*)&request[0], request.length(), + "" /* destination url */, 0); +} + +void +ClearKeySessionManager::LoadSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) +{ + CK_LOGD("ClearKeySessionManager::LoadSession"); + + if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) { + mCallback->ResolveLoadSessionPromise(aPromiseId, false); + return; + } + + if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this, + aPromiseId, + aSessionId, + aSessionIdLength)) { + return; + } + + string sid(aSessionId, aSessionId + aSessionIdLength); + if (!ClearKeyPersistence::IsPersistentSessionId(sid)) { + mCallback->ResolveLoadSessionPromise(aPromiseId, false); + return; + } + + // Callsback PersistentSessionDataLoaded with results... + ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId); +} + +void +ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus, + uint32_t aPromiseId, + const string& aSessionId, + const uint8_t* aKeyData, + uint32_t aKeyDataSize) +{ + CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded"); + if (GMP_FAILED(aStatus) || + Contains(mSessions, aSessionId) || + (aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) { + mCallback->ResolveLoadSessionPromise(aPromiseId, false); + return; + } + + ClearKeySession* session = new ClearKeySession(aSessionId, + mCallback, + kGMPPersistentSession); + mSessions[aSessionId] = session; + + uint32_t numKeys = aKeyDataSize / (2 * CLEARKEY_KEY_LEN); + for (uint32_t i = 0; i < numKeys; i ++) { + const uint8_t* base = aKeyData + 2 * CLEARKEY_KEY_LEN * i; + + KeyId keyId(base, base + CLEARKEY_KEY_LEN); + MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN); + + Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN); + MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN); + + session->AddKeyId(keyId); + + mDecryptionManager->ExpectKeyId(keyId); + mDecryptionManager->InitKey(keyId, key); + mKeyIds.insert(key); + mCallback->KeyIdUsable(&aSessionId[0], aSessionId.size(), + &keyId[0], keyId.size()); + } + + mCallback->ResolveLoadSessionPromise(aPromiseId, true); +} + +void +ClearKeySessionManager::UpdateSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aResponse, + uint32_t aResponseSize) +{ + CK_LOGD("ClearKeySessionManager::UpdateSession"); + string sessionId(aSessionId, aSessionId + aSessionIdLength); + + auto itr = mSessions.find(sessionId); + if (itr == mSessions.end() || !(itr->second)) { + CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession."); + mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0); + return; + } + ClearKeySession* session = itr->second; + + // Parse the response for any (key ID, key) pairs. + vector keyPairs; + if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) { + CK_LOGW("ClearKey CDM failed to parse JSON Web Key."); + mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0); + return; + } + + for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) { + mDecryptionManager->InitKey(it->mKeyId, it->mKey); + mKeyIds.insert(it->mKeyId); + mCallback->KeyIdUsable(aSessionId, aSessionIdLength, + &it->mKeyId[0], it->mKeyId.size()); + } + + if (session->Type() != kGMPPersistentSession) { + mCallback->ResolvePromise(aPromiseId); + return; + } + + // Store the keys on disk. We store a record whose name is the sessionId, + // and simply append each keyId followed by its key. + vector keydata; + Serialize(session, keydata); + GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId); + static const char* message = "Couldn't store cenc key init data"; + GMPTask* reject = WrapTask(mCallback, + &GMPDecryptorCallback::RejectPromise, + aPromiseId, + kGMPInvalidStateError, + message, + strlen(message)); + StoreData(sessionId, keydata, resolve, reject); +} + +void +ClearKeySessionManager::Serialize(const ClearKeySession* aSession, + std::vector& aOutKeyData) +{ + const std::vector& keyIds = aSession->GetKeyIds(); + for (size_t i = 0; i < keyIds.size(); i++) { + const KeyId& keyId = keyIds[i]; + if (!mDecryptionManager->HasKeyForKeyId(keyId)) { + continue; + } + MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN); + aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end()); + const Key& key = mDecryptionManager->GetDecryptionKey(keyId); + MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN); + aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end()); + } +} + +void +ClearKeySessionManager::CloseSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) +{ + CK_LOGD("ClearKeySessionManager::CloseSession"); + + string sessionId(aSessionId, aSessionId + aSessionIdLength); + auto itr = mSessions.find(sessionId); + if (itr == mSessions.end()) { + CK_LOGW("ClearKey CDM couldn't close non-existent session."); + mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0); + return; + } + + ClearKeySession* session = itr->second; + MOZ_ASSERT(session); + + ClearInMemorySessionData(session); + mCallback->ResolvePromise(aPromiseId); + mCallback->SessionClosed(aSessionId, aSessionIdLength); +} + +void +ClearKeySessionManager::ClearInMemorySessionData(ClearKeySession* aSession) +{ + MOZ_ASSERT(aSession); + + const vector& keyIds = aSession->GetKeyIds(); + for (auto it = keyIds.begin(); it != keyIds.end(); it++) { + MOZ_ASSERT(mDecryptionManager->HasKeyForKeyId(*it)); + mDecryptionManager->ReleaseKeyId(*it); + + const string& sessionId = aSession->Id(); + mCallback->KeyIdNotUsable(&sessionId[0], sessionId.size(), + &(*it)[0], it->size()); + } + + mSessions.erase(aSession->Id()); + delete aSession; +} + +void +ClearKeySessionManager::RemoveSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) +{ + CK_LOGD("ClearKeySessionManager::RemoveSession"); + string sessionId(aSessionId, aSessionId + aSessionIdLength); + auto itr = mSessions.find(sessionId); + if (itr == mSessions.end()) { + CK_LOGW("ClearKey CDM couldn't remove non-existent session."); + mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0); + return; + } + + ClearKeySession* session = itr->second; + MOZ_ASSERT(session); + string sid = session->Id(); + bool isPersistent = session->Type() == kGMPPersistentSession; + ClearInMemorySessionData(session); + + if (!isPersistent) { + mCallback->ResolvePromise(aPromiseId); + return; + } + + ClearKeyPersistence::PersistentSessionRemoved(sid); + + // Overwrite the record storing the sessionId's key data with a zero + // length record to delete it. + vector emptyKeydata; + GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId); + static const char* message = "Could not remove session"; + GMPTask* reject = WrapTask(mCallback, + &GMPDecryptorCallback::RejectPromise, + aPromiseId, + kGMPInvalidAccessError, + message, + strlen(message)); + StoreData(sessionId, emptyKeydata, resolve, reject); +} + +void +ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId, + const uint8_t* aServerCert, + uint32_t aServerCertSize) +{ + // ClearKey CDM doesn't support this method by spec. + CK_LOGD("ClearKeySessionManager::SetServerCertificate"); + mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError, + nullptr /* message */, 0 /* messageLen */); +} + +void +ClearKeySessionManager::Decrypt(GMPBuffer* aBuffer, + GMPEncryptedBufferMetadata* aMetadata) +{ + CK_LOGD("ClearKeySessionManager::Decrypt"); + + if (!mThread) { + CK_LOGW("No decrypt thread"); + mCallback->Decrypted(aBuffer, GMPGenericErr); + return; + } + + mThread->Post(WrapTask(this, + &ClearKeySessionManager::DoDecrypt, + aBuffer, aMetadata)); +} + +void +ClearKeySessionManager::DoDecrypt(GMPBuffer* aBuffer, + GMPEncryptedBufferMetadata* aMetadata) +{ + CK_LOGD("ClearKeySessionManager::DoDecrypt"); + + GMPErr rv = mDecryptionManager->Decrypt(aBuffer->Data(), aBuffer->Size(), + aMetadata); + CK_LOGD("DeDecrypt finished with code %x\n", rv); + mCallback->Decrypted(aBuffer, rv); +} + +void +ClearKeySessionManager::Shutdown() +{ + CK_LOGD("ClearKeySessionManager::Shutdown"); + + for (auto it = mSessions.begin(); it != mSessions.end(); it++) { + delete it->second; + } + mSessions.clear(); +} + +void +ClearKeySessionManager::DecryptingComplete() +{ + CK_LOGD("ClearKeySessionManager::DecryptingComplete"); + + GMPThread* thread = mThread; + thread->Join(); + + Shutdown(); + mDecryptionManager = nullptr; + Release(); +} diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.h b/media/gmp-clearkey/0.1/ClearKeySessionManager.h new file mode 100644 index 000000000000..19d8c67e7cdc --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h @@ -0,0 +1,88 @@ +/* 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 __ClearKeyDecryptor_h__ +#define __ClearKeyDecryptor_h__ + +#include +#include +#include +#include + +#include "ClearKeyDecryptionManager.h" +#include "ClearKeySession.h" +#include "ClearKeyUtils.h" +#include "gmp-api/gmp-decryption.h" +#include "mozilla/RefPtr.h" +#include "ScopedNSSTypes.h" +#include "RefCounted.h" + +class ClearKeySessionManager MOZ_FINAL : public GMPDecryptor + , public RefCounted +{ +public: + ClearKeySessionManager(); + + virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE; + + virtual void CreateSession(uint32_t aCreateSessionToken, + uint32_t aPromiseId, + const char* aInitDataType, + uint32_t aInitDataTypeSize, + const uint8_t* aInitData, + uint32_t aInitDataSize, + GMPSessionType aSessionType) MOZ_OVERRIDE; + + virtual void LoadSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) MOZ_OVERRIDE; + + virtual void UpdateSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aResponse, + uint32_t aResponseSize) MOZ_OVERRIDE; + + virtual void CloseSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) MOZ_OVERRIDE; + + virtual void RemoveSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) MOZ_OVERRIDE; + + virtual void SetServerCertificate(uint32_t aPromiseId, + const uint8_t* aServerCert, + uint32_t aServerCertSize) MOZ_OVERRIDE; + + virtual void Decrypt(GMPBuffer* aBuffer, + GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE; + + virtual void DecryptingComplete() MOZ_OVERRIDE; + + void PersistentSessionDataLoaded(GMPErr aStatus, + uint32_t aPromiseId, + const std::string& aSessionId, + const uint8_t* aKeyData, + uint32_t aKeyDataSize); + +private: + ~ClearKeySessionManager(); + + void DoDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata); + void Shutdown(); + + void ClearInMemorySessionData(ClearKeySession* aSession); + void Serialize(const ClearKeySession* aSession, std::vector& aOutKeyData); + + mozilla::RefPtr mDecryptionManager; + + GMPDecryptorCallback* mCallback; + GMPThread* mThread; + + std::set mKeyIds; + std::map mSessions; +}; + +#endif // __ClearKeyDecryptor_h__ diff --git a/media/gmp-clearkey/0.1/RefCounted.h b/media/gmp-clearkey/0.1/RefCounted.h index 849c75204b80..f5306759eb3e 100644 --- a/media/gmp-clearkey/0.1/RefCounted.h +++ b/media/gmp-clearkey/0.1/RefCounted.h @@ -12,12 +12,12 @@ public: ++mRefCount; } - void Release() { - if (mRefCount == 1) { + uint32_t Release() { + uint32_t newCount = --mRefCount; + if (!newCount) { delete this; - } else { - --mRefCount; } + return newCount; } protected: diff --git a/media/gmp-clearkey/0.1/gmp-clearkey.cpp b/media/gmp-clearkey/0.1/gmp-clearkey.cpp index c34d5b04656a..3835eb6a9d3a 100644 --- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp +++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp @@ -5,7 +5,7 @@ #include #include -#include "ClearKeyDecryptionManager.h" +#include "ClearKeySessionManager.h" #include "gmp-api/gmp-decryption.h" #include "gmp-api/gmp-platform.h" @@ -34,7 +34,7 @@ GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI) return GMPNotImplementedErr; } - *aPluginAPI = new ClearKeyDecryptionManager(); + *aPluginAPI = new ClearKeySessionManager(); return GMPNoErr; } diff --git a/media/gmp-clearkey/0.1/moz.build b/media/gmp-clearkey/0.1/moz.build index 8a5db291aef3..311b54289137 100644 --- a/media/gmp-clearkey/0.1/moz.build +++ b/media/gmp-clearkey/0.1/moz.build @@ -12,6 +12,7 @@ UNIFIED_SOURCES += [ 'ClearKeyDecryptionManager.cpp', 'ClearKeyPersistence.cpp', 'ClearKeySession.cpp', + 'ClearKeySessionManager.cpp', 'ClearKeyStorage.cpp', 'ClearKeyUtils.cpp', 'gmp-clearkey.cpp',