Backed out 3 changesets (bug 1318965) for frequent media test failures a=backout

Backed out changeset 3f756d8ee4cf (bug 1318965)
Backed out changeset 4bdf65d60c9e (bug 1318965)
Backed out changeset c1e2b6c14a7f (bug 1318965)

MozReview-Commit-ID: 6CPk5oS5AOw

--HG--
extra : source : babe3f8a0258fb592e17a590450de6ceb09460c3
This commit is contained in:
Wes Kocher 2017-01-13 13:23:31 -08:00
Родитель 565fdbe5d8
Коммит 07fd8cebf7
30 изменённых файлов: 3318 добавлений и 1485 удалений

Просмотреть файл

@ -72,7 +72,6 @@ EXPORTS += [
'GMPVideoHost.h', 'GMPVideoHost.h',
'GMPVideoi420FrameImpl.h', 'GMPVideoi420FrameImpl.h',
'GMPVideoPlaneImpl.h', 'GMPVideoPlaneImpl.h',
'widevine-adapter/content_decryption_module.h',
] ]
# We link GMPLoader into xul on Android and Linux as its code does not # We link GMPLoader into xul on Android and Linux as its code does not
@ -120,7 +119,7 @@ UNIFIED_SOURCES += [
'GMPVideoEncoderParent.cpp', 'GMPVideoEncoderParent.cpp',
'GMPVideoHost.cpp', 'GMPVideoHost.cpp',
'GMPVideoi420FrameImpl.cpp', 'GMPVideoi420FrameImpl.cpp',
'GMPVideoPlaneImpl.cpp' 'GMPVideoPlaneImpl.cpp',
] ]
DIRS += [ DIRS += [

Просмотреть файл

@ -313,17 +313,6 @@ WidevineDecryptor::OnResolveNewSessionPromise(uint32_t aPromiseId,
Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId); Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId);
return; return;
} }
// This is laid out in the API. If we fail to load a session we should
// call OnResolveNewSessionPromise with nullptr as the sessionId.
// We can safely assume this means that we have failed to load a session
// as the other methods specify calling 'OnRejectPromise' when they fail.
if (!aSessionId) {
Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) Failed to load session", aPromiseId);
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
return;
}
Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d)", aPromiseId); Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d)", aPromiseId);
auto iter = mPromiseIdToNewSessionTokens.find(aPromiseId); auto iter = mPromiseIdToNewSessionTokens.find(aPromiseId);
if (iter == mPromiseIdToNewSessionTokens.end()) { if (iter == mPromiseIdToNewSessionTokens.end()) {

Просмотреть файл

@ -0,0 +1,79 @@
/*
* Copyright 2015, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AnnexB.h"
#include "BigEndian.h"
#include <cstring>
using mozilla::BigEndian;
static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 };
/* static */ void
AnnexB::ConvertFrameInPlace(std::vector<uint8_t>& aBuffer)
{
for (size_t i = 0; i < aBuffer.size() - 4 - sizeof(kAnnexBDelimiter) + 1; ) {
uint32_t nalLen = BigEndian::readUint32(&aBuffer[i]);
memcpy(&aBuffer[i], kAnnexBDelimiter, sizeof(kAnnexBDelimiter));
i += nalLen + 4;
}
}
static void
ConvertParamSetToAnnexB(std::vector<uint8_t>::const_iterator& aIter,
size_t aCount,
std::vector<uint8_t>& aOutAnnexB)
{
for (size_t i = 0; i < aCount; i++) {
aOutAnnexB.insert(aOutAnnexB.end(), kAnnexBDelimiter,
kAnnexBDelimiter + sizeof(kAnnexBDelimiter));
uint16_t len = BigEndian::readUint16(&*aIter); aIter += 2;
aOutAnnexB.insert(aOutAnnexB.end(), aIter, aIter + len); aIter += len;
}
}
/* static */ void
AnnexB::ConvertConfig(const std::vector<uint8_t>& aBuffer,
std::vector<uint8_t>& aOutAnnexB)
{
// Skip past irrelevant headers
auto it = aBuffer.begin() + 5;
if (it >= aBuffer.end()) {
return;
}
size_t count = *(it++) & 31;
// Check that we have enough bytes for the Annex B conversion
// and the next size field. Bail if not.
if (it + count * 2 >= aBuffer.end()) {
return;
}
ConvertParamSetToAnnexB(it, count, aOutAnnexB);
// Check that we have enough bytes for the Annex B conversion.
count = *(it++);
if (it + count * 2 > aBuffer.end()) {
aOutAnnexB.clear();
return;
}
ConvertParamSetToAnnexB(it, count, aOutAnnexB);
}

Просмотреть файл

@ -0,0 +1,32 @@
/*
* Copyright 2015, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __AnnexB_h__
#define __AnnexB_h__
#include <cstdint>
#include <vector>
class AnnexB
{
public:
static void ConvertFrameInPlace(std::vector<uint8_t>& aBuffer);
static void ConvertConfig(const std::vector<uint8_t>& aBuffer,
std::vector<uint8_t>& aOutAnnexB);
};
#endif // __AnnexB_h__

Просмотреть файл

@ -0,0 +1,45 @@
/*
* Copyright 2015, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ClearKeyAsyncShutdown.h"
#include "gmp-task-utils.h"
ClearKeyAsyncShutdown::ClearKeyAsyncShutdown(GMPAsyncShutdownHost *aHostAPI)
: mHost(aHostAPI)
{
CK_LOGD("ClearKeyAsyncShutdown::ClearKeyAsyncShutdown");
AddRef();
}
ClearKeyAsyncShutdown::~ClearKeyAsyncShutdown()
{
CK_LOGD("ClearKeyAsyncShutdown::~ClearKeyAsyncShutdown");
}
void ShutdownTask(ClearKeyAsyncShutdown* aSelf, GMPAsyncShutdownHost* aHost)
{
// Dumb implementation that just immediately reports completion.
// Real GMPs should ensure they are properly shutdown.
CK_LOGD("ClearKeyAsyncShutdown::BeginShutdown calling ShutdownComplete");
aHost->ShutdownComplete();
aSelf->Release();
}
void ClearKeyAsyncShutdown::BeginShutdown()
{
CK_LOGD("ClearKeyAsyncShutdown::BeginShutdown dispatching asynchronous shutdown task");
GetPlatform()->runonmainthread(WrapTaskNM(ShutdownTask, this, mHost));
}

Просмотреть файл

@ -0,0 +1,37 @@
/*
* Copyright 2015, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __ClearKeyAsyncShutdown_h__
#define __ClearKeyAsyncShutdown_h__
#include "gmp-api/gmp-async-shutdown.h"
#include "RefCounted.h"
class ClearKeyAsyncShutdown : public GMPAsyncShutdown
, public RefCounted
{
public:
explicit ClearKeyAsyncShutdown(GMPAsyncShutdownHost *aHostAPI);
void BeginShutdown() override;
private:
virtual ~ClearKeyAsyncShutdown();
GMPAsyncShutdownHost* mHost;
};
#endif // __ClearKeyAsyncShutdown_h__

Просмотреть файл

@ -1,195 +0,0 @@
#include "ClearKeyCDM.h"
#include "ClearKeyUtils.h"
using namespace cdm;
ClearKeyCDM::ClearKeyCDM(Host_8* aHost)
{
mHost = aHost;
mSessionManager = new ClearKeySessionManager(mHost);
}
void
ClearKeyCDM::Initialize(bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState)
{
mSessionManager->Init(aAllowDistinctiveIdentifier,
aAllowPersistentState);
#ifdef ENABLE_WMF
mVideoDecoder = new VideoDecoder(mHost);
#endif
}
void
ClearKeyCDM::SetServerCertificate(uint32_t aPromiseId,
const uint8_t* aServerCertificateData,
uint32_t aServerCertificateDataSize)
{
mSessionManager->SetServerCertificate(aPromiseId,
aServerCertificateData,
aServerCertificateDataSize);
}
void
ClearKeyCDM::CreateSessionAndGenerateRequest(uint32_t aPromiseId,
SessionType aSessionType,
InitDataType aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize)
{
mSessionManager->CreateSession(aPromiseId,
aInitDataType,
aInitData,
aInitDataSize,
aSessionType);
}
void
ClearKeyCDM::LoadSession(uint32_t aPromiseId,
SessionType aSessionType,
const char* aSessionId,
uint32_t aSessionIdSize)
{
mSessionManager->LoadSession(aPromiseId,
aSessionId,
aSessionIdSize);
}
void
ClearKeyCDM::UpdateSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdSize,
const uint8_t* aResponse,
uint32_t aResponseSize)
{
mSessionManager->UpdateSession(aPromiseId,
aSessionId,
aSessionIdSize,
aResponse,
aResponseSize);
}
void
ClearKeyCDM::CloseSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdSize)
{
mSessionManager->CloseSession(aPromiseId,
aSessionId,
aSessionIdSize);
}
void
ClearKeyCDM::RemoveSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdSize)
{
mSessionManager->RemoveSession(aPromiseId,
aSessionId,
aSessionIdSize);
}
void
ClearKeyCDM::TimerExpired(void* aContext)
{
// Clearkey is not interested in timers, so this method has not been
// implemented.
assert(false);
}
Status
ClearKeyCDM::Decrypt(const InputBuffer& aEncryptedBuffer,
DecryptedBlock* aDecryptedBuffer)
{
return mSessionManager->Decrypt(aEncryptedBuffer, aDecryptedBuffer);
}
Status
ClearKeyCDM::InitializeAudioDecoder(
const AudioDecoderConfig& aAudioDecoderConfig)
{
// Audio decoding is not supported by Clearkey because Widevine doesn't
// support it and Clearkey's raison d'etre is to provide test coverage
// for paths that Widevine will exercise in the wild.
return Status::kDecodeError;
}
Status
ClearKeyCDM::InitializeVideoDecoder(
const VideoDecoderConfig& aVideoDecoderConfig)
{
#ifdef ENABLE_WMF
return mVideoDecoder->InitDecode(aVideoDecoderConfig);
#else
return Status::kDecodeError;
#endif
}
void
ClearKeyCDM::DeinitializeDecoder(StreamType aDecoderType)
{
#ifdef ENABLE_WMF
if (aDecoderType == StreamType::kStreamTypeVideo) {
mVideoDecoder->Reset();
}
#endif
}
void
ClearKeyCDM::ResetDecoder(StreamType aDecoderType)
{
#ifdef ENABLE_WMF
if (aDecoderType == StreamType::kStreamTypeVideo) {
mVideoDecoder->Reset();
}
#endif
}
Status
ClearKeyCDM::DecryptAndDecodeFrame(const InputBuffer& aEncryptedBuffer,
VideoFrame* aVideoFrame)
{
#ifdef ENABLE_WMF
return mVideoDecoder->Decode(aEncryptedBuffer, aVideoFrame);
#else
return Status::kDecodeError;
#endif
}
Status
ClearKeyCDM::DecryptAndDecodeSamples(const InputBuffer& aEncryptedBuffer,
AudioFrames* aAudioFrame)
{
// Audio decoding is not supported by Clearkey because Widevine doesn't
// support it and Clearkey's raison d'etre is to provide test coverage
// for paths that Widevine will exercise in the wild.
return Status::kDecodeError;
}
void
ClearKeyCDM::OnPlatformChallengeResponse(
const PlatformChallengeResponse& aResponse)
{
// This function should never be called and is not supported.
assert(false);
}
void
ClearKeyCDM::OnQueryOutputProtectionStatus(QueryResult aResult,
uint32_t aLinkMask,
uint32_t aOutputProtectionMask)
{
// This function should never be called and is not supported.
assert(false);
}
void
ClearKeyCDM::Destroy()
{
mSessionManager->DecryptingComplete();
#ifdef ENABLE_WMF
mVideoDecoder->DecodingComplete();
#endif
}

Просмотреть файл

@ -1,98 +0,0 @@
#ifndef ClearKeyCDM_h_
#define ClearKeyCDM_h_
#include "ClearKeySessionManager.h"
// This include is required in order for content_decryption_module to work
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#ifdef ENABLE_WMF
#include "WMFUtils.h"
#include "VideoDecoder.h"
#endif
class ClearKeyCDM : public cdm::ContentDecryptionModule_8
{
private:
RefPtr<ClearKeySessionManager> mSessionManager;
#ifdef ENABLE_WMF
RefPtr<VideoDecoder> mVideoDecoder;
#endif
protected:
cdm::Host_8* mHost;
public:
explicit ClearKeyCDM(cdm::Host_8* mHost);
void Initialize(bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState) override;
void SetServerCertificate(uint32_t aPromiseId,
const uint8_t* aServerCertificateData,
uint32_t aServerCertificateDataSize)
override;
void CreateSessionAndGenerateRequest(uint32_t aPromiseId,
cdm::SessionType aSessionType,
cdm::InitDataType aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize)
override;
void LoadSession(uint32_t aPromiseId,
cdm::SessionType aSessionType,
const char* aSessionId,
uint32_t aSessionIdSize) override;
void UpdateSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdSize,
const uint8_t* aResponse,
uint32_t aResponseSize) override;
void CloseSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdSize) override;
void RemoveSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdSize) override;
void TimerExpired(void* aContext) override;
cdm::Status Decrypt(const cdm::InputBuffer& aEncryptedBuffer,
cdm::DecryptedBlock* aDecryptedBuffer) override;
cdm::Status InitializeAudioDecoder(
const cdm::AudioDecoderConfig& aAudioDecoderConfig) override;
cdm::Status InitializeVideoDecoder(
const cdm::VideoDecoderConfig& aVideoDecoderConfig) override;
void DeinitializeDecoder(cdm::StreamType aDecoderType) override;
void ResetDecoder(cdm::StreamType aDecoderType) override;
cdm::Status DecryptAndDecodeFrame(
const cdm::InputBuffer& aEncryptedBuffer,
cdm::VideoFrame* aVideoFrame) override;
cdm::Status DecryptAndDecodeSamples(
const cdm::InputBuffer& aEncryptedBuffer,
cdm::AudioFrames* aAudioFrame) override;
void OnPlatformChallengeResponse(
const cdm::PlatformChallengeResponse& aResponse) override;
void
OnQueryOutputProtectionStatus(cdm::QueryResult aResult,
uint32_t aLinkMask,
uint32_t aOutputProtectionMask) override;
void Destroy() override;
};
#endif

Просмотреть файл

@ -14,15 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
#include "ClearKeyDecryptionManager.h"
#include "psshparser/PsshParser.h"
#include <assert.h>
#include <string.h> #include <string.h>
#include <vector> #include <vector>
using namespace cdm; #include "ClearKeyDecryptionManager.h"
#include "psshparser/PsshParser.h"
#include "gmp-api/gmp-decryption.h"
#include <assert.h>
class ClearKeyDecryptor : public RefCounted class ClearKeyDecryptor : public RefCounted
{ {
@ -32,7 +30,7 @@ public:
void InitKey(const Key& aKey); void InitKey(const Key& aKey);
bool HasKey() const { return !!mKey.size(); } bool HasKey() const { return !!mKey.size(); }
Status Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
const CryptoMetaData& aMetadata); const CryptoMetaData& aMetadata);
const Key& DecryptionKey() const { return mKey; } const Key& DecryptionKey() const { return mKey; }
@ -44,8 +42,7 @@ private:
}; };
/* static */ ClearKeyDecryptionManager* /* static */ ClearKeyDecryptionManager* ClearKeyDecryptionManager::sInstance = nullptr;
ClearKeyDecryptionManager::sInstance = nullptr;
/* static */ ClearKeyDecryptionManager* /* static */ ClearKeyDecryptionManager*
ClearKeyDecryptionManager::Get() ClearKeyDecryptionManager::Get()
@ -76,17 +73,14 @@ ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
bool bool
ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const
{ {
CK_LOGD("ClearKeyDecryptionManager::SeenKeyId %s", CK_LOGD("ClearKeyDecryptionManager::SeenKeyId %s", mDecryptors.find(aKeyId) != mDecryptors.end() ? "t" : "f");
mDecryptors.find(aKeyId) != mDecryptors.end() ? "t" : "f");
return mDecryptors.find(aKeyId) != mDecryptors.end(); return mDecryptors.find(aKeyId) != mDecryptors.end();
} }
bool bool
ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const
{ {
CK_LOGARRAY("ClearKeyDecryptionManager::IsExpectingKeyForId ", CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForId %08x...", *(uint32_t*)&aKeyId[0]);
aKeyId.data(),
aKeyId.size());
const auto& decryptor = mDecryptors.find(aKeyId); const auto& decryptor = mDecryptors.find(aKeyId);
return decryptor != mDecryptors.end() && !decryptor->second->HasKey(); return decryptor != mDecryptors.end() && !decryptor->second->HasKey();
} }
@ -109,23 +103,16 @@ ClearKeyDecryptionManager::GetDecryptionKey(const KeyId& aKeyId)
void void
ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey) ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey)
{ {
CK_LOGD("ClearKeyDecryptionManager::InitKey ", CK_LOGD("ClearKeyDecryptionManager::InitKey %08x...", *(uint32_t*)&aKeyId[0]);
aKeyId.data(),
aKeyId.size());
if (IsExpectingKeyForKeyId(aKeyId)) { if (IsExpectingKeyForKeyId(aKeyId)) {
CK_LOGARRAY("Initialized Key ", aKeyId.data(), aKeyId.size());
mDecryptors[aKeyId]->InitKey(aKey); mDecryptors[aKeyId]->InitKey(aKey);
} else {
CK_LOGARRAY("Failed to initialize key ", aKeyId.data(), aKeyId.size());
} }
} }
void void
ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId) ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId)
{ {
CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId ", CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId %08x...", *(uint32_t*)&aKeyId[0]);
aKeyId.data(),
aKeyId.size());
if (!HasSeenKeyId(aKeyId)) { if (!HasSeenKeyId(aKeyId)) {
mDecryptors[aKeyId] = new ClearKeyDecryptor(); mDecryptors[aKeyId] = new ClearKeyDecryptor();
} }
@ -144,31 +131,23 @@ ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId)
} }
} }
Status GMPErr
ClearKeyDecryptionManager::Decrypt(std::vector<uint8_t>& aBuffer, ClearKeyDecryptionManager::Decrypt(std::vector<uint8_t>& aBuffer,
const CryptoMetaData& aMetadata) const CryptoMetaData& aMetadata)
{ {
return Decrypt(&aBuffer[0], aBuffer.size(), aMetadata); return Decrypt(&aBuffer[0], aBuffer.size(), aMetadata);
} }
Status GMPErr
ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
const CryptoMetaData& aMetadata) const CryptoMetaData& aMetadata)
{ {
CK_LOGD("ClearKeyDecryptionManager::Decrypt"); CK_LOGD("ClearKeyDecryptionManager::Decrypt");
if (!HasKeyForKeyId(aMetadata.mKeyId)) { if (!HasKeyForKeyId(aMetadata.mKeyId)) {
CK_LOGARRAY("Unable to find decryptor for keyId: ", return GMPNoKeyErr;
aMetadata.mKeyId.data(),
aMetadata.mKeyId.size());
return Status::kNoKey;
} }
CK_LOGARRAY("Found decryptor for keyId: ", return mDecryptors[aMetadata.mKeyId]->Decrypt(aBuffer, aBufferSize, aMetadata);
aMetadata.mKeyId.data(),
aMetadata.mKeyId.size());
return mDecryptors[aMetadata.mKeyId]->Decrypt(aBuffer,
aBufferSize,
aMetadata);
} }
ClearKeyDecryptor::ClearKeyDecryptor() ClearKeyDecryptor::ClearKeyDecryptor()
@ -179,9 +158,7 @@ ClearKeyDecryptor::ClearKeyDecryptor()
ClearKeyDecryptor::~ClearKeyDecryptor() ClearKeyDecryptor::~ClearKeyDecryptor()
{ {
if (HasKey()) { if (HasKey()) {
CK_LOGARRAY("ClearKeyDecryptor dtor; key = ", CK_LOGD("ClearKeyDecryptor dtor; key = %08x...", *(uint32_t*)&mKey[0]);
mKey.data(),
mKey.size());
} else { } else {
CK_LOGD("ClearKeyDecryptor dtor"); CK_LOGD("ClearKeyDecryptor dtor");
} }
@ -193,7 +170,7 @@ ClearKeyDecryptor::InitKey(const Key& aKey)
mKey = aKey; mKey = aKey;
} }
Status GMPErr
ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
const CryptoMetaData& aMetadata) const CryptoMetaData& aMetadata)
{ {
@ -212,7 +189,7 @@ ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
uint32_t cipherBytes = aMetadata.mCipherBytes[i]; uint32_t cipherBytes = aMetadata.mCipherBytes[i];
if (data + cipherBytes > aBuffer + aBufferSize) { if (data + cipherBytes > aBuffer + aBufferSize) {
// Trying to read past the end of the buffer! // Trying to read past the end of the buffer!
return Status::kDecryptError; return GMPCryptoErr;
} }
memcpy(iter, data, cipherBytes); memcpy(iter, data, cipherBytes);
@ -250,5 +227,5 @@ ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
memcpy(aBuffer, &tmp[0], aBufferSize); memcpy(aBuffer, &tmp[0], aBufferSize);
} }
return Status::kSuccess; return GMPNoErr;
} }

Просмотреть файл

@ -17,43 +17,32 @@
#ifndef __ClearKeyDecryptionManager_h__ #ifndef __ClearKeyDecryptionManager_h__
#define __ClearKeyDecryptionManager_h__ #define __ClearKeyDecryptionManager_h__
#include "ClearKeyUtils.h"
// This include is required in order for content_decryption_module to work
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#include "RefCounted.h"
#include <map> #include <map>
#include "ClearKeyUtils.h"
#include "RefCounted.h"
class ClearKeyDecryptor; class ClearKeyDecryptor;
class CryptoMetaData class CryptoMetaData {
{
public: public:
CryptoMetaData() {} CryptoMetaData() {}
explicit CryptoMetaData(const cdm::InputBuffer* aInputBuffer) explicit CryptoMetaData(const GMPEncryptedBufferMetadata* aCrypto)
{ {
Init(aInputBuffer); Init(aCrypto);
} }
void Init(const cdm::InputBuffer* aInputBuffer) void Init(const GMPEncryptedBufferMetadata* aCrypto)
{ {
if (!aInputBuffer) { if (!aCrypto) {
assert(!IsValid()); assert(!IsValid());
return; return;
} }
Assign(mKeyId, aCrypto->KeyId(), aCrypto->KeyIdSize());
Assign(mKeyId, aInputBuffer->key_id, aInputBuffer->key_id_size); Assign(mIV, aCrypto->IV(), aCrypto->IVSize());
Assign(mIV, aInputBuffer->iv, aInputBuffer->iv_size); Assign(mClearBytes, aCrypto->ClearBytes(), aCrypto->NumSubsamples());
Assign(mCipherBytes, aCrypto->CipherBytes(), aCrypto->NumSubsamples());
for (uint32_t i = 0; i < aInputBuffer->num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = aInputBuffer->subsamples[i];
mCipherBytes.push_back(subsample.cipher_bytes);
mClearBytes.push_back(subsample.clear_bytes);
}
} }
bool IsValid() const { bool IsValid() const {
@ -70,7 +59,7 @@ public:
std::vector<uint8_t> mKeyId; std::vector<uint8_t> mKeyId;
std::vector<uint8_t> mIV; std::vector<uint8_t> mIV;
std::vector<uint32_t> mClearBytes; std::vector<uint16_t> mClearBytes;
std::vector<uint32_t> mCipherBytes; std::vector<uint32_t> mCipherBytes;
}; };
@ -96,10 +85,12 @@ public:
void ReleaseKeyId(KeyId aKeyId); void ReleaseKeyId(KeyId aKeyId);
// Decrypts buffer *in place*. // Decrypts buffer *in place*.
cdm::Status Decrypt(uint8_t* aBuffer, uint32_t aBufferSize, GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
const CryptoMetaData& aMetadata); const CryptoMetaData& aMetadata);
cdm::Status Decrypt(std::vector<uint8_t>& aBuffer, GMPErr Decrypt(std::vector<uint8_t>& aBuffer,
const CryptoMetaData& aMetadata); const CryptoMetaData& aMetadata);
void Shutdown();
private: private:
bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const; bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const;

Просмотреть файл

@ -15,123 +15,81 @@
*/ */
#include "ClearKeyPersistence.h" #include "ClearKeyPersistence.h"
#include "ClearKeyUtils.h" #include "ClearKeyUtils.h"
#include "ClearKeyStorage.h" #include "ClearKeyStorage.h"
#include "ClearKeySessionManager.h" #include "ClearKeySessionManager.h"
#include "RefCounted.h" #include "RefCounted.h"
#include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <sstream>
#include <string.h> #include <string.h>
#include <set>
#include <vector>
#include <sstream>
#include <assert.h>
using namespace std; using namespace std;
using namespace cdm;
void // Whether we've loaded the persistent session ids from GMPStorage yet.
ClearKeyPersistence::ReadAllRecordsFromIndex(function<void()>&& aOnComplete) { enum PersistentKeyState {
// Clear what we think the index file contains, we're about to read it again. UNINITIALIZED,
mPersistentSessionIds.clear(); LOADING,
LOADED
};
static PersistentKeyState sPersistentKeyState = UNINITIALIZED;
// Hold a reference to the persistence manager, so it isn't released before // Set of session Ids of the persistent sessions created or residing in
// we try and use it. // storage.
RefPtr<ClearKeyPersistence> self(this); static set<uint32_t> sPersistentSessionIds;
function<void(const uint8_t*, uint32_t)> onIndexSuccess =
[self, aOnComplete] (const uint8_t* data, uint32_t size)
{
CK_LOGD("ClearKeyPersistence: Loaded index file!");
const char* charData = (const char*)data;
stringstream ss(string(charData, charData + size)); static vector<GMPTask*> sTasksBlockedOnSessionIdLoad;
string name;
while (getline(ss, name)) { static void
if (ClearKeyUtils::IsValidSessionId(name.data(), name.size())) { ReadAllRecordsFromIterator(GMPRecordIterator* aRecordIterator,
self->mPersistentSessionIds.insert(atoi(name.c_str())); void* aUserArg,
GMPErr aStatus)
{
assert(sPersistentKeyState == LOADING);
if (GMP_SUCCEEDED(aStatus)) {
// Extract the record names which are valid uint32_t's; they're
// the persistent session ids.
const char* name = nullptr;
uint32_t len = 0;
while (GMP_SUCCEEDED(aRecordIterator->GetName(&name, &len))) {
if (ClearKeyUtils::IsValidSessionId(name, len)) {
assert(name[len] == 0);
sPersistentSessionIds.insert(atoi(name));
} }
aRecordIterator->NextRecord();
} }
self->mPersistentKeyState = PersistentKeyState::LOADED;
aOnComplete();
};
function<void()> onIndexFailed =
[self, aOnComplete] ()
{
CK_LOGD("ClearKeyPersistence: Failed to load index file (it might not exist");
self->mPersistentKeyState = PersistentKeyState::LOADED;
aOnComplete();
};
string filename = "index";
ReadData(mHost, filename, move(onIndexSuccess), move(onIndexFailed));
}
void
ClearKeyPersistence::WriteIndex() {
function <void()> onIndexSuccess =
[] ()
{
CK_LOGD("ClearKeyPersistence: Wrote index file");
};
function <void()> onIndexFail =
[] ()
{
CK_LOGD("ClearKeyPersistence: Failed to write index file (this is bad)");
};
stringstream ss;
for (const uint32_t& sessionId : mPersistentSessionIds) {
ss << sessionId;
ss << '\n';
} }
sPersistentKeyState = LOADED;
aRecordIterator->Close();
string dataString = ss.str(); for (size_t i = 0; i < sTasksBlockedOnSessionIdLoad.size(); i++) {
uint8_t* dataArray = (uint8_t*)dataString.data(); sTasksBlockedOnSessionIdLoad[i]->Run();
vector<uint8_t> data(dataArray, dataArray + dataString.size()); sTasksBlockedOnSessionIdLoad[i]->Destroy();
}
string filename = "index"; sTasksBlockedOnSessionIdLoad.clear();
WriteData(mHost,
filename,
data,
move(onIndexSuccess),
move(onIndexFail));
} }
/* static */ void
ClearKeyPersistence::ClearKeyPersistence(Host_8* aHost) ClearKeyPersistence::EnsureInitialized()
{ {
this->mHost = aHost; if (sPersistentKeyState == UNINITIALIZED) {
} sPersistentKeyState = LOADING;
if (GMP_FAILED(EnumRecordNames(&ReadAllRecordsFromIterator))) {
void sPersistentKeyState = LOADED;
ClearKeyPersistence::EnsureInitialized(bool aPersistentStateAllowed, }
function<void()>&& aOnInitialized)
{
if (aPersistentStateAllowed &&
mPersistentKeyState == PersistentKeyState::UNINITIALIZED) {
mPersistentKeyState = LOADING;
ReadAllRecordsFromIndex(move(aOnInitialized));
} else {
mPersistentKeyState = PersistentKeyState::LOADED;
aOnInitialized();
} }
} }
bool ClearKeyPersistence::IsLoaded() const /* static */ string
{ ClearKeyPersistence::GetNewSessionId(GMPSessionType aSessionType)
return mPersistentKeyState == PersistentKeyState::LOADED;
}
string
ClearKeyPersistence::GetNewSessionId(SessionType aSessionType)
{ {
static uint32_t sNextSessionId = 1; static uint32_t sNextSessionId = 1;
// Ensure we don't re-use a session id that was persisted. // Ensure we don't re-use a session id that was persisted.
while (Contains(mPersistentSessionIds, sNextSessionId)) { while (Contains(sPersistentSessionIds, sNextSessionId)) {
sNextSessionId++; sNextSessionId++;
} }
@ -140,11 +98,8 @@ ClearKeyPersistence::GetNewSessionId(SessionType aSessionType)
ss << sNextSessionId; ss << sNextSessionId;
ss >> sessionId; ss >> sessionId;
if (aSessionType == SessionType::kPersistentLicense) { if (aSessionType == kGMPPersistentSession) {
mPersistentSessionIds.insert(sNextSessionId); sPersistentSessionIds.insert(sNextSessionId);
// Save the updated index file.
WriteIndex();
} }
sNextSessionId++; sNextSessionId++;
@ -152,17 +107,154 @@ ClearKeyPersistence::GetNewSessionId(SessionType aSessionType)
return sessionId; return sessionId;
} }
bool
class CreateSessionTask : public GMPTask {
public:
CreateSessionTask(ClearKeySessionManager* aTarget,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType)
: mTarget(aTarget)
, mCreateSessionToken(aCreateSessionToken)
, mPromiseId(aPromiseId)
, mInitDataType(aInitDataType)
, mSessionType(aSessionType)
{
mInitData.insert(mInitData.end(),
aInitData,
aInitData + aInitDataSize);
}
virtual void Run() override {
mTarget->CreateSession(mCreateSessionToken,
mPromiseId,
mInitDataType.c_str(),
mInitDataType.size(),
&mInitData.front(),
mInitData.size(),
mSessionType);
}
virtual void Destroy() override {
delete this;
}
private:
RefPtr<ClearKeySessionManager> mTarget;
uint32_t mCreateSessionToken;
uint32_t mPromiseId;
const string mInitDataType;
vector<uint8_t> mInitData;
GMPSessionType mSessionType;
};
/* static */ bool
ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType)
{
if (sPersistentKeyState >= LOADED) {
return false;
}
GMPTask* t = new CreateSessionTask(aInstance,
aCreateSessionToken,
aPromiseId,
aInitDataType,
aInitData,
aInitDataSize,
aSessionType);
sTasksBlockedOnSessionIdLoad.push_back(t);
return true;
}
class LoadSessionTask : public GMPTask {
public:
LoadSessionTask(ClearKeySessionManager* aTarget,
uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength)
: mTarget(aTarget)
, mPromiseId(aPromiseId)
, mSessionId(aSessionId, aSessionId + aSessionIdLength)
{
}
virtual void Run() override {
mTarget->LoadSession(mPromiseId,
mSessionId.c_str(),
mSessionId.size());
}
virtual void Destroy() override {
delete this;
}
private:
RefPtr<ClearKeySessionManager> mTarget;
uint32_t mPromiseId;
string mSessionId;
};
/* static */ bool
ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength)
{
if (sPersistentKeyState >= LOADED) {
return false;
}
GMPTask* t = new LoadSessionTask(aInstance,
aPromiseId,
aSessionId,
aSessionIdLength);
sTasksBlockedOnSessionIdLoad.push_back(t);
return true;
}
/* static */ bool
ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId) ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
{ {
return Contains(mPersistentSessionIds, atoi(aSessionId.c_str())); return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
} }
void class LoadSessionFromKeysTask : public ReadContinuation {
ClearKeyPersistence::PersistentSessionRemoved(string& aSessionId) public:
LoadSessionFromKeysTask(ClearKeySessionManager* aTarget,
const string& aSessionId,
uint32_t aPromiseId)
: mTarget(aTarget)
, mSessionId(aSessionId)
, mPromiseId(aPromiseId)
{
}
virtual void ReadComplete(GMPErr aStatus,
const uint8_t* aData,
uint32_t aLength) override
{
mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength);
}
private:
RefPtr<ClearKeySessionManager> mTarget;
string mSessionId;
uint32_t mPromiseId;
};
/* static */ void
ClearKeyPersistence::LoadSessionData(ClearKeySessionManager* aInstance,
const string& aSid,
uint32_t aPromiseId)
{ {
mPersistentSessionIds.erase(atoi(aSessionId.c_str())); LoadSessionFromKeysTask* loadTask =
new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
// Update the index file. ReadData(aSid, loadTask);
WriteIndex(); }
/* static */ void
ClearKeyPersistence::PersistentSessionRemoved(const string& aSessionId)
{
sPersistentSessionIds.erase(atoi(aSessionId.c_str()));
} }

Просмотреть файл

@ -17,51 +17,37 @@
#ifndef __ClearKeyPersistence_h__ #ifndef __ClearKeyPersistence_h__
#define __ClearKeyPersistence_h__ #define __ClearKeyPersistence_h__
// This include is required in order for content_decryption_module to work
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#include "RefCounted.h"
#include <functional>
#include <set>
#include <string> #include <string>
#include <vector> #include "gmp-api/gmp-decryption.h"
class ClearKeySessionManager; class ClearKeySessionManager;
// Whether we've loaded the persistent session ids yet. class ClearKeyPersistence {
enum PersistentKeyState {
UNINITIALIZED,
LOADING,
LOADED
};
class ClearKeyPersistence : public RefCounted
{
public: public:
explicit ClearKeyPersistence(cdm::Host_8* aHost); static void EnsureInitialized();
void EnsureInitialized(bool aPersistentStateAllowed, static std::string GetNewSessionId(GMPSessionType aSessionType);
std::function<void()>&& aOnInitialized);
bool IsLoaded() const; static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const std::string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType);
std::string GetNewSessionId(cdm::SessionType aSessionType); static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength);
bool IsPersistentSessionId(const std::string& aSid); static bool IsPersistentSessionId(const std::string& aSid);
void PersistentSessionRemoved(std::string& aSid); static void LoadSessionData(ClearKeySessionManager* aInstance,
private: const std::string& aSid,
cdm::Host_8* mHost = nullptr; uint32_t aPromiseId);
PersistentKeyState mPersistentKeyState = PersistentKeyState::UNINITIALIZED; static void PersistentSessionRemoved(const std::string& aSid);
std::set<uint32_t> mPersistentSessionIds;
void ReadAllRecordsFromIndex(std::function<void()>&& aOnComplete);
void WriteIndex();
}; };
#endif // __ClearKeyPersistence_h__ #endif // __ClearKeyPersistence_h__

Просмотреть файл

@ -20,17 +20,18 @@
#include "ClearKeyUtils.h" #include "ClearKeyUtils.h"
#include "ClearKeyStorage.h" #include "ClearKeyStorage.h"
#include "psshparser/PsshParser.h" #include "psshparser/PsshParser.h"
#include "gmp-task-utils.h"
#include "gmp-api/gmp-decryption.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
using namespace mozilla; using namespace mozilla;
using namespace cdm;
using namespace std;
ClearKeySession::ClearKeySession(const std::string& aSessionId, ClearKeySession::ClearKeySession(const std::string& aSessionId,
SessionType aSessionType) GMPDecryptorCallback* aCallback,
GMPSessionType aSessionType)
: mSessionId(aSessionId) : mSessionId(aSessionId)
, mCallback(aCallback)
, mSessionType(aSessionType) , mSessionType(aSessionType)
{ {
CK_LOGD("ClearKeySession ctor %p", this); CK_LOGD("ClearKeySession ctor %p", this);
@ -39,21 +40,30 @@ ClearKeySession::ClearKeySession(const std::string& aSessionId,
ClearKeySession::~ClearKeySession() ClearKeySession::~ClearKeySession()
{ {
CK_LOGD("ClearKeySession dtor %p", this); CK_LOGD("ClearKeySession dtor %p", this);
std::vector<GMPMediaKeyInfo> key_infos;
for (const KeyId& keyId : mKeyIds) {
assert(ClearKeyDecryptionManager::Get()->HasSeenKeyId(keyId));
ClearKeyDecryptionManager::Get()->ReleaseKeyId(keyId);
key_infos.push_back(GMPMediaKeyInfo(&keyId[0], keyId.size(), kGMPUnknown));
}
mCallback->BatchedKeyStatusChanged(&mSessionId[0], mSessionId.size(),
key_infos.data(), key_infos.size());
} }
bool void
ClearKeySession::Init(InitDataType aInitDataType, ClearKeySession::Init(uint32_t aCreateSessionToken,
const uint8_t* aInitData, uint32_t aPromiseId,
uint32_t aInitDataSize) const std::string& aInitDataType,
const uint8_t* aInitData, uint32_t aInitDataSize)
{ {
CK_LOGD("ClearKeySession::Init"); CK_LOGD("ClearKeySession::Init");
if (aInitDataType == InitDataType::kCenc) { if (aInitDataType == "cenc") {
ParseCENCInitData(aInitData, aInitDataSize, mKeyIds); ParseCENCInitData(aInitData, aInitDataSize, mKeyIds);
} else if (aInitDataType == InitDataType::kKeyIds) { } else if (aInitDataType == "keyids") {
ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds); ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds);
} else if (aInitDataType == InitDataType::kWebM && } else if (aInitDataType == "webm" && aInitDataSize <= kMaxWebmInitDataSize) {
aInitDataSize <= kMaxWebmInitDataSize) {
// "webm" initData format is simply the raw bytes of the keyId. // "webm" initData format is simply the raw bytes of the keyId.
vector<uint8_t> keyId; vector<uint8_t> keyId;
keyId.assign(aInitData, aInitData+aInitDataSize); keyId.assign(aInitData, aInitData+aInitDataSize);
@ -61,13 +71,17 @@ ClearKeySession::Init(InitDataType aInitDataType,
} }
if (!mKeyIds.size()) { if (!mKeyIds.size()) {
return false; const char message[] = "Couldn't parse init data";
mCallback->RejectPromise(aPromiseId, kGMPTypeError, message, strlen(message));
return;
} }
return true; mCallback->SetSessionId(aCreateSessionToken, &mSessionId[0], mSessionId.length());
mCallback->ResolvePromise(aPromiseId);
} }
SessionType GMPSessionType
ClearKeySession::Type() const ClearKeySession::Type() const
{ {
return mSessionType; return mSessionType;

Просмотреть файл

@ -18,28 +18,30 @@
#define __ClearKeySession_h__ #define __ClearKeySession_h__
#include "ClearKeyUtils.h" #include "ClearKeyUtils.h"
// This include is required in order for content_decryption_module to work #include "gmp-api/gmp-decryption.h"
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#include <string> class GMPBuffer;
#include <vector> class GMPDecryptorCallback;
class GMPDecryptorHost;
class GMPEncryptedBufferMetadata;
class ClearKeySession class ClearKeySession
{ {
public: public:
explicit ClearKeySession(const std::string& aSessionId, explicit ClearKeySession(const std::string& aSessionId,
cdm::SessionType aSessionType); GMPDecryptorCallback* aCallback,
GMPSessionType aSessionType);
~ClearKeySession(); ~ClearKeySession();
const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; } const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; }
bool Init(cdm::InitDataType aInitDataType, void Init(uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const string& aInitDataType,
const uint8_t* aInitData, uint32_t aInitDataSize); const uint8_t* aInitData, uint32_t aInitDataSize);
cdm::SessionType Type() const; GMPSessionType Type() const;
void AddKeyId(const KeyId& aKeyId); void AddKeyId(const KeyId& aKeyId);
@ -49,7 +51,8 @@ private:
const std::string mSessionId; const std::string mSessionId;
std::vector<KeyId> mKeyIds; std::vector<KeyId> mKeyIds;
const cdm::SessionType mSessionType; GMPDecryptorCallback* mCallback;
const GMPSessionType mSessionType;
}; };
#endif // __ClearKeySession_h__ #endif // __ClearKeySession_h__

Просмотреть файл

@ -14,33 +14,30 @@
* limitations under the License. * limitations under the License.
*/ */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "ClearKeyDecryptionManager.h" #include "ClearKeyDecryptionManager.h"
#include "ClearKeySessionManager.h" #include "ClearKeySessionManager.h"
#include "ClearKeyUtils.h" #include "ClearKeyUtils.h"
#include "ClearKeyStorage.h" #include "ClearKeyStorage.h"
#include "ClearKeyPersistence.h" #include "ClearKeyPersistence.h"
// This include is required in order for content_decryption_module to work #include "gmp-task-utils.h"
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#include "psshparser/PsshParser.h"
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
using namespace std; using namespace std;
using namespace cdm;
ClearKeySessionManager::ClearKeySessionManager(Host_8* aHost) ClearKeySessionManager::ClearKeySessionManager()
: mDecryptionManager(ClearKeyDecryptionManager::Get()) : mDecryptionManager(ClearKeyDecryptionManager::Get())
{ {
CK_LOGD("ClearKeySessionManager ctor %p", this); CK_LOGD("ClearKeySessionManager ctor %p", this);
AddRef(); AddRef();
mHost = aHost; if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
mPersistence = new ClearKeyPersistence(mHost); CK_LOGD("failed to create thread in clearkey cdm");
mThread = nullptr;
}
} }
ClearKeySessionManager::~ClearKeySessionManager() ClearKeySessionManager::~ClearKeySessionManager()
@ -49,106 +46,56 @@ ClearKeySessionManager::~ClearKeySessionManager()
} }
void void
ClearKeySessionManager::Init(bool aDistinctiveIdentifierAllowed, ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback,
bool aDistinctiveIdentifierAllowed,
bool aPersistentStateAllowed) bool aPersistentStateAllowed)
{ {
CK_LOGD("ClearKeySessionManager::Init"); CK_LOGD("ClearKeySessionManager::Init");
mCallback = aCallback;
RefPtr<ClearKeySessionManager> self(this); ClearKeyPersistence::EnsureInitialized();
function<void()> onPersistentStateLoaded =
[self] ()
{
while (!self->mDeferredInitialize.empty()) {
function<void()> func = self->mDeferredInitialize.front();
self->mDeferredInitialize.pop();
func();
}
};
mPersistence->EnsureInitialized(aPersistentStateAllowed,
move(onPersistentStateLoaded));
} }
void void
ClearKeySessionManager::CreateSession(uint32_t aPromiseId, ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
InitDataType aInitDataType, uint32_t aPromiseId,
const char* aInitDataType,
uint32_t aInitDataTypeSize,
const uint8_t* aInitData, const uint8_t* aInitData,
uint32_t aInitDataSize, uint32_t aInitDataSize,
SessionType aSessionType) GMPSessionType aSessionType)
{ {
// Copy the init data so it is correctly captured by the lambda
vector<uint8_t> initData(aInitData, aInitData + aInitDataSize);
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer =
[self, aPromiseId, aInitDataType, initData, aSessionType] ()
{
self->CreateSession(aPromiseId,
aInitDataType,
initData.data(),
initData.size(),
aSessionType);
};
// If we haven't loaded, don't do this yet
if (MaybeDeferTillInitialized(deferrer)) {
return;
}
CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType); CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
CK_LOGARRAY("ClearKeySessionManager::CreateSession initdata: ", string initDataType(aInitDataType, aInitDataType + aInitDataTypeSize);
aInitData,
aInitDataSize);
// If 'DecryptingComplete' has been called mHost will be null so we can't
// won't be able to resolve our promise
if (!mHost) {
CK_LOGD("ClearKeySessionManager::CreateSession: mHost is nullptr")
return;
}
// initDataType must be "cenc", "keyids", or "webm". // initDataType must be "cenc", "keyids", or "webm".
if (aInitDataType != InitDataType::kCenc && if (initDataType != "cenc" &&
aInitDataType != InitDataType::kKeyIds && initDataType != "keyids" &&
aInitDataType != InitDataType::kWebM) { initDataType != "webm") {
string message = "'" + initDataType + "' is an initDataType unsupported by ClearKey";
string message = "initDataType is not supported by ClearKey"; mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
mHost->OnRejectPromise(aPromiseId, message.c_str(), message.size());
Error::kNotSupportedError,
0,
message.c_str(),
message.size());
return; return;
} }
string sessionId = mPersistence->GetNewSessionId(aSessionType); if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
aCreateSessionToken,
aPromiseId,
initDataType,
aInitData,
aInitDataSize,
aSessionType)) {
return;
}
string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
assert(mSessions.find(sessionId) == mSessions.end()); assert(mSessions.find(sessionId) == mSessions.end());
ClearKeySession* session = new ClearKeySession(sessionId, ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
aSessionType); session->Init(aCreateSessionToken, aPromiseId, initDataType, aInitData, aInitDataSize);
if (!session->Init(aInitDataType, aInitData, aInitDataSize)) {
CK_LOGD("Failed to initialize session: %s", sessionId.c_str());
const static char* message = "Failed to initialize session";
mHost->OnRejectPromise(aPromiseId,
Error::kUnknownError,
0,
message,
strlen(message));
return;
}
mSessions[sessionId] = session; mSessions[sessionId] = session;
const vector<KeyId>& sessionKeys = session->GetKeyIds(); const vector<KeyId>& sessionKeys = session->GetKeyIds();
vector<KeyId> neededKeys; vector<KeyId> neededKeys;
for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) { for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
// Need to request this key ID from the client. We always send a key // 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 // request, whether or not another session has sent a request with the same
@ -166,19 +113,9 @@ ClearKeySessionManager::CreateSession(uint32_t aPromiseId,
// Send a request for needed key data. // Send a request for needed key data.
string request; string request;
ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType); ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
mCallback->SessionMessage(&sessionId[0], sessionId.length(),
// Resolve the promise with the new session information. kGMPLicenseRequest,
mHost->OnResolveNewSessionPromise(aPromiseId, (uint8_t*)&request[0], request.length());
sessionId.c_str(),
sessionId.size());
mHost->OnSessionMessage(sessionId.c_str(),
sessionId.size(),
MessageType::kLicenseRequest,
request.c_str(),
request.size(),
nullptr,
0);
} }
void void
@ -186,90 +123,53 @@ ClearKeySessionManager::LoadSession(uint32_t aPromiseId,
const char* aSessionId, const char* aSessionId,
uint32_t aSessionIdLength) uint32_t aSessionIdLength)
{ {
// Copy the sessionId into a string so the lambda captures it properly.
string sessionId(aSessionId, aSessionId + aSessionIdLength);
// Hold a reference to the SessionManager so that it isn't released before
// we try to use it.
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer =
[self, aPromiseId, sessionId] ()
{
self->LoadSession(aPromiseId, sessionId.data(), sessionId.size());
};
if (MaybeDeferTillInitialized(deferrer)) {
return;
}
CK_LOGD("ClearKeySessionManager::LoadSession"); CK_LOGD("ClearKeySessionManager::LoadSession");
// If the SessionManager has been shutdown mHost will be null and we won't
// be able to resolve the promise.
if (!mHost) {
return;
}
if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) { if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); mCallback->ResolveLoadSessionPromise(aPromiseId, false);
return; return;
} }
if (!mPersistence->IsPersistentSessionId(sessionId)) { if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); aPromiseId,
aSessionId,
aSessionIdLength)) {
return; return;
} }
function<void(const uint8_t*, uint32_t)> success = string sid(aSessionId, aSessionId + aSessionIdLength);
[self, sessionId, aPromiseId] (const uint8_t* data, uint32_t size) if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
{ mCallback->ResolveLoadSessionPromise(aPromiseId, false);
self->PersistentSessionDataLoaded(aPromiseId, return;
sessionId, }
data,
size);
};
function<void()> failure = [self, sessionId, aPromiseId] { // Callsback PersistentSessionDataLoaded with results...
if (!self->mHost) { ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
return;
}
// As per the API described in ContentDecryptionModule_8
self->mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
};
ReadData(mHost, sessionId, move(success), move(failure));
} }
void void
ClearKeySessionManager::PersistentSessionDataLoaded(uint32_t aPromiseId, ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus,
uint32_t aPromiseId,
const string& aSessionId, const string& aSessionId,
const uint8_t* aKeyData, const uint8_t* aKeyData,
uint32_t aKeyDataSize) uint32_t aKeyDataSize)
{ {
CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded"); CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded");
if (GMP_FAILED(aStatus) ||
// Check that the SessionManager has not been shut down before we try and Contains(mSessions, aSessionId) ||
// resolve any promises.
if (!mHost) {
return;
}
if (Contains(mSessions, aSessionId) ||
(aKeyDataSize % (2 * CENC_KEY_LEN)) != 0) { (aKeyDataSize % (2 * CENC_KEY_LEN)) != 0) {
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
// As per the instructions in ContentDecryptionModule_8
mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
return; return;
} }
ClearKeySession* session = new ClearKeySession(aSessionId, ClearKeySession* session = new ClearKeySession(aSessionId,
SessionType::kPersistentLicense); mCallback,
kGMPPersistentSession);
mSessions[aSessionId] = session; mSessions[aSessionId] = session;
uint32_t numKeys = aKeyDataSize / (2 * CENC_KEY_LEN); uint32_t numKeys = aKeyDataSize / (2 * CENC_KEY_LEN);
vector<KeyInformation> keyInfos; vector<GMPMediaKeyInfo> key_infos;
vector<KeyIdPair> keyPairs; vector<KeyIdPair> keyPairs;
for (uint32_t i = 0; i < numKeys; i ++) { for (uint32_t i = 0; i < numKeys; i ++) {
const uint8_t* base = aKeyData + 2 * CENC_KEY_LEN * i; const uint8_t* base = aKeyData + 2 * CENC_KEY_LEN * i;
@ -287,25 +187,16 @@ ClearKeySessionManager::PersistentSessionDataLoaded(uint32_t aPromiseId,
mDecryptionManager->ExpectKeyId(keyPair.mKeyId); mDecryptionManager->ExpectKeyId(keyPair.mKeyId);
mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey); mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
mKeyIds.insert(keyPair.mKey); mKeyIds.insert(keyPair.mKey);
keyPairs.push_back(keyPair); keyPairs.push_back(keyPair);
key_infos.push_back(GMPMediaKeyInfo(&keyPairs[i].mKeyId[0],
KeyInformation keyInfo = KeyInformation(); keyPairs[i].mKeyId.size(),
keyInfo.key_id = &keyPairs.back().mKeyId[0]; kGMPUsable));
keyInfo.key_id_size = keyPair.mKeyId.size();
keyInfo.status = KeyStatus::kUsable;
keyInfos.push_back(keyInfo);
} }
mCallback->BatchedKeyStatusChanged(&aSessionId[0], aSessionId.size(),
key_infos.data(), key_infos.size());
mHost->OnSessionKeysChange(&aSessionId[0], mCallback->ResolveLoadSessionPromise(aPromiseId, true);
aSessionId.size(),
true,
keyInfos.data(),
keyInfos.size());
mHost->OnResolveNewSessionPromise(aPromiseId,
aSessionId.c_str(),
aSessionId.size());
} }
void void
@ -315,47 +206,13 @@ ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
const uint8_t* aResponse, const uint8_t* aResponse,
uint32_t aResponseSize) uint32_t aResponseSize)
{ {
// Copy the method arguments so we can capture them in the lambda
string sessionId(aSessionId, aSessionId + aSessionIdLength);
vector<uint8_t> response(aResponse, aResponse + aResponseSize);
// Hold a reference to the SessionManager so it isn't released before we
// callback.
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer =
[self, aPromiseId, sessionId, response] ()
{
self->UpdateSession(aPromiseId,
sessionId.data(),
sessionId.size(),
response.data(),
response.size());
};
// If we haven't fully loaded, defer calling this method
if (MaybeDeferTillInitialized(deferrer)) {
return;
}
// Make sure the SessionManager has not been shutdown before we try and
// resolve any promises.
if (!mHost) {
return;
}
CK_LOGD("ClearKeySessionManager::UpdateSession"); CK_LOGD("ClearKeySessionManager::UpdateSession");
CK_LOGD("Updating session: %s", sessionId.c_str()); string sessionId(aSessionId, aSessionId + aSessionIdLength);
auto itr = mSessions.find(sessionId); auto itr = mSessions.find(sessionId);
if (itr == mSessions.end() || !(itr->second)) { if (itr == mSessions.end() || !(itr->second)) {
CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession."); CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
CK_LOGD("Unable to find session: %s", sessionId.c_str()); mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
mHost->OnRejectPromise(aPromiseId,
Error::kInvalidAccessError,
0,
nullptr,
0);
return; return;
} }
ClearKeySession* session = itr->second; ClearKeySession* session = itr->second;
@ -363,56 +220,32 @@ ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
// Verify the size of session response. // Verify the size of session response.
if (aResponseSize >= kMaxSessionResponseLength) { if (aResponseSize >= kMaxSessionResponseLength) {
CK_LOGW("Session response size is not within a reasonable size."); CK_LOGW("Session response size is not within a reasonable size.");
CK_LOGD("Failed to parse response for session %s", sessionId.c_str()); mCallback->RejectPromise(aPromiseId, kGMPTypeError, nullptr, 0);
mHost->OnRejectPromise(aPromiseId,
Error::kInvalidAccessError,
0,
nullptr,
0);
return; return;
} }
// Parse the response for any (key ID, key) pairs. // Parse the response for any (key ID, key) pairs.
vector<KeyIdPair> keyPairs; vector<KeyIdPair> keyPairs;
if (!ClearKeyUtils::ParseJWK(aResponse, if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
aResponseSize,
keyPairs,
session->Type())) {
CK_LOGW("ClearKey CDM failed to parse JSON Web Key."); CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
mCallback->RejectPromise(aPromiseId, kGMPTypeError, nullptr, 0);
mHost->OnRejectPromise(aPromiseId,
Error::kInvalidAccessError,
0,
nullptr,
0);
return; return;
} }
vector<KeyInformation> keyInfos; vector<GMPMediaKeyInfo> key_infos;
for (size_t i = 0; i < keyPairs.size(); i++) { for (size_t i = 0; i < keyPairs.size(); i++) {
KeyIdPair& keyPair = keyPairs[i]; KeyIdPair& keyPair = keyPairs[i];
mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey); mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
mKeyIds.insert(keyPair.mKeyId); mKeyIds.insert(keyPair.mKeyId);
key_infos.push_back(GMPMediaKeyInfo(&keyPair.mKeyId[0],
KeyInformation keyInfo = KeyInformation(); keyPair.mKeyId.size(),
keyInfo.key_id = &keyPair.mKeyId[0]; kGMPUsable));
keyInfo.key_id_size = keyPair.mKeyId.size();
keyInfo.status = KeyStatus::kUsable;
keyInfos.push_back(keyInfo);
} }
mCallback->BatchedKeyStatusChanged(aSessionId, aSessionIdLength,
key_infos.data(), key_infos.size());
mHost->OnSessionKeysChange(aSessionId, if (session->Type() != kGMPPersistentSession) {
aSessionIdLength, mCallback->ResolvePromise(aPromiseId);
true,
keyInfos.data(),
keyInfos.size());
if (session->Type() != SessionType::kPersistentLicense) {
mHost->OnResolvePromise(aPromiseId);
return; return;
} }
@ -420,30 +253,15 @@ ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
// and simply append each keyId followed by its key. // and simply append each keyId followed by its key.
vector<uint8_t> keydata; vector<uint8_t> keydata;
Serialize(session, keydata); Serialize(session, keydata);
GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
function<void()> resolve = [self, aPromiseId] () static const char* message = "Couldn't store cenc key init data";
{ GMPTask* reject = WrapTask(mCallback,
if (!self->mHost) { &GMPDecryptorCallback::RejectPromise,
return; aPromiseId,
} kGMPInvalidStateError,
self->mHost->OnResolvePromise(aPromiseId); message,
}; strlen(message));
StoreData(sessionId, keydata, resolve, reject);
function<void()> reject = [self, aPromiseId] ()
{
if (!self->mHost) {
return;
}
static const char* message = "Couldn't store cenc key init data";
self->mHost->OnRejectPromise(aPromiseId,
Error::kInvalidStateError,
0,
message,
strlen(message));
};
WriteData(mHost, sessionId, keydata, move(resolve), move(reject));
} }
void void
@ -469,39 +287,13 @@ ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
const char* aSessionId, const char* aSessionId,
uint32_t aSessionIdLength) uint32_t aSessionIdLength)
{ {
// Copy the sessionId into a string so we capture it properly.
string sessionId(aSessionId, aSessionId + aSessionIdLength);
// Hold a reference to the session manager, so it doesn't get deleted
// before we need to use it.
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer =
[self, aPromiseId, sessionId] ()
{
self->CloseSession(aPromiseId, sessionId.data(), sessionId.size());
};
// If we haven't loaded, call this method later.
if (MaybeDeferTillInitialized(deferrer)) {
return;
}
CK_LOGD("ClearKeySessionManager::CloseSession"); CK_LOGD("ClearKeySessionManager::CloseSession");
// If DecryptingComplete has been called mHost will be null and we won't string sessionId(aSessionId, aSessionId + aSessionIdLength);
// be able to resolve our promise.
if (!mHost) {
return;
}
auto itr = mSessions.find(sessionId); auto itr = mSessions.find(sessionId);
if (itr == mSessions.end()) { if (itr == mSessions.end()) {
CK_LOGW("ClearKey CDM couldn't close non-existent session."); CK_LOGW("ClearKey CDM couldn't close non-existent session.");
mHost->OnRejectPromise(aPromiseId, mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
Error::kInvalidAccessError,
0,
nullptr,
0);
return; return;
} }
@ -509,9 +301,8 @@ ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
assert(session); assert(session);
ClearInMemorySessionData(session); ClearInMemorySessionData(session);
mCallback->SessionClosed(aSessionId, aSessionIdLength);
mHost->OnSessionClosed(aSessionId, aSessionIdLength); mCallback->ResolvePromise(aPromiseId);
mHost->OnResolvePromise(aPromiseId);
} }
void void
@ -526,80 +317,40 @@ ClearKeySessionManager::RemoveSession(uint32_t aPromiseId,
const char* aSessionId, const char* aSessionId,
uint32_t aSessionIdLength) uint32_t aSessionIdLength)
{ {
// Copy the sessionId into a string so it can be captured for the lambda.
string sessionId(aSessionId, aSessionId + aSessionIdLength);
// Hold a reference to the SessionManager, so it isn't released before we
// try and use it.
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer =
[self, aPromiseId, sessionId] ()
{
self->RemoveSession(aPromiseId, sessionId.data(), sessionId.size());
};
// If we haven't fully loaded, defer calling this method.
if (MaybeDeferTillInitialized(deferrer)) {
return;
}
// Check that the SessionManager has not been shutdown before we try and
// resolve any promises.
if (!mHost) {
return;
}
CK_LOGD("ClearKeySessionManager::RemoveSession"); CK_LOGD("ClearKeySessionManager::RemoveSession");
string sessionId(aSessionId, aSessionId + aSessionIdLength);
auto itr = mSessions.find(sessionId); auto itr = mSessions.find(sessionId);
if (itr == mSessions.end()) { if (itr == mSessions.end()) {
CK_LOGW("ClearKey CDM couldn't remove non-existent session."); CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
mHost->OnRejectPromise(aPromiseId,
Error::kInvalidAccessError,
0,
nullptr,
0);
return; return;
} }
ClearKeySession* session = itr->second; ClearKeySession* session = itr->second;
assert(session); assert(session);
string sid = session->Id(); string sid = session->Id();
bool isPersistent = session->Type() == SessionType::kPersistentLicense; bool isPersistent = session->Type() == kGMPPersistentSession;
ClearInMemorySessionData(session); ClearInMemorySessionData(session);
if (!isPersistent) { if (!isPersistent) {
mHost->OnResolvePromise(aPromiseId); mCallback->ResolvePromise(aPromiseId);
return; return;
} }
mPersistence->PersistentSessionRemoved(sid); ClearKeyPersistence::PersistentSessionRemoved(sid);
// Overwrite the record storing the sessionId's key data with a zero
// length record to delete it.
vector<uint8_t> emptyKeydata; vector<uint8_t> emptyKeydata;
GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
function<void()> resolve = [self, aPromiseId, sessionId] () static const char* message = "Could not remove session";
{ GMPTask* reject = WrapTask(mCallback,
if (!self->mHost) { &GMPDecryptorCallback::RejectPromise,
return; aPromiseId,
} kGMPInvalidAccessError,
self->mHost->OnResolvePromise(aPromiseId); message,
}; strlen(message));
StoreData(sessionId, emptyKeydata, resolve, reject);
function<void()> reject = [self, aPromiseId, sessionId] ()
{
if (!self->mHost) {
return;
}
static const char* message = "Could not remove session";
self->mHost->OnRejectPromise(aPromiseId,
Error::kInvalidAccessError,
0,
message,
strlen(message));
};
WriteData(mHost, sessionId, emptyKeydata, move(resolve), move(reject));
} }
void void
@ -609,36 +360,48 @@ ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId,
{ {
// ClearKey CDM doesn't support this method by spec. // ClearKey CDM doesn't support this method by spec.
CK_LOGD("ClearKeySessionManager::SetServerCertificate"); CK_LOGD("ClearKeySessionManager::SetServerCertificate");
mHost->OnRejectPromise(aPromiseId, mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
Error::kNotSupportedError, nullptr /* message */, 0 /* messageLen */);
0,
nullptr /* message */,
0 /* messageLen */);
} }
Status void
ClearKeySessionManager::Decrypt(const InputBuffer& aBuffer, ClearKeySessionManager::Decrypt(GMPBuffer* aBuffer,
DecryptedBlock* aDecryptedBlock) GMPEncryptedBufferMetadata* aMetadata)
{ {
CK_LOGD("ClearKeySessionManager::Decrypt"); CK_LOGD("ClearKeySessionManager::Decrypt");
CK_LOGARRAY("Key: ", aBuffer.key_id, aBuffer.key_id_size); if (!mThread) {
CK_LOGW("No decrypt thread");
mCallback->Decrypted(aBuffer, GMPGenericErr);
return;
}
Buffer* buffer = mHost->Allocate(aBuffer.data_size); mThread->Post(WrapTaskRefCounted(this,
assert(buffer != nullptr); &ClearKeySessionManager::DoDecrypt,
assert(buffer->Data() != nullptr); aBuffer, aMetadata));
assert(buffer->Capacity() >= aBuffer.data_size); }
memcpy(buffer->Data(), aBuffer.data, aBuffer.data_size); void
ClearKeySessionManager::DoDecrypt(GMPBuffer* aBuffer,
GMPEncryptedBufferMetadata* aMetadata)
{
CK_LOGD("ClearKeySessionManager::DoDecrypt");
Status status = mDecryptionManager->Decrypt(buffer->Data(), GMPErr rv = mDecryptionManager->Decrypt(aBuffer->Data(), aBuffer->Size(),
buffer->Size(), CryptoMetaData(aMetadata));
CryptoMetaData(&aBuffer)); CK_LOGD("DeDecrypt finished with code %x\n", rv);
mCallback->Decrypted(aBuffer, rv);
}
aDecryptedBlock->SetDecryptedBuffer(buffer); void
aDecryptedBlock->SetTimestamp(aBuffer.timestamp); ClearKeySessionManager::Shutdown()
{
CK_LOGD("ClearKeySessionManager::Shutdown %p", this);
return status; for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
delete it->second;
}
mSessions.clear();
} }
void void
@ -646,23 +409,10 @@ ClearKeySessionManager::DecryptingComplete()
{ {
CK_LOGD("ClearKeySessionManager::DecryptingComplete %p", this); CK_LOGD("ClearKeySessionManager::DecryptingComplete %p", this);
for (auto it = mSessions.begin(); it != mSessions.end(); it++) { GMPThread* thread = mThread;
delete it->second; thread->Join();
}
mSessions.clear();
Shutdown();
mDecryptionManager = nullptr; mDecryptionManager = nullptr;
mHost = nullptr;
Release(); Release();
} }
bool ClearKeySessionManager::MaybeDeferTillInitialized(function<void()> aMaybeDefer)
{
if (mPersistence->IsLoaded()) {
return false;
}
mDeferredInitialize.emplace(move(aMaybeDefer));
return true;
}

Просмотреть файл

@ -1,81 +1,80 @@
/* /*
* Copyright 2015, Mozilla Foundation and contributors * Copyright 2015, Mozilla Foundation and contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#ifndef __ClearKeyDecryptor_h__ #ifndef __ClearKeyDecryptor_h__
#define __ClearKeyDecryptor_h__ #define __ClearKeyDecryptor_h__
#include "ClearKeyDecryptionManager.h"
#include "ClearKeyPersistence.h"
#include "ClearKeySession.h"
#include "ClearKeyUtils.h"
// This include is required in order for content_decryption_module to work
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#include "RefCounted.h"
#include <functional>
#include <map> #include <map>
#include <queue>
#include <set> #include <set>
#include <string> #include <string>
#include <vector>
class ClearKeySessionManager final : public RefCounted #include "ClearKeyDecryptionManager.h"
#include "ClearKeySession.h"
#include "ClearKeyUtils.h"
#include "gmp-api/gmp-decryption.h"
#include "RefCounted.h"
class ClearKeySessionManager final : public GMPDecryptor
, public RefCounted
{ {
public: public:
explicit ClearKeySessionManager(cdm::Host_8* aHost); ClearKeySessionManager();
void Init(bool aDistinctiveIdentifierAllowed, virtual void Init(GMPDecryptorCallback* aCallback,
bool aPersistentStateAllowed); bool aDistinctiveIdentifierAllowed,
bool aPersistentStateAllowed) override;
void CreateSession(uint32_t aPromiseId, virtual void CreateSession(uint32_t aCreateSessionToken,
cdm::InitDataType aInitDataType, uint32_t aPromiseId,
const uint8_t* aInitData, const char* aInitDataType,
uint32_t aInitDataSize, uint32_t aInitDataTypeSize,
cdm::SessionType aSessionType); const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType) override;
void LoadSession(uint32_t aPromiseId, virtual void LoadSession(uint32_t aPromiseId,
const char* aSessionId, const char* aSessionId,
uint32_t aSessionIdLength); uint32_t aSessionIdLength) override;
void UpdateSession(uint32_t aPromiseId, virtual void UpdateSession(uint32_t aPromiseId,
const char* aSessionId, const char* aSessionId,
uint32_t aSessionIdLength, uint32_t aSessionIdLength,
const uint8_t* aResponse, const uint8_t* aResponse,
uint32_t aResponseSize); uint32_t aResponseSize) override;
void CloseSession(uint32_t aPromiseId, virtual void CloseSession(uint32_t aPromiseId,
const char* aSessionId, const char* aSessionId,
uint32_t aSessionIdLength); uint32_t aSessionIdLength) override;
void RemoveSession(uint32_t aPromiseId, virtual void RemoveSession(uint32_t aPromiseId,
const char* aSessionId, const char* aSessionId,
uint32_t aSessionIdLength); uint32_t aSessionIdLength) override;
void SetServerCertificate(uint32_t aPromiseId, virtual void SetServerCertificate(uint32_t aPromiseId,
const uint8_t* aServerCert, const uint8_t* aServerCert,
uint32_t aServerCertSize); uint32_t aServerCertSize) override;
cdm::Status virtual void Decrypt(GMPBuffer* aBuffer,
Decrypt(const cdm::InputBuffer& aBuffer, GMPEncryptedBufferMetadata* aMetadata) override;
cdm::DecryptedBlock* aDecryptedBlock);
void DecryptingComplete(); virtual void DecryptingComplete() override;
void PersistentSessionDataLoaded(uint32_t aPromiseId, void PersistentSessionDataLoaded(GMPErr aStatus,
uint32_t aPromiseId,
const std::string& aSessionId, const std::string& aSessionId,
const uint8_t* aKeyData, const uint8_t* aKeyData,
uint32_t aKeyDataSize); uint32_t aKeyDataSize);
@ -83,20 +82,19 @@ public:
private: private:
~ClearKeySessionManager(); ~ClearKeySessionManager();
void DoDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
void Shutdown();
void ClearInMemorySessionData(ClearKeySession* aSession); void ClearInMemorySessionData(ClearKeySession* aSession);
bool MaybeDeferTillInitialized(std::function<void()> aMaybeDefer); void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
void Serialize(const ClearKeySession* aSession,
std::vector<uint8_t>& aOutKeyData);
RefPtr<ClearKeyDecryptionManager> mDecryptionManager; RefPtr<ClearKeyDecryptionManager> mDecryptionManager;
RefPtr<ClearKeyPersistence> mPersistence;
cdm::Host_8* mHost = nullptr; GMPDecryptorCallback* mCallback;
GMPThread* mThread;
std::set<KeyId> mKeyIds; std::set<KeyId> mKeyIds;
std::map<std::string, ClearKeySession*> mSessions; std::map<std::string, ClearKeySession*> mSessions;
std::queue<std::function<void()>> mDeferredInitialize;
}; };
#endif // __ClearKeyDecryptor_h__ #endif // __ClearKeyDecryptor_h__

Просмотреть файл

@ -15,212 +15,180 @@
*/ */
#include "ClearKeyStorage.h" #include "ClearKeyStorage.h"
#include "ClearKeyUtils.h" #include "ClearKeyUtils.h"
// This include is required in order for content_decryption_module to work #include "gmp-task-utils.h"
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#include <assert.h> #include <assert.h>
#include "ArrayUtils.h" #include "ArrayUtils.h"
#include <vector> #include <vector>
using namespace cdm; static GMPErr
using namespace std; RunOnMainThread(GMPTask* aTask)
class WriteRecordClient : public FileIOClient
{ {
return GetPlatform()->runonmainthread(aTask);
}
GMPErr
OpenRecord(const char* aName,
uint32_t aNameLength,
GMPRecord** aOutRecord,
GMPRecordClient* aClient)
{
return GetPlatform()->createrecord(aName, aNameLength, aOutRecord, aClient);
}
class WriteRecordClient : public GMPRecordClient {
public: public:
/* /*
* This function will take the memory ownership of the parameters and * This function will take the memory ownership of the parameters and
* delete them when done. * delete them when done.
*/ */
static void Write(Host_8* aHost, static void Write(const std::string& aRecordName,
string& aRecordName, const std::vector<uint8_t>& aData,
const vector<uint8_t>& aData, GMPTask* aOnSuccess,
function<void()>&& aOnSuccess, GMPTask* aOnFailure) {
function<void()>&& aOnFailure) (new WriteRecordClient(aData, aOnSuccess, aOnFailure))->Do(aRecordName);
{
WriteRecordClient* client = new WriteRecordClient(aData,
move(aOnSuccess),
move(aOnFailure));
client->Do(aRecordName, aHost);
} }
void OnOpenComplete(Status aStatus) override virtual void OpenComplete(GMPErr aStatus) override {
{ if (GMP_FAILED(aStatus) ||
// If we hit an error, fail. GMP_FAILED(mRecord->Write(&mData.front(), mData.size()))) {
if (aStatus != Status::kSuccess) { Done(mOnFailure, mOnSuccess);
Done(aStatus);
} else if (mFileIO) { // Otherwise, write our data to the file.
mFileIO->Write(&mData[0], mData.size());
} }
} }
void OnReadComplete(Status aStatus, virtual void ReadComplete(GMPErr aStatus,
const uint8_t* aData, const uint8_t* aData,
uint32_t aDataSize) override uint32_t aDataSize) override {
{ assert(false); // Should not reach here.
// This function should never be called, we only ever write data with this
// client.
assert(false);
} }
void OnWriteComplete(Status aStatus) override virtual void WriteComplete(GMPErr aStatus) override {
{ if (GMP_FAILED(aStatus)) {
Done(aStatus); Done(mOnFailure, mOnSuccess);
} else {
Done(mOnSuccess, mOnFailure);
}
} }
private: private:
explicit WriteRecordClient(const vector<uint8_t>& aData, WriteRecordClient(const std::vector<uint8_t>& aData,
function<void()>&& aOnSuccess, GMPTask* aOnSuccess,
function<void()>&& aOnFailure) GMPTask* aOnFailure)
: mFileIO(nullptr) : mRecord(nullptr)
, mOnSuccess(move(aOnSuccess)) , mOnSuccess(aOnSuccess)
, mOnFailure(move(aOnFailure)) , mOnFailure(aOnFailure)
, mData(aData) {} , mData(aData) {}
void Do(const string& aName, Host_8* aHost) void Do(const std::string& aName) {
{ auto err = OpenRecord(aName.c_str(), aName.size(), &mRecord, this);
// Initialize the FileIO. if (GMP_FAILED(err) ||
mFileIO = aHost->CreateFileIO(this); GMP_FAILED(mRecord->Open())) {
mFileIO->Open(aName.c_str(), aName.size()); Done(mOnFailure, mOnSuccess);
}
} }
void Done(cdm::FileIOClient::Status aStatus) void Done(GMPTask* aToRun, GMPTask* aToDestroy) {
{
// Note: Call Close() before running continuation, in case the // Note: Call Close() before running continuation, in case the
// continuation tries to open the same record; if we call Close() // continuation tries to open the same record; if we call Close()
// after running the continuation, the Close() call will arrive // after running the continuation, the Close() call will arrive
// just after the Open() call succeeds, immediately closing the // just after the Open() call succeeds, immediately closing the
// record we just opened. // record we just opened.
if (mFileIO) { if (mRecord) {
mFileIO->Close(); mRecord->Close();
} }
aToDestroy->Destroy();
if (IO_SUCCEEDED(aStatus)) { RunOnMainThread(aToRun);
mOnSuccess();
} else {
mOnFailure();
}
delete this; delete this;
} }
FileIO* mFileIO = nullptr; GMPRecord* mRecord;
GMPTask* mOnSuccess;
function<void()> mOnSuccess; GMPTask* mOnFailure;
function<void()> mOnFailure; const std::vector<uint8_t> mData;
const vector<uint8_t> mData;
}; };
void void
WriteData(Host_8* aHost, StoreData(const std::string& aRecordName,
string& aRecordName, const std::vector<uint8_t>& aData,
const vector<uint8_t>& aData, GMPTask* aOnSuccess,
function<void()>&& aOnSuccess, GMPTask* aOnFailure)
function<void()>&& aOnFailure)
{ {
WriteRecordClient::Write(aHost, WriteRecordClient::Write(aRecordName, aData, aOnSuccess, aOnFailure);
aRecordName,
aData,
move(aOnSuccess),
move(aOnFailure));
} }
class ReadRecordClient : public FileIOClient class ReadRecordClient : public GMPRecordClient {
{
public: public:
/* /*
* This function will take the memory ownership of the parameters and * This function will take the memory ownership of the parameters and
* delete them when done. * delete them when done.
*/ */
static void Read(Host_8* aHost, static void Read(const std::string& aRecordName,
string& aRecordName, ReadContinuation* aContinuation) {
function<void(const uint8_t*, uint32_t)>&& aOnSuccess, assert(aContinuation);
function<void()>&& aOnFailure) (new ReadRecordClient(aContinuation))->Do(aRecordName);
{
(new ReadRecordClient(move(aOnSuccess), move(aOnFailure)))->
Do(aRecordName, aHost);
} }
void OnOpenComplete(Status aStatus) override virtual void OpenComplete(GMPErr aStatus) override {
{
auto err = aStatus; auto err = aStatus;
if (aStatus != Status::kSuccess) { if (GMP_FAILED(err) ||
GMP_FAILED(err = mRecord->Read())) {
Done(err, nullptr, 0); Done(err, nullptr, 0);
} else {
mFileIO->Read();
} }
} }
void OnReadComplete(Status aStatus, virtual void ReadComplete(GMPErr aStatus,
const uint8_t* aData, const uint8_t* aData,
uint32_t aDataSize) override uint32_t aDataSize) override {
{
Done(aStatus, aData, aDataSize); Done(aStatus, aData, aDataSize);
} }
void OnWriteComplete(Status aStatus) override virtual void WriteComplete(GMPErr aStatus) override {
{ assert(false); // Should not reach here.
// We should never reach here, this client only ever reads data.
assert(false);
} }
private: private:
explicit ReadRecordClient(function<void(const uint8_t*, uint32_t)>&& aOnSuccess, explicit ReadRecordClient(ReadContinuation* aContinuation)
function<void()>&& aOnFailure) : mRecord(nullptr)
: mFileIO(nullptr) , mContinuation(aContinuation) {}
, mOnSuccess(move(aOnSuccess))
, mOnFailure(move(aOnFailure))
{}
void Do(const string& aName, Host_8* aHost) void Do(const std::string& aName) {
{ auto err = OpenRecord(aName.c_str(), aName.size(), &mRecord, this);
mFileIO = aHost->CreateFileIO(this); if (GMP_FAILED(err) ||
mFileIO->Open(aName.c_str(), aName.size()); GMP_FAILED(err = mRecord->Open())) {
Done(err, nullptr, 0);
}
} }
void Done(cdm::FileIOClient::Status aStatus, void Done(GMPErr err, const uint8_t* aData, uint32_t aDataSize) {
const uint8_t* aData,
uint32_t aDataSize)
{
// Note: Call Close() before running continuation, in case the // Note: Call Close() before running continuation, in case the
// continuation tries to open the same record; if we call Close() // continuation tries to open the same record; if we call Close()
// after running the continuation, the Close() call will arrive // after running the continuation, the Close() call will arrive
// just after the Open() call succeeds, immediately closing the // just after the Open() call succeeds, immediately closing the
// record we just opened. // record we just opened.
if (mFileIO) { if (mRecord) {
mFileIO->Close(); mRecord->Close();
} }
mContinuation->ReadComplete(err, aData, aDataSize);
if (IO_SUCCEEDED(aStatus)) { delete mContinuation;
mOnSuccess(aData, aDataSize);
} else {
mOnFailure();
}
delete this; delete this;
} }
FileIO* mFileIO = nullptr; GMPRecord* mRecord;
ReadContinuation* mContinuation;
function<void(const uint8_t*, uint32_t)> mOnSuccess;
function<void()> mOnFailure;
}; };
void void
ReadData(Host_8* mHost, ReadData(const std::string& aRecordName,
string& aRecordName, ReadContinuation* aContinuation)
function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
function<void()>&& aOnFailure)
{ {
ReadRecordClient::Read(mHost, ReadRecordClient::Read(aRecordName, aContinuation);
aRecordName, }
move(aOnSuccess),
move(aOnFailure)); GMPErr
EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc)
{
return GetPlatform()->getrecordenumerator(aRecvIteratorFunc, nullptr);
} }

Просмотреть файл

@ -17,27 +17,32 @@
#ifndef __ClearKeyStorage_h__ #ifndef __ClearKeyStorage_h__
#define __ClearKeyStorage_h__ #define __ClearKeyStorage_h__
#include <functional> #include "gmp-api/gmp-errors.h"
#include <stdint.h> #include "gmp-api/gmp-platform.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include <stdint.h>
#include "ClearKeySessionManager.h" class GMPTask;
#define IO_SUCCEEDED(x) ((x) == cdm::FileIOClient::Status::kSuccess) // Responsible for ensuring that both aOnSuccess and aOnFailure are destroyed.
#define IO_FAILED(x) ((x) != cdm::FileIOClient::Status::kSuccess) void StoreData(const std::string& aRecordName,
// Writes data to a file and fires the appropriate callback when complete.
void WriteData(cdm::Host_8* aHost,
std::string& aRecordName,
const std::vector<uint8_t>& aData, const std::vector<uint8_t>& aData,
std::function<void()>&& aOnSuccess, GMPTask* aOnSuccess,
std::function<void()>&& aOnFailure); GMPTask* aOnFailure);
// Reads data from a file and fires the appropriate callback when complete. class ReadContinuation {
void ReadData(cdm::Host_8* aHost, public:
std::string& aRecordName, virtual void ReadComplete(GMPErr aStatus,
std::function<void(const uint8_t*, uint32_t)>&& aOnSuccess, const uint8_t* aData,
std::function<void()>&& aOnFailure); uint32_t aLength) = 0;
virtual ~ReadContinuation() {}
};
// Deletes aContinuation after running it to report the result.
void ReadData(const std::string& aSessionId,
ReadContinuation* aContinuation);
GMPErr EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc);
#endif // __ClearKeyStorage_h__ #endif // __ClearKeyStorage_h__

Просмотреть файл

@ -14,77 +14,33 @@
* limitations under the License. * limitations under the License.
*/ */
#include "ClearKeyUtils.h"
#include <algorithm> #include <algorithm>
#include <assert.h>
#include <stdlib.h>
#include <cctype>
#include <ctype.h> #include <ctype.h>
#include <memory.h>
#include <sstream>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <vector> #include <vector>
#include "ArrayUtils.h" #include "ClearKeyUtils.h"
#include "BigEndian.h"
#include "ClearKeyBase64.h" #include "ClearKeyBase64.h"
// This include is required in order for content_decryption_module to work #include "ArrayUtils.h"
// on Unix systems. #include <assert.h>
#include "stddef.h" #include <memory.h>
#include "content_decryption_module.h" #include "BigEndian.h"
#include "openaes/oaes_lib.h" #include "openaes/oaes_lib.h"
#include "psshparser/PsshParser.h"
using namespace cdm;
using namespace std; using namespace std;
void void
CK_Log(const char* aFmt, ...) CK_Log(const char* aFmt, ...)
{ {
FILE* out = stdout;
if (getenv("CLEARKEY_LOG_FILE")) {
out = fopen(getenv("CLEARKEY_LOG_FILE"), "a");
}
va_list ap; va_list ap;
va_start(ap, aFmt); va_start(ap, aFmt);
const size_t len = 1024; vprintf(aFmt, ap);
char buf[len];
vsnprintf(buf, len, aFmt, ap);
va_end(ap); va_end(ap);
fprintf(out, "%s\n", buf); printf("\n");
fflush(out); fflush(stdout);
if (out != stdout) {
fclose(out);
}
}
static bool
PrintableAsString(const uint8_t* aBytes, uint32_t aLength)
{
return all_of(aBytes, aBytes + aLength, [] (uint8_t c) {
return isprint(c) == 1;
});
}
void
CK_LogArray(const char* prepend,
const uint8_t* aData,
const uint32_t aDataSize)
{
// If the data is valid ascii, use that. Otherwise print the hex
string data = PrintableAsString(aData, aDataSize) ?
string(aData, aData + aDataSize) :
ClearKeyUtils::ToHexString(aData, aDataSize);
CK_LOGD("%s%s", prepend, data.c_str());
} }
static void static void
@ -159,9 +115,7 @@ EncodeBase64Web(vector<uint8_t> aBinary, string& aEncoded)
// Cast idx to size_t before using it as an array-index, // Cast idx to size_t before using it as an array-index,
// to pacify clang 'Wchar-subscripts' warning: // to pacify clang 'Wchar-subscripts' warning:
size_t idx = static_cast<size_t>(out[i]); size_t idx = static_cast<size_t>(out[i]);
assert(idx < MOZ_ARRAY_LENGTH(sAlphabet)); // out of bounds index for 'sAlphabet'
// out of bounds index for 'sAlphabet'
assert(idx < MOZ_ARRAY_LENGTH(sAlphabet));
out[i] = sAlphabet[idx]; out[i] = sAlphabet[idx];
} }
@ -171,7 +125,7 @@ EncodeBase64Web(vector<uint8_t> aBinary, string& aEncoded)
/* static */ void /* static */ void
ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs, ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
string& aOutRequest, string& aOutRequest,
SessionType aSessionType) GMPSessionType aSessionType)
{ {
assert(aKeyIDs.size() && aOutRequest.empty()); assert(aKeyIDs.size() && aOutRequest.empty());
@ -435,7 +389,7 @@ ParseKeys(ParserContext& aCtx, vector<KeyIdPair>& aOutKeys)
/* static */ bool /* static */ bool
ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize, ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
vector<KeyIdPair>& aOutKeys, vector<KeyIdPair>& aOutKeys,
SessionType aSessionType) GMPSessionType aSessionType)
{ {
ParserContext ctx; ParserContext ctx;
ctx.mIter = aKeyData; ctx.mIter = aKeyData;
@ -551,16 +505,15 @@ ClearKeyUtils::ParseKeyIdsInitData(const uint8_t* aInitData,
} }
/* static */ const char* /* static */ const char*
ClearKeyUtils::SessionTypeToString(SessionType aSessionType) ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType)
{ {
switch (aSessionType) { switch (aSessionType) {
case SessionType::kTemporary: return "temporary"; case kGMPTemporySession: return "temporary";
case SessionType::kPersistentLicense: return "persistent-license"; case kGMPPersistentSession: return "persistent-license";
default: { default: {
// We don't support any other license types. assert(false); // Should not reach here.
assert(false); return "invalid";
return "invalid"; }
}
} }
} }
@ -580,15 +533,9 @@ ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength)
return true; return true;
} }
string GMPMutex* GMPCreateMutex() {
ClearKeyUtils::ToHexString(const uint8_t * aBytes, uint32_t aLength) GMPMutex* mutex;
{ auto err = GetPlatform()->createmutex(&mutex);
stringstream ss; assert(mutex);
ss << std::showbase << std::uppercase << std::hex; return GMP_FAILED(err) ? nullptr : mutex;
for (uint32_t i = 0; i < aLength; ++i) {
ss << std::hex << static_cast<uint32_t>(aBytes[i]);
ss << " ";
}
return ss.str();
} }

Просмотреть файл

@ -21,29 +21,22 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <assert.h> #include <assert.h>
#include "gmp-api/gmp-decryption.h"
// This include is required in order for content_decryption_module to work
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#if 0 #if 0
void CK_Log(const char* aFmt, ...); void CK_Log(const char* aFmt, ...);
#define CK_LOGE(...) CK_Log(__VA_ARGS__) #define CK_LOGE(...) CK_Log(__VA_ARGS__)
#define CK_LOGD(...) CK_Log(__VA_ARGS__) #define CK_LOGD(...) CK_Log(__VA_ARGS__)
#define CK_LOGW(...) CK_Log(__VA_ARGS__) #define CK_LOGW(...) CK_Log(__VA_ARGS__)
#define CK_LOGARRAY(APREPEND, ADATA, ADATA_SIZE) CK_LogArray(APREPEND, \
ADATA, \
ADATA_SIZE)
#else #else
// Note: Enabling logging slows things down a LOT, especially when logging to
// a file.
#define CK_LOGE(...) #define CK_LOGE(...)
#define CK_LOGD(...) #define CK_LOGD(...)
#define CK_LOGW(...) #define CK_LOGW(...)
#define CK_LOGARRAY(APREPEND, ADATA, ADATA_SIZE)
#endif #endif
struct GMPPlatformAPI;
extern GMPPlatformAPI* GetPlatform();
typedef std::vector<uint8_t> KeyId; typedef std::vector<uint8_t> KeyId;
typedef std::vector<uint8_t> Key; typedef std::vector<uint8_t> Key;
@ -55,10 +48,6 @@ static const uint32_t kMaxSessionResponseLength = 65536;
static const uint32_t kMaxWebmInitDataSize = 65536; static const uint32_t kMaxWebmInitDataSize = 65536;
static const uint32_t kMaxKeyIdsLength = 512; static const uint32_t kMaxKeyIdsLength = 512;
void CK_LogArray(const char* aPrepend,
const uint8_t* aData,
const uint32_t aDataSize);
struct KeyIdPair struct KeyIdPair
{ {
KeyId mKeyId; KeyId mKeyId;
@ -77,16 +66,14 @@ public:
static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds, static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds,
std::string& aOutRequest, std::string& aOutRequest,
cdm::SessionType aSessionType); GMPSessionType aSessionType);
static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize, static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
std::vector<KeyIdPair>& aOutKeys, std::vector<KeyIdPair>& aOutKeys,
cdm::SessionType aSessionType); GMPSessionType aSessionType);
static const char* SessionTypeToString(cdm::SessionType aSessionType); static const char* SessionTypeToString(GMPSessionType aSessionType);
static bool IsValidSessionId(const char* aBuff, uint32_t aLength); static bool IsValidSessionId(const char* aBuff, uint32_t aLength);
static std::string ToHexString(const uint8_t * aBytes, uint32_t aLength);
}; };
template<class Container, class Element> template<class Container, class Element>
@ -96,6 +83,27 @@ Contains(const Container& aContainer, const Element& aElement)
return aContainer.find(aElement) != aContainer.end(); return aContainer.find(aElement) != aContainer.end();
} }
class AutoLock {
public:
explicit AutoLock(GMPMutex* aMutex)
: mMutex(aMutex)
{
assert(aMutex);
if (mMutex) {
mMutex->Acquire();
}
}
~AutoLock() {
if (mMutex) {
mMutex->Release();
}
}
private:
GMPMutex* mMutex;
};
GMPMutex* GMPCreateMutex();
template<typename T> template<typename T>
inline void inline void
Assign(std::vector<T>& aVec, const T* aData, size_t aLength) Assign(std::vector<T>& aVec, const T* aData, size_t aLength)

Просмотреть файл

@ -21,7 +21,41 @@
#include <assert.h> #include <assert.h>
#include "ClearKeyUtils.h" #include "ClearKeyUtils.h"
#if defined(_MSC_VER)
#include <atomic> #include <atomic>
typedef std::atomic<uint32_t> AtomicRefCount;
#else
class AtomicRefCount {
public:
explicit AtomicRefCount(uint32_t aValue)
: mCount(aValue)
, mMutex(GMPCreateMutex())
{
assert(mMutex);
}
~AtomicRefCount()
{
if (mMutex) {
mMutex->Destroy();
}
}
uint32_t operator--() {
AutoLock lock(mMutex);
return --mCount;
}
uint32_t operator++() {
AutoLock lock(mMutex);
return ++mCount;
}
operator uint32_t() {
AutoLock lock(mMutex);
return mCount;
}
private:
uint32_t mCount;
GMPMutex* mMutex;
};
#endif
// Note: Thread safe. // Note: Thread safe.
class RefCounted { class RefCounted {
@ -47,41 +81,27 @@ protected:
{ {
assert(!mRefCount); assert(!mRefCount);
} }
std::atomic<uint32_t> mRefCount; AtomicRefCount mRefCount;
}; };
template<class T> template<class T>
class RefPtr { class RefPtr {
public: public:
RefPtr(const RefPtr& src) { explicit RefPtr(T* aPtr) : mPtr(nullptr) {
Set(src.mPtr); Assign(aPtr);
} }
explicit RefPtr(T* aPtr) {
Set(aPtr);
}
RefPtr() { Set(nullptr); }
~RefPtr() { ~RefPtr() {
Set(nullptr); Assign(nullptr);
} }
T* operator->() const { return mPtr; } T* operator->() const { return mPtr; }
T** operator&() { return &mPtr; }
T* operator->() { return mPtr; }
operator T*() { return mPtr; }
T* Get() const { return mPtr; }
RefPtr& operator=(T* aVal) { RefPtr& operator=(T* aVal) {
Set(aVal); Assign(aVal);
return *this; return *this;
} }
private: private:
T* Set(T* aPtr) { void Assign(T* aPtr) {
if (mPtr == aPtr) {
return aPtr;
}
if (mPtr) { if (mPtr) {
mPtr->Release(); mPtr->Release();
} }
@ -89,10 +109,8 @@ private:
if (mPtr) { if (mPtr) {
aPtr->AddRef(); aPtr->AddRef();
} }
return mPtr;
} }
T* mPtr;
T* mPtr = nullptr;
}; };
#endif // __RefCount_h__ #endif // __RefCount_h__

Просмотреть файл

@ -14,162 +14,247 @@
* limitations under the License. * limitations under the License.
*/ */
#include <algorithm>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include "AnnexB.h"
#include "BigEndian.h" #include "BigEndian.h"
#include "ClearKeyDecryptionManager.h" #include "ClearKeyDecryptionManager.h"
#include "ClearKeyUtils.h" #include "ClearKeyUtils.h"
#include "gmp-task-utils.h"
#include "VideoDecoder.h" #include "VideoDecoder.h"
using namespace wmf; using namespace wmf;
using namespace cdm;
VideoDecoder::VideoDecoder(Host_8 *aHost) VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI)
: mHost(aHost) : mHostAPI(aHostAPI)
, mCallback(nullptr)
, mWorkerThread(nullptr)
, mMutex(nullptr)
, mNumInputTasks(0)
, mSentExtraData(false)
, mIsFlushing(false)
, mHasShutdown(false) , mHasShutdown(false)
{ {
// We drop the ref in DecodingComplete(). // We drop the ref in DecodingComplete().
AddRef(); AddRef();
mDecoder = new WMFH264Decoder();
uint32_t cores = std::max(1u, std::thread::hardware_concurrency());
HRESULT hr = mDecoder->Init(cores);
} }
VideoDecoder::~VideoDecoder() VideoDecoder::~VideoDecoder()
{ {
if (mMutex) {
mMutex->Destroy();
}
} }
Status void
VideoDecoder::InitDecode(const VideoDecoderConfig& aConfig) VideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings,
const uint8_t* aCodecSpecific,
uint32_t aCodecSpecificLength,
GMPVideoDecoderCallback* aCallback,
int32_t aCoreCount)
{ {
if (!mDecoder) { mCallback = aCallback;
assert(mCallback);
mDecoder = new WMFH264Decoder();
HRESULT hr = mDecoder->Init(aCoreCount);
if (FAILED(hr)) {
CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder"); CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
mCallback->Error(GMPGenericErr);
return Status::kDecodeError; return;
} }
return Status::kSuccess; auto err = GetPlatform()->createmutex(&mMutex);
if (GMP_FAILED(err)) {
CK_LOGD("VideoDecoder::InitDecode failed to create GMPMutex");
mCallback->Error(GMPGenericErr);
return;
}
// The first byte is mPacketizationMode, which is only relevant for
// WebRTC/OpenH264 usecase.
const uint8_t* avcc = aCodecSpecific + 1;
const uint8_t* avccEnd = aCodecSpecific + aCodecSpecificLength;
mExtraData.insert(mExtraData.end(), avcc, avccEnd);
AnnexB::ConvertConfig(mExtraData, mAnnexB);
} }
Status void
VideoDecoder::Decode(const InputBuffer& aInputBuffer, VideoFrame* aVideoFrame) VideoDecoder::EnsureWorker()
{ {
// If the input buffer we have been passed has a null buffer, it means we if (!mWorkerThread) {
// should drain. GetPlatform()->createthread(&mWorkerThread);
if (!aInputBuffer.data) { if (!mWorkerThread) {
// This will drain the decoder until there are no frames left to drain, mCallback->Error(GMPAllocErr);
// whereupon it will return 'NeedsMoreData'. return;
CK_LOGD("Input buffer null: Draining"); }
return Drain(aVideoFrame); }
}
void
VideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame,
bool aMissingFrames,
const uint8_t* aCodecSpecificInfo,
uint32_t aCodecSpecificInfoLength,
int64_t aRenderTimeMs)
{
if (aInputFrame->BufferType() != GMP_BufferLength32) {
// Gecko should only send frames with 4 byte NAL sizes to GMPs.
mCallback->Error(GMPGenericErr);
return;
} }
DecodeData* data = new DecodeData(); EnsureWorker();
Assign(data->mBuffer, aInputBuffer.data, aInputBuffer.data_size);
data->mTimestamp = aInputBuffer.timestamp;
data->mCrypto = CryptoMetaData(&aInputBuffer);
{
AutoLock lock(mMutex);
mNumInputTasks++;
}
// Note: we don't need the codec specific info on a per-frame basis.
// It's mostly useful for WebRTC use cases.
// Make a copy of the data, so we can release aInputFrame ASAP,
// to avoid too many shmem handles being held by the GMP process.
// If the GMP process holds on to too many shmem handles, the Gecko
// side can fail to allocate a shmem to send more input. This is
// particularly a problem in Gecko mochitests, which can open multiple
// actors at once which share the same pool of shmems.
DecodeData* data = new DecodeData();
Assign(data->mBuffer, aInputFrame->Buffer(), aInputFrame->Size());
data->mTimestamp = aInputFrame->TimeStamp();
data->mDuration = aInputFrame->Duration();
data->mIsKeyframe = (aInputFrame->FrameType() == kGMPKeyFrame);
const GMPEncryptedBufferMetadata* crypto = aInputFrame->GetDecryptionData();
if (crypto) {
data->mCrypto.Init(crypto);
}
aInputFrame->Destroy();
mWorkerThread->Post(WrapTaskRefCounted(this,
&VideoDecoder::DecodeTask,
data));
}
void
VideoDecoder::DecodeTask(DecodeData* aData)
{
CK_LOGD("VideoDecoder::DecodeTask"); CK_LOGD("VideoDecoder::DecodeTask");
AutoPtr<DecodeData> d(data); AutoPtr<DecodeData> d(aData);
HRESULT hr; HRESULT hr;
if (!data || !mDecoder) { {
CK_LOGE("Decode job not set up correctly!"); AutoLock lock(mMutex);
return Status::kDecodeError; mNumInputTasks--;
assert(mNumInputTasks >= 0);
} }
std::vector<uint8_t>& buffer = data->mBuffer; if (mIsFlushing) {
CK_LOGD("VideoDecoder::DecodeTask rejecting frame: flushing.");
return;
}
if (data->mCrypto.IsValid()) { if (!aData || !mHostAPI || !mDecoder) {
Status rv = CK_LOGE("Decode job not set up correctly!");
ClearKeyDecryptionManager::Get()->Decrypt(buffer, data->mCrypto); return;
}
if (STATUS_FAILED(rv)) { std::vector<uint8_t>& buffer = aData->mBuffer;
CK_LOGARRAY("Failed to decrypt video using key ", if (aData->mCrypto.IsValid()) {
aInputBuffer.key_id, // Plugin host should have set up its decryptor/key sessions
aInputBuffer.key_id_size); // before trying to decode!
return rv; GMPErr rv =
ClearKeyDecryptionManager::Get()->Decrypt(buffer, aData->mCrypto);
if (GMP_FAILED(rv)) {
MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::Error, rv));
return;
} }
} }
AnnexB::ConvertFrameInPlace(buffer);
if (aData->mIsKeyframe) {
// We must send the SPS and PPS to Windows Media Foundation's decoder.
// Note: We do this *after* decryption, otherwise the subsample info
// would be incorrect.
buffer.insert(buffer.begin(), mAnnexB.begin(), mAnnexB.end());
}
hr = mDecoder->Input(buffer.data(), hr = mDecoder->Input(buffer.data(),
buffer.size(), buffer.size(),
data->mTimestamp); aData->mTimestamp,
aData->mDuration);
CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr); CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
if (FAILED(hr)) { if (FAILED(hr)) {
assert(hr != MF_E_TRANSFORM_NEED_MORE_INPUT);
CK_LOGE("VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n", CK_LOGE("VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n",
hr, hr,
((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : "")); ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
CK_LOGD("Decode failed. The decoder is not accepting input"); return;
return Status::kDecodeError;
} }
return OutputFrame(aVideoFrame); while (hr == S_OK) {
}
Status VideoDecoder::OutputFrame(VideoFrame* aVideoFrame) {
HRESULT hr = S_OK;
// Read all the output from the decoder. Ideally, this would be a while loop
// where we read the output and check the result as the condition. However,
// this produces a memory leak connected to assigning a new CComPtr to the
// address of the old one, which avoids the CComPtr cleaning up.
while (true) {
CComPtr<IMFSample> output; CComPtr<IMFSample> output;
hr = mDecoder->Output(&output); hr = mDecoder->Output(&output);
if (hr != S_OK) {
break;
}
CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr); CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr);
if (hr == S_OK) {
MaybeRunOnMainThread(
WrapTaskRefCounted(this,
&VideoDecoder::ReturnOutput,
CComPtr<IMFSample>(output),
mDecoder->GetFrameWidth(),
mDecoder->GetFrameHeight(),
mDecoder->GetStride()));
}
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
AutoLock lock(mMutex);
if (mNumInputTasks == 0) {
// We have run all input tasks. We *must* notify Gecko so that it will
// send us more data.
MaybeRunOnMainThread(
WrapTask(mCallback,
&GMPVideoDecoderCallback::InputDataExhausted));
}
}
if (FAILED(hr)) {
CK_LOGE("VideoDecoder::DecodeTask() output failed hr=0x%x\n", hr);
}
}
}
mOutputQueue.push(output); void
CK_LOGD("Queue size: %u", mOutputQueue.size()); VideoDecoder::ReturnOutput(IMFSample* aSample,
int32_t aWidth,
int32_t aHeight,
int32_t aStride)
{
CK_LOGD("[%p] VideoDecoder::ReturnOutput()\n", this);
assert(aSample);
HRESULT hr;
GMPVideoFrame* f = nullptr;
auto err = mHostAPI->CreateFrame(kGMPI420VideoFrame, &f);
if (GMP_FAILED(err) || !f) {
CK_LOGE("Failed to create i420 frame!\n");
return;
}
if (HasShutdown()) {
// Note: GMPVideoHost::CreateFrame() can process messages before returning,
// including a message that calls VideoDecoder::DecodingComplete(), i.e.
// we can shutdown during the call!
CK_LOGD("Shutdown while waiting on GMPVideoHost::CreateFrame()!\n");
f->Destroy();
return;
} }
// If we don't have any inputs, we need more data. auto vf = static_cast<GMPVideoi420Frame*>(f);
if (mOutputQueue.empty()) {
CK_LOGD("Decode failed. Not enought data; Requesting more input");
return Status::kNeedMoreData;
}
// We will get a MF_E_TRANSFORM_NEED_MORE_INPUT every time, as we always hr = SampleToVideoFrame(aSample, aWidth, aHeight, aStride, vf);
// consume everything in the buffer. ENSURE(SUCCEEDED(hr), /*void*/);
if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT && FAILED(hr)) {
CK_LOGD("Decode failed output ret=0x%x\n", hr);
return Status::kDecodeError;
}
CComPtr<IMFSample> result = mOutputQueue.front(); mCallback->Decoded(vf);
mOutputQueue.pop();
// The Chromium CDM API doesn't have support for negative strides, though
// they are theoretically possible in real world data.
if (mDecoder->GetStride() <= 0) {
return Status::kDecodeError;
}
hr = SampleToVideoFrame(result,
mDecoder->GetFrameWidth(),
mDecoder->GetFrameHeight(),
mDecoder->GetStride(),
aVideoFrame);
if (FAILED(hr)) {
return Status::kDecodeError;
}
CK_LOGD("Decode succeeded.");
return Status::kSuccess;
} }
HRESULT HRESULT
@ -177,19 +262,14 @@ VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
int32_t aWidth, int32_t aWidth,
int32_t aHeight, int32_t aHeight,
int32_t aStride, int32_t aStride,
VideoFrame* aVideoFrame) GMPVideoi420Frame* aVideoFrame)
{ {
CK_LOGD("[%p] VideoDecoder::SampleToVideoFrame()\n", this);
assert(aSample);
ENSURE(aSample != nullptr, E_POINTER); ENSURE(aSample != nullptr, E_POINTER);
ENSURE(aVideoFrame != nullptr, E_POINTER); ENSURE(aVideoFrame != nullptr, E_POINTER);
HRESULT hr; HRESULT hr;
CComPtr<IMFMediaBuffer> mediaBuffer; CComPtr<IMFMediaBuffer> mediaBuffer;
aVideoFrame->SetFormat(kI420);
// Must convert to contiguous mediaBuffer to use IMD2DBuffer interface. // Must convert to contiguous mediaBuffer to use IMD2DBuffer interface.
hr = aSample->ConvertToContiguousBuffer(&mediaBuffer); hr = aSample->ConvertToContiguousBuffer(&mediaBuffer);
ENSURE(SUCCEEDED(hr), hr); ENSURE(SUCCEEDED(hr), hr);
@ -205,60 +285,46 @@ VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
hr = twoDBuffer->Lock2D(&data, &stride); hr = twoDBuffer->Lock2D(&data, &stride);
ENSURE(SUCCEEDED(hr), hr); ENSURE(SUCCEEDED(hr), hr);
} else { } else {
hr = mediaBuffer->Lock(&data, nullptr, nullptr); hr = mediaBuffer->Lock(&data, NULL, NULL);
ENSURE(SUCCEEDED(hr), hr); ENSURE(SUCCEEDED(hr), hr);
stride = aStride; stride = aStride;
} }
// The U and V planes are stored 16-row-aligned, so we need to add padding // The V and U planes are stored 16-row-aligned, so we need to add padding
// to the row heights to ensure the Y'CbCr planes are referenced properly. // to the row heights to ensure the Y'CbCr planes are referenced properly.
// YV12, planar format: [YYYY....][UUUU....][VVVV....] // YV12, planar format: [YYYY....][VVVV....][UUUU....]
// i.e., Y, then U, then V. // i.e., Y, then V, then U.
uint32_t padding = 0; uint32_t padding = 0;
if (aHeight % 16 != 0) { if (aHeight % 16 != 0) {
padding = 16 - (aHeight % 16); padding = 16 - (aHeight % 16);
} }
uint32_t ySize = stride * (aHeight + padding); int32_t y_size = stride * (aHeight + padding);
uint32_t uSize = stride * (aHeight + padding) / 4; int32_t v_size = stride * (aHeight + padding) / 4;
uint32_t halfStride = (stride + 1) / 2; int32_t halfStride = (stride + 1) / 2;
uint32_t halfHeight = (aHeight + 1) / 2; int32_t halfHeight = (aHeight + 1) / 2;
aVideoFrame->SetStride(VideoFrame::kYPlane, stride); auto err = aVideoFrame->CreateEmptyFrame(stride, aHeight, stride, halfStride, halfStride);
aVideoFrame->SetStride(VideoFrame::kUPlane, halfStride); ENSURE(GMP_SUCCEEDED(err), E_FAIL);
aVideoFrame->SetStride(VideoFrame::kVPlane, halfStride);
aVideoFrame->SetSize(Size(aWidth, aHeight)); err = aVideoFrame->SetWidth(aWidth);
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
err = aVideoFrame->SetHeight(aHeight);
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
uint64_t bufferSize = ySize + 2 * uSize; uint8_t* outBuffer = aVideoFrame->Buffer(kGMPYPlane);
ENSURE(outBuffer != nullptr, E_FAIL);
assert(aVideoFrame->AllocatedSize(kGMPYPlane) >= stride*aHeight);
memcpy(outBuffer, data, stride*aHeight);
// If the buffer is bigger than the max for a 32 bit, fail to avoid buffer outBuffer = aVideoFrame->Buffer(kGMPUPlane);
// overflows. ENSURE(outBuffer != nullptr, E_FAIL);
if (bufferSize > UINT32_MAX) { assert(aVideoFrame->AllocatedSize(kGMPUPlane) >= halfStride*halfHeight);
return Status::kDecodeError; memcpy(outBuffer, data+y_size, halfStride*halfHeight);
}
// Get the buffer from the host. outBuffer = aVideoFrame->Buffer(kGMPVPlane);
Buffer* buffer = mHost->Allocate(bufferSize); ENSURE(outBuffer != nullptr, E_FAIL);
aVideoFrame->SetFrameBuffer(buffer); assert(aVideoFrame->AllocatedSize(kGMPVPlane) >= halfStride*halfHeight);
memcpy(outBuffer, data + y_size + v_size, halfStride*halfHeight);
// Make sure the buffer is non-null (allocate guarantees it will be of
// sufficient size).
if (!buffer) {
return E_OUTOFMEMORY;
}
uint8_t* outBuffer = buffer->Data();
aVideoFrame->SetPlaneOffset(VideoFrame::kYPlane, 0);
// Offset is the size of the copied y data.
aVideoFrame->SetPlaneOffset(VideoFrame::kUPlane, ySize);
// Offset is the size of the copied y data + the size of the copied u data.
aVideoFrame->SetPlaneOffset(VideoFrame::kVPlane, ySize + uSize);
// Copy the data.
memcpy(outBuffer, data, ySize + uSize * 2);
if (twoDBuffer) { if (twoDBuffer) {
twoDBuffer->Unlock2D(); twoDBuffer->Unlock2D();
@ -269,46 +335,84 @@ VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
LONGLONG hns = 0; LONGLONG hns = 0;
hr = aSample->GetSampleTime(&hns); hr = aSample->GetSampleTime(&hns);
ENSURE(SUCCEEDED(hr), hr); ENSURE(SUCCEEDED(hr), hr);
aVideoFrame->SetTimestamp(HNsToUsecs(hns)); aVideoFrame->SetTimestamp(HNsToUsecs(hns));
hr = aSample->GetSampleDuration(&hns);
ENSURE(SUCCEEDED(hr), hr);
aVideoFrame->SetDuration(HNsToUsecs(hns));
return S_OK; return S_OK;
} }
void
VideoDecoder::ResetCompleteTask()
{
mIsFlushing = false;
if (mCallback) {
MaybeRunOnMainThread(WrapTask(mCallback,
&GMPVideoDecoderCallback::ResetComplete));
}
}
void void
VideoDecoder::Reset() VideoDecoder::Reset()
{ {
CK_LOGD("VideoDecoder::Reset"); mIsFlushing = true;
if (mDecoder) { if (mDecoder) {
mDecoder->Reset(); mDecoder->Reset();
} }
// Remove all the frames from the output queue. // Schedule ResetComplete callback to run after existing frames have been
while (!mOutputQueue.empty()) { // flushed out of the task queue.
mOutputQueue.pop(); EnsureWorker();
} mWorkerThread->Post(WrapTaskRefCounted(this,
&VideoDecoder::ResetCompleteTask));
} }
Status void
VideoDecoder::Drain(VideoFrame* aVideoFrame) VideoDecoder::DrainTask()
{ {
CK_LOGD("VideoDecoder::Drain()");
if (!mDecoder) {
CK_LOGD("Drain failed! Decoder was not initialized");
return Status::kDecodeError;
}
mDecoder->Drain(); mDecoder->Drain();
// Return any pending output. // Return any pending output.
return OutputFrame(aVideoFrame); HRESULT hr = S_OK;
while (hr == S_OK) {
CComPtr<IMFSample> output;
hr = mDecoder->Output(&output);
CK_LOGD("VideoDecoder::DrainTask() output ret=0x%x\n", hr);
if (hr == S_OK) {
MaybeRunOnMainThread(
WrapTaskRefCounted(this,
&VideoDecoder::ReturnOutput,
CComPtr<IMFSample>(output),
mDecoder->GetFrameWidth(),
mDecoder->GetFrameHeight(),
mDecoder->GetStride()));
}
}
MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
}
void
VideoDecoder::Drain()
{
if (!mDecoder) {
if (mCallback) {
mCallback->DrainComplete();
}
return;
}
EnsureWorker();
mWorkerThread->Post(WrapTaskRefCounted(this,
&VideoDecoder::DrainTask));
} }
void void
VideoDecoder::DecodingComplete() VideoDecoder::DecodingComplete()
{ {
if (mWorkerThread) {
mWorkerThread->Join();
}
mHasShutdown = true; mHasShutdown = true;
// Release the reference we added in the constructor. There may be // Release the reference we added in the constructor. There may be
@ -316,3 +420,36 @@ VideoDecoder::DecodingComplete()
// us alive a little longer. // us alive a little longer.
Release(); Release();
} }
void
VideoDecoder::MaybeRunOnMainThread(GMPTask* aTask)
{
class MaybeRunTask : public GMPTask
{
public:
MaybeRunTask(VideoDecoder* aDecoder, GMPTask* aTask)
: mDecoder(aDecoder), mTask(aTask)
{ }
virtual void Run(void) {
if (mDecoder->HasShutdown()) {
CK_LOGD("Trying to dispatch to main thread after VideoDecoder has shut down");
return;
}
mTask->Run();
}
virtual void Destroy()
{
mTask->Destroy();
delete this;
}
private:
RefPtr<VideoDecoder> mDecoder;
GMPTask* mTask;
};
GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask));
}

Просмотреть файл

@ -18,28 +18,37 @@
#define __VideoDecoder_h__ #define __VideoDecoder_h__
#include <atomic> #include <atomic>
#include <queue>
#include <thread>
// This include is required in order for content_decryption_module to work #include "gmp-task-utils.h"
// on Unix systems. #include "gmp-video-decode.h"
#include "stddef.h" #include "gmp-video-host.h"
#include "content_decryption_module.h"
#include "WMFH264Decoder.h" #include "WMFH264Decoder.h"
class VideoDecoder : public RefCounted #include "mfobjects.h"
class VideoDecoder : public GMPVideoDecoder
, public RefCounted
{ {
public: public:
explicit VideoDecoder(cdm::Host_8 *aHost); explicit VideoDecoder(GMPVideoHost *aHostAPI);
cdm::Status InitDecode(const cdm::VideoDecoderConfig& aConfig); virtual void InitDecode(const GMPVideoCodec& aCodecSettings,
const uint8_t* aCodecSpecific,
uint32_t aCodecSpecificLength,
GMPVideoDecoderCallback* aCallback,
int32_t aCoreCount) override;
cdm::Status Decode(const cdm::InputBuffer& aEncryptedBuffer, virtual void Decode(GMPVideoEncodedFrame* aInputFrame,
cdm::VideoFrame* aVideoFrame); bool aMissingFrames,
const uint8_t* aCodecSpecific,
uint32_t aCodecSpecificLength,
int64_t aRenderTimeMs = -1);
void Reset(); virtual void Reset() override;
void DecodingComplete(); virtual void Drain() override;
virtual void DecodingComplete() override;
bool HasShutdown() { return mHasShutdown; } bool HasShutdown() { return mHasShutdown; }
@ -47,26 +56,53 @@ private:
virtual ~VideoDecoder(); virtual ~VideoDecoder();
cdm::Status Drain(cdm::VideoFrame* aVideoFrame); void EnsureWorker();
void DrainTask();
struct DecodeData { struct DecodeData {
DecodeData()
: mTimestamp(0)
, mDuration(0)
, mIsKeyframe(false)
{}
std::vector<uint8_t> mBuffer; std::vector<uint8_t> mBuffer;
uint64_t mTimestamp = 0; uint64_t mTimestamp;
uint64_t mDuration;
bool mIsKeyframe;
CryptoMetaData mCrypto; CryptoMetaData mCrypto;
}; };
cdm::Status OutputFrame(cdm::VideoFrame* aVideoFrame); void DecodeTask(DecodeData* aData);
void ResetCompleteTask();
void ReturnOutput(IMFSample* aSample,
int32_t aWidth,
int32_t aHeight,
int32_t aStride);
HRESULT SampleToVideoFrame(IMFSample* aSample, HRESULT SampleToVideoFrame(IMFSample* aSample,
int32_t aWidth, int32_t aWidth,
int32_t aHeight, int32_t aHeight,
int32_t aStride, int32_t aStride,
cdm::VideoFrame* aVideoFrame); GMPVideoi420Frame* aVideoFrame);
cdm::Host_8* mHost; void MaybeRunOnMainThread(GMPTask* aTask);
GMPVideoHost *mHostAPI; // host-owned, invalid at DecodingComplete
GMPVideoDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
GMPThread* mWorkerThread;
GMPMutex* mMutex;
wmf::AutoPtr<wmf::WMFH264Decoder> mDecoder; wmf::AutoPtr<wmf::WMFH264Decoder> mDecoder;
std::queue<wmf::CComPtr<IMFSample>> mOutputQueue; std::vector<uint8_t> mExtraData;
std::vector<uint8_t> mAnnexB;
int32_t mNumInputTasks;
bool mSentExtraData;
std::atomic<bool> mIsFlushing;
bool mHasShutdown; bool mHasShutdown;
}; };

Просмотреть файл

@ -196,6 +196,7 @@ HRESULT
WMFH264Decoder::CreateInputSample(const uint8_t* aData, WMFH264Decoder::CreateInputSample(const uint8_t* aData,
uint32_t aDataSize, uint32_t aDataSize,
Microseconds aTimestamp, Microseconds aTimestamp,
Microseconds aDuration,
IMFSample** aOutSample) IMFSample** aOutSample)
{ {
HRESULT hr; HRESULT hr;
@ -230,6 +231,8 @@ WMFH264Decoder::CreateInputSample(const uint8_t* aData,
hr = sample->SetSampleTime(UsecsToHNs(aTimestamp)); hr = sample->SetSampleTime(UsecsToHNs(aTimestamp));
ENSURE(SUCCEEDED(hr), hr); ENSURE(SUCCEEDED(hr), hr);
sample->SetSampleDuration(UsecsToHNs(aDuration));
*aOutSample = sample.Detach(); *aOutSample = sample.Detach();
return S_OK; return S_OK;
@ -298,11 +301,12 @@ WMFH264Decoder::GetOutputSample(IMFSample** aOutSample)
HRESULT HRESULT
WMFH264Decoder::Input(const uint8_t* aData, WMFH264Decoder::Input(const uint8_t* aData,
uint32_t aDataSize, uint32_t aDataSize,
Microseconds aTimestamp) Microseconds aTimestamp,
Microseconds aDuration)
{ {
HRESULT hr; HRESULT hr;
CComPtr<IMFSample> input = nullptr; CComPtr<IMFSample> input = nullptr;
hr = CreateInputSample(aData, aDataSize, aTimestamp, &input); hr = CreateInputSample(aData, aDataSize, aTimestamp, aDuration, &input);
ENSURE(SUCCEEDED(hr) && input!=nullptr, hr); ENSURE(SUCCEEDED(hr) && input!=nullptr, hr);
hr = mDecoder->ProcessInput(0, input, 0); hr = mDecoder->ProcessInput(0, input, 0);

Просмотреть файл

@ -30,7 +30,8 @@ public:
HRESULT Input(const uint8_t* aData, HRESULT Input(const uint8_t* aData,
uint32_t aDataSize, uint32_t aDataSize,
Microseconds aTimestamp); Microseconds aTimestamp,
Microseconds aDuration);
HRESULT Output(IMFSample** aOutput); HRESULT Output(IMFSample** aOutput);
@ -52,6 +53,7 @@ private:
HRESULT CreateInputSample(const uint8_t* aData, HRESULT CreateInputSample(const uint8_t* aData,
uint32_t aDataSize, uint32_t aDataSize,
Microseconds aTimestamp, Microseconds aTimestamp,
Microseconds aDuration,
IMFSample** aOutSample); IMFSample** aOutSample);
HRESULT CreateOutputSample(IMFSample** aOutSample); HRESULT CreateOutputSample(IMFSample** aOutSample);

Просмотреть файл

@ -119,8 +119,8 @@ typedef int64_t Microseconds;
#define ENSURE(condition, ret) \ #define ENSURE(condition, ret) \
{ if (!(condition)) { LOG("##condition## FAILED %S:%d\n", __FILE__, __LINE__); return ret; } } { if (!(condition)) { LOG("##condition## FAILED %S:%d\n", __FILE__, __LINE__); return ret; } }
#define STATUS_SUCCEEDED(x) ((x) == Status::kSuccess) #define GMP_SUCCEEDED(x) ((x) == GMPNoErr)
#define STATUS_FAILED(x) ((x) != Status::kSuccess) #define GMP_FAILED(x) ((x) != GMPNoErr)
#define MFPLAT_FUNC(_func, _dllname) \ #define MFPLAT_FUNC(_func, _dllname) \
extern decltype(::_func)* _func; extern decltype(::_func)* _func;

Просмотреть файл

@ -18,47 +18,68 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "ClearKeyCDM.h" #include "ClearKeyAsyncShutdown.h"
#include "ClearKeySessionManager.h" #include "ClearKeySessionManager.h"
// This include is required in order for content_decryption_module to work #include "gmp-api/gmp-async-shutdown.h"
// on Unix systems. #include "gmp-api/gmp-decryption.h"
#include "stddef.h" #include "gmp-api/gmp-platform.h"
#include "content_decryption_module.h"
#ifdef ENABLE_WMF #if defined(ENABLE_WMF)
#include "WMFUtils.h" #include "WMFUtils.h"
#endif // ENABLE_WMF #include "VideoDecoder.h"
#endif
#if defined(WIN32)
#define GMP_EXPORT __declspec(dllexport)
#else
#define GMP_EXPORT __attribute__((visibility("default")))
#endif
static GMPPlatformAPI* sPlatform = nullptr;
GMPPlatformAPI*
GetPlatform()
{
return sPlatform;
}
extern "C" { extern "C" {
CDM_EXPORT GMP_EXPORT GMPErr
void INITIALIZE_CDM_MODULE() { GMPInit(GMPPlatformAPI* aPlatformAPI)
{
sPlatform = aPlatformAPI;
return GMPNoErr;
} }
CDM_EXPORT GMP_EXPORT GMPErr
void* CreateCdmInstance(int cdm_interface_version, GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func,
void* user_data)
{ {
CK_LOGD("ClearKey GMPGetAPI |%s|", aApiName);
assert(!*aPluginAPI);
CK_LOGE("ClearKey CreateCDMInstance"); if (!strcmp(aApiName, GMP_API_DECRYPTOR)) {
*aPluginAPI = new ClearKeySessionManager();
#ifdef ENABLE_WMF }
if (!wmf::EnsureLibs()) { #if defined(ENABLE_WMF)
CK_LOGE("Required libraries were not found"); else if (!strcmp(aApiName, GMP_API_VIDEO_DECODER) &&
return nullptr; wmf::EnsureLibs()) {
*aPluginAPI = new VideoDecoder(static_cast<GMPVideoHost*>(aHostAPI));
} }
#endif #endif
else if (!strcmp(aApiName, GMP_API_ASYNC_SHUTDOWN)) {
*aPluginAPI = new ClearKeyAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
} else {
CK_LOGE("GMPGetAPI couldn't resolve API name |%s|\n", aApiName);
}
cdm::Host_8* host = static_cast<cdm::Host_8*>( return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
get_cdm_host_func(cdm_interface_version, user_data));
ClearKeyCDM* clearKey = new ClearKeyCDM(host);
CK_LOGE("Created ClearKeyCDM instance!");
return clearKey;
} }
GMP_EXPORT GMPErr
GMPShutdown(void)
{
CK_LOGD("ClearKey GMPShutdown");
return GMPNoErr;
}
} }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,47 @@
/*
* Copyright 2015, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Original author: ekr@rtfm.com
#ifndef gmp_task_utils_h_
#define gmp_task_utils_h_
#include "gmp-api/gmp-platform.h"
class gmp_task_args_base : public GMPTask {
public:
virtual void Destroy() { delete this; }
virtual void Run() = 0;
};
// The generated file contains four major function templates
// (in variants for arbitrary numbers of arguments up to 10,
// which is why it is machine generated). The four templates
// are:
//
// WrapTask(o, m, ...) -- wraps a member function m of an object ptr o
// WrapTaskRet(o, m, ..., r) -- wraps a member function m of an object ptr o
// the function returns something that can
// be assigned to *r
// WrapTaskNM(f, ...) -- wraps a function f
// WrapTaskNMRet(f, ..., r) -- wraps a function f that returns something
// that can be assigned to *r
//
// All of these template functions return a GMPTask* which can be passed
// to DispatchXX().
#include "gmp-task-utils-generated.h"
#endif // gmp_task_utils_h_

Просмотреть файл

@ -11,8 +11,8 @@ FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
FINAL_TARGET_PP_FILES += ['manifest.json.in'] FINAL_TARGET_PP_FILES += ['manifest.json.in']
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
'ClearKeyAsyncShutdown.cpp',
'ClearKeyBase64.cpp', 'ClearKeyBase64.cpp',
'ClearKeyCDM.cpp',
'ClearKeyDecryptionManager.cpp', 'ClearKeyDecryptionManager.cpp',
'ClearKeyPersistence.cpp', 'ClearKeyPersistence.cpp',
'ClearKeySession.cpp', 'ClearKeySession.cpp',
@ -28,6 +28,7 @@ SOURCES += [
if CONFIG['OS_ARCH'] == 'WINNT': if CONFIG['OS_ARCH'] == 'WINNT':
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
'AnnexB.cpp',
'VideoDecoder.cpp', 'VideoDecoder.cpp',
'WMFH264Decoder.cpp', 'WMFH264Decoder.cpp',
] ]
@ -42,13 +43,15 @@ if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['ENABLE_WMF'] = True DEFINES['ENABLE_WMF'] = True
DEFINES['CDM_IMPLEMENTATION'] = True
TEST_DIRS += [ TEST_DIRS += [
'gtest', 'gtest',
] ]
LOCAL_INCLUDES += [
'/dom/media/gmp',
]
DISABLE_STL_WRAPPING = True DISABLE_STL_WRAPPING = True
DEFINES['MOZ_NO_MOZALLOC'] = True DEFINES['MOZ_NO_MOZALLOC'] = True