mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-28 19:37:32 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
79
media/gmp-clearkey/0.1/AnnexB.cpp
Normal file
79
media/gmp-clearkey/0.1/AnnexB.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
32
media/gmp-clearkey/0.1/AnnexB.h
Normal file
32
media/gmp-clearkey/0.1/AnnexB.h
Normal file
|
|
@ -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__
|
||||
23
media/gmp-clearkey/0.1/ArrayUtils.h
Normal file
23
media/gmp-clearkey/0.1/ArrayUtils.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 __ArrayUtils_h__
|
||||
#define __ArrayUtils_h__
|
||||
|
||||
#define MOZ_ARRAY_LENGTH(array_) \
|
||||
(sizeof(array_)/sizeof(array_[0]))
|
||||
|
||||
#endif
|
||||
312
media/gmp-clearkey/0.1/AudioDecoder.cpp
Normal file
312
media/gmp-clearkey/0.1/AudioDecoder.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* 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 <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include "AudioDecoder.h"
|
||||
#include "ClearKeyDecryptionManager.h"
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "gmp-task-utils.h"
|
||||
|
||||
using namespace wmf;
|
||||
|
||||
AudioDecoder::AudioDecoder(GMPAudioHost *aHostAPI)
|
||||
: mHostAPI(aHostAPI)
|
||||
, mCallback(nullptr)
|
||||
, mWorkerThread(nullptr)
|
||||
, mMutex(nullptr)
|
||||
, mNumInputTasks(0)
|
||||
, mHasShutdown(false)
|
||||
{
|
||||
// We drop the ref in DecodingComplete().
|
||||
AddRef();
|
||||
}
|
||||
|
||||
AudioDecoder::~AudioDecoder()
|
||||
{
|
||||
if (mMutex) {
|
||||
mMutex->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::InitDecode(const GMPAudioCodec& aConfig,
|
||||
GMPAudioDecoderCallback* aCallback)
|
||||
{
|
||||
mCallback = aCallback;
|
||||
assert(mCallback);
|
||||
mDecoder = new WMFAACDecoder();
|
||||
HRESULT hr = mDecoder->Init(aConfig.mChannelCount,
|
||||
aConfig.mSamplesPerSecond,
|
||||
(BYTE*)aConfig.mExtraData,
|
||||
aConfig.mExtraDataLen);
|
||||
LOG("[%p] AudioDecoder::InitializeAudioDecoder() hr=0x%x\n", this, hr);
|
||||
if (FAILED(hr)) {
|
||||
mCallback->Error(GMPGenericErr);
|
||||
return;
|
||||
}
|
||||
auto err = GetPlatform()->createmutex(&mMutex);
|
||||
if (GMP_FAILED(err)) {
|
||||
mCallback->Error(GMPGenericErr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::EnsureWorker()
|
||||
{
|
||||
if (!mWorkerThread) {
|
||||
GetPlatform()->createthread(&mWorkerThread);
|
||||
if (!mWorkerThread) {
|
||||
mCallback->Error(GMPAllocErr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::Decode(GMPAudioSamples* aInput)
|
||||
{
|
||||
EnsureWorker();
|
||||
{
|
||||
AutoLock lock(mMutex);
|
||||
mNumInputTasks++;
|
||||
}
|
||||
mWorkerThread->Post(WrapTaskRefCounted(this,
|
||||
&AudioDecoder::DecodeTask,
|
||||
aInput));
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::DecodeTask(GMPAudioSamples* aInput)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
{
|
||||
AutoLock lock(mMutex);
|
||||
mNumInputTasks--;
|
||||
assert(mNumInputTasks >= 0);
|
||||
}
|
||||
|
||||
if (!aInput || !mHostAPI || !mDecoder) {
|
||||
LOG("Decode job not set up correctly!");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t* inBuffer = aInput->Buffer();
|
||||
if (!inBuffer) {
|
||||
LOG("No buffer for encoded samples!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData();
|
||||
std::vector<uint8_t> buffer(inBuffer, inBuffer + aInput->Size());
|
||||
if (crypto) {
|
||||
// Plugin host should have set up its decryptor/key sessions
|
||||
// before trying to decode!
|
||||
GMPErr rv =
|
||||
ClearKeyDecryptionManager::Get()->Decrypt(buffer, CryptoMetaData(crypto));
|
||||
|
||||
if (GMP_FAILED(rv)) {
|
||||
CK_LOGE("Failed to decrypt with key id %08x...", *(uint32_t*)crypto->KeyId());
|
||||
MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hr = mDecoder->Input(&buffer[0],
|
||||
buffer.size(),
|
||||
aInput->TimeStamp());
|
||||
|
||||
// We must delete the input sample!
|
||||
GetPlatform()->runonmainthread(WrapTask(aInput, &GMPAudioSamples::Destroy));
|
||||
|
||||
SAMPLE_LOG("AudioDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
|
||||
if (FAILED(hr)) {
|
||||
LOG("AudioDecoder::DecodeTask() decode failed ret=0x%x%s\n",
|
||||
hr,
|
||||
((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
|
||||
return;
|
||||
}
|
||||
|
||||
while (hr == S_OK) {
|
||||
CComPtr<IMFSample> output;
|
||||
hr = mDecoder->Output(&output);
|
||||
SAMPLE_LOG("AudioDecoder::DecodeTask() output ret=0x%x\n", hr);
|
||||
if (hr == S_OK) {
|
||||
ReturnOutput(output);
|
||||
}
|
||||
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, &GMPAudioDecoderCallback::InputDataExhausted));
|
||||
}
|
||||
} else if (FAILED(hr)) {
|
||||
LOG("AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::ReturnOutput(IMFSample* aSample)
|
||||
{
|
||||
SAMPLE_LOG("[%p] AudioDecoder::ReturnOutput()\n", this);
|
||||
assert(aSample);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
GMPAudioSamples* samples = nullptr;
|
||||
mHostAPI->CreateSamples(kGMPAudioIS16Samples, &samples);
|
||||
if (!samples) {
|
||||
LOG("Failed to create i420 frame!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hr = MFToGMPSample(aSample, samples);
|
||||
if (FAILED(hr)) {
|
||||
samples->Destroy();
|
||||
LOG("Failed to prepare output sample!");
|
||||
return;
|
||||
}
|
||||
ENSURE(SUCCEEDED(hr), /*void*/);
|
||||
|
||||
MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
|
||||
}
|
||||
|
||||
HRESULT
|
||||
AudioDecoder::MFToGMPSample(IMFSample* aInput,
|
||||
GMPAudioSamples* aOutput)
|
||||
{
|
||||
ENSURE(aInput != nullptr, E_POINTER);
|
||||
ENSURE(aOutput != nullptr, E_POINTER);
|
||||
|
||||
HRESULT hr;
|
||||
CComPtr<IMFMediaBuffer> mediaBuffer;
|
||||
|
||||
hr = aInput->ConvertToContiguousBuffer(&mediaBuffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
|
||||
DWORD maxLength = 0, currentLength = 0;
|
||||
hr = mediaBuffer->Lock(&data, &maxLength, ¤tLength);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
auto err = aOutput->SetBufferSize(currentLength);
|
||||
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
|
||||
|
||||
memcpy(aOutput->Buffer(), data, currentLength);
|
||||
|
||||
mediaBuffer->Unlock();
|
||||
|
||||
LONGLONG hns = 0;
|
||||
hr = aInput->GetSampleTime(&hns);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
aOutput->SetTimeStamp(HNsToUsecs(hns));
|
||||
aOutput->SetChannels(mDecoder->Channels());
|
||||
aOutput->SetRate(mDecoder->Rate());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::Reset()
|
||||
{
|
||||
if (mDecoder) {
|
||||
mDecoder->Reset();
|
||||
}
|
||||
if (mCallback) {
|
||||
mCallback->ResetComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::DrainTask()
|
||||
{
|
||||
mDecoder->Drain();
|
||||
|
||||
// Return any pending output.
|
||||
HRESULT hr = S_OK;
|
||||
while (hr == S_OK) {
|
||||
CComPtr<IMFSample> output;
|
||||
hr = mDecoder->Output(&output);
|
||||
SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr);
|
||||
if (hr == S_OK) {
|
||||
ReturnOutput(output);
|
||||
}
|
||||
}
|
||||
MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::Drain()
|
||||
{
|
||||
if (!mDecoder) {
|
||||
return;
|
||||
}
|
||||
EnsureWorker();
|
||||
mWorkerThread->Post(WrapTaskRefCounted(this,
|
||||
&AudioDecoder::DrainTask));
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::DecodingComplete()
|
||||
{
|
||||
if (mWorkerThread) {
|
||||
mWorkerThread->Join();
|
||||
}
|
||||
mHasShutdown = true;
|
||||
|
||||
// Release the reference we added in the constructor. There may be
|
||||
// WrapRefCounted tasks that also hold references to us, and keep
|
||||
// us alive a little longer.
|
||||
Release();
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecoder::MaybeRunOnMainThread(GMPTask* aTask)
|
||||
{
|
||||
class MaybeRunTask : public GMPTask
|
||||
{
|
||||
public:
|
||||
MaybeRunTask(AudioDecoder* aDecoder, GMPTask* aTask)
|
||||
: mDecoder(aDecoder), mTask(aTask)
|
||||
{ }
|
||||
|
||||
virtual void Run(void) {
|
||||
if (mDecoder->HasShutdown()) {
|
||||
CK_LOGD("Trying to dispatch to main thread after AudioDecoder has shut down");
|
||||
return;
|
||||
}
|
||||
|
||||
mTask->Run();
|
||||
}
|
||||
|
||||
virtual void Destroy()
|
||||
{
|
||||
mTask->Destroy();
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<AudioDecoder> mDecoder;
|
||||
GMPTask* mTask;
|
||||
};
|
||||
|
||||
GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask));
|
||||
}
|
||||
73
media/gmp-clearkey/0.1/AudioDecoder.h
Normal file
73
media/gmp-clearkey/0.1/AudioDecoder.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 __AudioDecoder_h__
|
||||
#define __AudioDecoder_h__
|
||||
|
||||
#include "gmp-audio-decode.h"
|
||||
#include "gmp-audio-host.h"
|
||||
#include "gmp-task-utils.h"
|
||||
#include "WMFAACDecoder.h"
|
||||
#include "RefCounted.h"
|
||||
|
||||
#include "mfobjects.h"
|
||||
|
||||
class AudioDecoder : public GMPAudioDecoder
|
||||
, public RefCounted
|
||||
{
|
||||
public:
|
||||
AudioDecoder(GMPAudioHost *aHostAPI);
|
||||
|
||||
virtual void InitDecode(const GMPAudioCodec& aCodecSettings,
|
||||
GMPAudioDecoderCallback* aCallback) override;
|
||||
|
||||
virtual void Decode(GMPAudioSamples* aEncodedSamples);
|
||||
|
||||
virtual void Reset() override;
|
||||
|
||||
virtual void Drain() override;
|
||||
|
||||
virtual void DecodingComplete() override;
|
||||
|
||||
bool HasShutdown() { return mHasShutdown; }
|
||||
|
||||
private:
|
||||
virtual ~AudioDecoder();
|
||||
|
||||
void EnsureWorker();
|
||||
|
||||
void DecodeTask(GMPAudioSamples* aEncodedSamples);
|
||||
void DrainTask();
|
||||
|
||||
void ReturnOutput(IMFSample* aSample);
|
||||
|
||||
HRESULT MFToGMPSample(IMFSample* aSample,
|
||||
GMPAudioSamples* aAudioFrame);
|
||||
|
||||
void MaybeRunOnMainThread(GMPTask* aTask);
|
||||
|
||||
GMPAudioHost *mHostAPI; // host-owned, invalid at DecodingComplete
|
||||
GMPAudioDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
|
||||
GMPThread* mWorkerThread;
|
||||
GMPMutex* mMutex;
|
||||
wmf::AutoPtr<wmf::WMFAACDecoder> mDecoder;
|
||||
|
||||
int32_t mNumInputTasks;
|
||||
|
||||
bool mHasShutdown;
|
||||
};
|
||||
|
||||
#endif // __AudioDecoder_h__
|
||||
68
media/gmp-clearkey/0.1/BigEndian.h
Normal file
68
media/gmp-clearkey/0.1/BigEndian.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 __BigEndian_h__
|
||||
#define __BigEndian_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class BigEndian {
|
||||
public:
|
||||
|
||||
static uint32_t readUint32(const void* aPtr) {
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(aPtr);
|
||||
return uint32_t(p[0]) << 24 |
|
||||
uint32_t(p[1]) << 16 |
|
||||
uint32_t(p[2]) << 8 |
|
||||
uint32_t(p[3]);
|
||||
}
|
||||
|
||||
static uint16_t readUint16(const void* aPtr) {
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(aPtr);
|
||||
return uint32_t(p[0]) << 8 |
|
||||
uint32_t(p[1]);
|
||||
}
|
||||
|
||||
static uint64_t readUint64(const void* aPtr) {
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(aPtr);
|
||||
return uint64_t(p[0]) << 56 |
|
||||
uint64_t(p[1]) << 48 |
|
||||
uint64_t(p[2]) << 40 |
|
||||
uint64_t(p[3]) << 32 |
|
||||
uint64_t(p[4]) << 24 |
|
||||
uint64_t(p[5]) << 16 |
|
||||
uint64_t(p[6]) << 8 |
|
||||
uint64_t(p[7]);
|
||||
}
|
||||
|
||||
static void writeUint64(void* aPtr, uint64_t aValue) {
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(aPtr);
|
||||
p[0] = uint8_t(aValue >> 56) & 0xff;
|
||||
p[1] = uint8_t(aValue >> 48) & 0xff;
|
||||
p[2] = uint8_t(aValue >> 40) & 0xff;
|
||||
p[3] = uint8_t(aValue >> 32) & 0xff;
|
||||
p[4] = uint8_t(aValue >> 24) & 0xff;
|
||||
p[5] = uint8_t(aValue >> 16) & 0xff;
|
||||
p[6] = uint8_t(aValue >> 8) & 0xff;
|
||||
p[7] = uint8_t(aValue) & 0xff;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // __BigEndian_h__
|
||||
45
media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.cpp
Normal file
45
media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.cpp
Normal file
|
|
@ -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));
|
||||
}
|
||||
37
media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h
Normal file
37
media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h
Normal file
|
|
@ -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__
|
||||
99
media/gmp-clearkey/0.1/ClearKeyBase64.cpp
Normal file
99
media/gmp-clearkey/0.1/ClearKeyBase64.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 "ClearKeyBase64.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* Take a base64-encoded string, convert (in-place) each character to its
|
||||
* corresponding value in the [0x00, 0x3f] range, and truncate any padding.
|
||||
*/
|
||||
static bool
|
||||
Decode6Bit(string& aStr)
|
||||
{
|
||||
for (size_t i = 0; i < aStr.length(); i++) {
|
||||
if (aStr[i] >= 'A' && aStr[i] <= 'Z') {
|
||||
aStr[i] -= 'A';
|
||||
}
|
||||
else if (aStr[i] >= 'a' && aStr[i] <= 'z') {
|
||||
aStr[i] -= 'a' - 26;
|
||||
}
|
||||
else if (aStr[i] >= '0' && aStr[i] <= '9') {
|
||||
aStr[i] -= '0' - 52;
|
||||
}
|
||||
else if (aStr[i] == '-' || aStr[i] == '+') {
|
||||
aStr[i] = 62;
|
||||
}
|
||||
else if (aStr[i] == '_' || aStr[i] == '/') {
|
||||
aStr[i] = 63;
|
||||
}
|
||||
else {
|
||||
// Truncate '=' padding at the end of the aString.
|
||||
if (aStr[i] != '=') {
|
||||
aStr.erase(i, string::npos);
|
||||
return false;
|
||||
}
|
||||
aStr[i] = '\0';
|
||||
aStr.resize(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DecodeBase64(const string& aEncoded, vector<uint8_t>& aOutDecoded)
|
||||
{
|
||||
if (aEncoded.empty()) {
|
||||
aOutDecoded.clear();
|
||||
return true;
|
||||
}
|
||||
if (aEncoded.size() == 1) {
|
||||
// Invalid Base64 encoding.
|
||||
return false;
|
||||
}
|
||||
string encoded = aEncoded;
|
||||
if (!Decode6Bit(encoded)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The number of bytes we haven't yet filled in the current byte, mod 8.
|
||||
int shift = 0;
|
||||
|
||||
aOutDecoded.resize((encoded.size() * 3) / 4);
|
||||
vector<uint8_t>::iterator out = aOutDecoded.begin();
|
||||
for (size_t i = 0; i < encoded.length(); i++) {
|
||||
if (!shift) {
|
||||
*out = encoded[i] << 2;
|
||||
}
|
||||
else {
|
||||
*out |= encoded[i] >> (6 - shift);
|
||||
out++;
|
||||
if (out == aOutDecoded.end()) {
|
||||
// Hit last 6bit octed in encoded, which is padding and can be ignored.
|
||||
break;
|
||||
}
|
||||
*out = encoded[i] << (shift + 2);
|
||||
}
|
||||
shift = (shift + 2) % 8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
28
media/gmp-clearkey/0.1/ClearKeyBase64.h
Normal file
28
media/gmp-clearkey/0.1/ClearKeyBase64.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 __ClearKeyBase64_h__
|
||||
#define __ClearKeyBase64_h__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
// Decodes a base64 encoded string. Returns true on success.
|
||||
bool
|
||||
DecodeBase64(const std::string& aEncoded, std::vector<uint8_t>& aOutDecoded);
|
||||
|
||||
#endif
|
||||
241
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
Normal file
241
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* 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 <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "ClearKeyDecryptionManager.h"
|
||||
#include "psshparser/PsshParser.h"
|
||||
#include "gmp-api/gmp-decryption.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include <assert.h>
|
||||
|
||||
class ClearKeyDecryptor : public RefCounted
|
||||
{
|
||||
public:
|
||||
ClearKeyDecryptor();
|
||||
|
||||
void InitKey(const Key& aKey);
|
||||
bool HasKey() const { return !!mKey.size(); }
|
||||
|
||||
GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
|
||||
const CryptoMetaData& aMetadata);
|
||||
|
||||
const Key& DecryptionKey() const { return mKey; }
|
||||
|
||||
private:
|
||||
~ClearKeyDecryptor();
|
||||
|
||||
Key mKey;
|
||||
};
|
||||
|
||||
|
||||
/* static */ ClearKeyDecryptionManager* ClearKeyDecryptionManager::sInstance = nullptr;
|
||||
|
||||
/* static */ ClearKeyDecryptionManager*
|
||||
ClearKeyDecryptionManager::Get()
|
||||
{
|
||||
if (!sInstance) {
|
||||
sInstance = new ClearKeyDecryptionManager();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
ClearKeyDecryptionManager::ClearKeyDecryptionManager()
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::ClearKeyDecryptionManager");
|
||||
}
|
||||
|
||||
ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::~ClearKeyDecryptionManager");
|
||||
|
||||
sInstance = nullptr;
|
||||
|
||||
for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
|
||||
it->second->Release();
|
||||
}
|
||||
mDecryptors.clear();
|
||||
}
|
||||
|
||||
bool
|
||||
ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::SeenKeyId %s", mDecryptors.find(aKeyId) != mDecryptors.end() ? "t" : "f");
|
||||
return mDecryptors.find(aKeyId) != mDecryptors.end();
|
||||
}
|
||||
|
||||
bool
|
||||
ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForId %08x...", *(uint32_t*)&aKeyId[0]);
|
||||
const auto& decryptor = mDecryptors.find(aKeyId);
|
||||
return decryptor != mDecryptors.end() && !decryptor->second->HasKey();
|
||||
}
|
||||
|
||||
bool
|
||||
ClearKeyDecryptionManager::HasKeyForKeyId(const KeyId& aKeyId) const
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::HasKeyForKeyId");
|
||||
const auto& decryptor = mDecryptors.find(aKeyId);
|
||||
return decryptor != mDecryptors.end() && decryptor->second->HasKey();
|
||||
}
|
||||
|
||||
const Key&
|
||||
ClearKeyDecryptionManager::GetDecryptionKey(const KeyId& aKeyId)
|
||||
{
|
||||
assert(HasKeyForKeyId(aKeyId));
|
||||
return mDecryptors[aKeyId]->DecryptionKey();
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey)
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::InitKey %08x...", *(uint32_t*)&aKeyId[0]);
|
||||
if (IsExpectingKeyForKeyId(aKeyId)) {
|
||||
mDecryptors[aKeyId]->InitKey(aKey);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId)
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId %08x...", *(uint32_t*)&aKeyId[0]);
|
||||
if (!HasSeenKeyId(aKeyId)) {
|
||||
mDecryptors[aKeyId] = new ClearKeyDecryptor();
|
||||
}
|
||||
mDecryptors[aKeyId]->AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId)
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::ReleaseKeyId");
|
||||
assert(HasSeenKeyId(aKeyId));
|
||||
|
||||
ClearKeyDecryptor* decryptor = mDecryptors[aKeyId];
|
||||
if (!decryptor->Release()) {
|
||||
mDecryptors.erase(aKeyId);
|
||||
}
|
||||
}
|
||||
|
||||
GMPErr
|
||||
ClearKeyDecryptionManager::Decrypt(std::vector<uint8_t>& aBuffer,
|
||||
const CryptoMetaData& aMetadata)
|
||||
{
|
||||
return Decrypt(&aBuffer[0], aBuffer.size(), aMetadata);
|
||||
}
|
||||
|
||||
GMPErr
|
||||
ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
|
||||
const CryptoMetaData& aMetadata)
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptionManager::Decrypt");
|
||||
if (!HasKeyForKeyId(aMetadata.mKeyId)) {
|
||||
return GMPNoKeyErr;
|
||||
}
|
||||
|
||||
return mDecryptors[aMetadata.mKeyId]->Decrypt(aBuffer, aBufferSize, aMetadata);
|
||||
}
|
||||
|
||||
ClearKeyDecryptor::ClearKeyDecryptor()
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptor ctor");
|
||||
}
|
||||
|
||||
ClearKeyDecryptor::~ClearKeyDecryptor()
|
||||
{
|
||||
if (HasKey()) {
|
||||
CK_LOGD("ClearKeyDecryptor dtor; key = %08x...", *(uint32_t*)&mKey[0]);
|
||||
} else {
|
||||
CK_LOGD("ClearKeyDecryptor dtor");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeyDecryptor::InitKey(const Key& aKey)
|
||||
{
|
||||
mKey = aKey;
|
||||
}
|
||||
|
||||
GMPErr
|
||||
ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
|
||||
const CryptoMetaData& aMetadata)
|
||||
{
|
||||
CK_LOGD("ClearKeyDecryptor::Decrypt");
|
||||
// If the sample is split up into multiple encrypted subsamples, we need to
|
||||
// stitch them into one continuous buffer for decryption.
|
||||
std::vector<uint8_t> tmp(aBufferSize);
|
||||
|
||||
if (aMetadata.NumSubsamples()) {
|
||||
// Take all encrypted parts of subsamples and stitch them into one
|
||||
// continuous encrypted buffer.
|
||||
static_assert(sizeof(uintptr_t) == sizeof(uint8_t*),
|
||||
"We need uintptr_t to be exactly the same size as a pointer");
|
||||
mozilla::CheckedInt<uintptr_t> data = reinterpret_cast<uintptr_t>(aBuffer);
|
||||
const uintptr_t endBuffer =
|
||||
reinterpret_cast<uintptr_t>(aBuffer + aBufferSize);
|
||||
uint8_t* iter = &tmp[0];
|
||||
for (size_t i = 0; i < aMetadata.NumSubsamples(); i++) {
|
||||
data += aMetadata.mClearBytes[i];
|
||||
if (!data.isValid() || data.value() > endBuffer) {
|
||||
// Trying to read past the end of the buffer!
|
||||
return GMPCryptoErr;
|
||||
}
|
||||
const uint32_t& cipherBytes = aMetadata.mCipherBytes[i];
|
||||
mozilla::CheckedInt<uintptr_t> dataAfterCipher = data + cipherBytes;
|
||||
if (!dataAfterCipher.isValid() || dataAfterCipher.value() > endBuffer) {
|
||||
// Trying to read past the end of the buffer!
|
||||
return GMPCryptoErr;
|
||||
}
|
||||
|
||||
memcpy(iter, reinterpret_cast<uint8_t*>(data.value()), cipherBytes);
|
||||
|
||||
data = dataAfterCipher;
|
||||
iter += cipherBytes;
|
||||
}
|
||||
|
||||
tmp.resize((size_t)(iter - &tmp[0]));
|
||||
} else {
|
||||
memcpy(&tmp[0], aBuffer, aBufferSize);
|
||||
}
|
||||
|
||||
assert(aMetadata.mIV.size() == 8 || aMetadata.mIV.size() == 16);
|
||||
std::vector<uint8_t> iv(aMetadata.mIV);
|
||||
iv.insert(iv.end(), CENC_KEY_LEN - aMetadata.mIV.size(), 0);
|
||||
|
||||
ClearKeyUtils::DecryptAES(mKey, tmp, iv);
|
||||
|
||||
if (aMetadata.NumSubsamples()) {
|
||||
// Take the decrypted buffer, split up into subsamples, and insert those
|
||||
// subsamples back into their original position in the original buffer.
|
||||
uint8_t* data = aBuffer;
|
||||
uint8_t* iter = &tmp[0];
|
||||
for (size_t i = 0; i < aMetadata.NumSubsamples(); i++) {
|
||||
data += aMetadata.mClearBytes[i];
|
||||
uint32_t cipherBytes = aMetadata.mCipherBytes[i];
|
||||
|
||||
memcpy(data, iter, cipherBytes);
|
||||
|
||||
data += cipherBytes;
|
||||
iter += cipherBytes;
|
||||
}
|
||||
} else {
|
||||
memcpy(aBuffer, &tmp[0], aBufferSize);
|
||||
}
|
||||
|
||||
return GMPNoErr;
|
||||
}
|
||||
101
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
Normal file
101
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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 __ClearKeyDecryptionManager_h__
|
||||
#define __ClearKeyDecryptionManager_h__
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "RefCounted.h"
|
||||
|
||||
class ClearKeyDecryptor;
|
||||
|
||||
class CryptoMetaData {
|
||||
public:
|
||||
CryptoMetaData() {}
|
||||
|
||||
explicit CryptoMetaData(const GMPEncryptedBufferMetadata* aCrypto)
|
||||
{
|
||||
Init(aCrypto);
|
||||
}
|
||||
|
||||
void Init(const GMPEncryptedBufferMetadata* aCrypto)
|
||||
{
|
||||
if (!aCrypto) {
|
||||
assert(!IsValid());
|
||||
return;
|
||||
}
|
||||
Assign(mKeyId, aCrypto->KeyId(), aCrypto->KeyIdSize());
|
||||
Assign(mIV, aCrypto->IV(), aCrypto->IVSize());
|
||||
Assign(mClearBytes, aCrypto->ClearBytes(), aCrypto->NumSubsamples());
|
||||
Assign(mCipherBytes, aCrypto->CipherBytes(), aCrypto->NumSubsamples());
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return !mKeyId.empty() &&
|
||||
!mIV.empty() &&
|
||||
!mCipherBytes.empty() &&
|
||||
!mClearBytes.empty();
|
||||
}
|
||||
|
||||
size_t NumSubsamples() const {
|
||||
assert(mClearBytes.size() == mCipherBytes.size());
|
||||
return mClearBytes.size();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> mKeyId;
|
||||
std::vector<uint8_t> mIV;
|
||||
std::vector<uint16_t> mClearBytes;
|
||||
std::vector<uint32_t> mCipherBytes;
|
||||
};
|
||||
|
||||
class ClearKeyDecryptionManager : public RefCounted
|
||||
{
|
||||
private:
|
||||
ClearKeyDecryptionManager();
|
||||
~ClearKeyDecryptionManager();
|
||||
|
||||
static ClearKeyDecryptionManager* sInstance;
|
||||
|
||||
public:
|
||||
static ClearKeyDecryptionManager* Get();
|
||||
|
||||
bool HasSeenKeyId(const KeyId& aKeyId) const;
|
||||
bool HasKeyForKeyId(const KeyId& aKeyId) const;
|
||||
|
||||
const Key& GetDecryptionKey(const KeyId& aKeyId);
|
||||
|
||||
// Create a decryptor for the given KeyId if one does not already exist.
|
||||
void InitKey(KeyId aKeyId, Key aKey);
|
||||
void ExpectKeyId(KeyId aKeyId);
|
||||
void ReleaseKeyId(KeyId aKeyId);
|
||||
|
||||
// Decrypts buffer *in place*.
|
||||
GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
|
||||
const CryptoMetaData& aMetadata);
|
||||
GMPErr Decrypt(std::vector<uint8_t>& aBuffer,
|
||||
const CryptoMetaData& aMetadata);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const;
|
||||
|
||||
std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
|
||||
};
|
||||
|
||||
#endif // __ClearKeyDecryptionManager_h__
|
||||
260
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
Normal file
260
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* 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 "ClearKeyPersistence.h"
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "ClearKeyStorage.h"
|
||||
#include "ClearKeySessionManager.h"
|
||||
#include "RefCounted.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Whether we've loaded the persistent session ids from GMPStorage yet.
|
||||
enum PersistentKeyState {
|
||||
UNINITIALIZED,
|
||||
LOADING,
|
||||
LOADED
|
||||
};
|
||||
static PersistentKeyState sPersistentKeyState = UNINITIALIZED;
|
||||
|
||||
// Set of session Ids of the persistent sessions created or residing in
|
||||
// storage.
|
||||
static set<uint32_t> sPersistentSessionIds;
|
||||
|
||||
static vector<GMPTask*> sTasksBlockedOnSessionIdLoad;
|
||||
|
||||
static void
|
||||
ReadAllRecordsFromIterator(GMPRecordIterator* aRecordIterator,
|
||||
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();
|
||||
}
|
||||
}
|
||||
sPersistentKeyState = LOADED;
|
||||
aRecordIterator->Close();
|
||||
|
||||
for (size_t i = 0; i < sTasksBlockedOnSessionIdLoad.size(); i++) {
|
||||
sTasksBlockedOnSessionIdLoad[i]->Run();
|
||||
sTasksBlockedOnSessionIdLoad[i]->Destroy();
|
||||
}
|
||||
sTasksBlockedOnSessionIdLoad.clear();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ClearKeyPersistence::EnsureInitialized()
|
||||
{
|
||||
if (sPersistentKeyState == UNINITIALIZED) {
|
||||
sPersistentKeyState = LOADING;
|
||||
if (GMP_FAILED(EnumRecordNames(&ReadAllRecordsFromIterator))) {
|
||||
sPersistentKeyState = LOADED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ string
|
||||
ClearKeyPersistence::GetNewSessionId(GMPSessionType aSessionType)
|
||||
{
|
||||
static uint32_t sNextSessionId = 1;
|
||||
|
||||
// Ensure we don't re-use a session id that was persisted.
|
||||
while (Contains(sPersistentSessionIds, sNextSessionId)) {
|
||||
sNextSessionId++;
|
||||
}
|
||||
|
||||
string sessionId;
|
||||
stringstream ss;
|
||||
ss << sNextSessionId;
|
||||
ss >> sessionId;
|
||||
|
||||
if (aSessionType == kGMPPersistentSession) {
|
||||
sPersistentSessionIds.insert(sNextSessionId);
|
||||
}
|
||||
|
||||
sNextSessionId++;
|
||||
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
|
||||
}
|
||||
|
||||
class LoadSessionFromKeysTask : public ReadContinuation {
|
||||
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)
|
||||
{
|
||||
LoadSessionFromKeysTask* loadTask =
|
||||
new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
|
||||
ReadData(aSid, loadTask);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ClearKeyPersistence::PersistentSessionRemoved(const string& aSessionId)
|
||||
{
|
||||
sPersistentSessionIds.erase(atoi(aSessionId.c_str()));
|
||||
}
|
||||
53
media/gmp-clearkey/0.1/ClearKeyPersistence.h
Normal file
53
media/gmp-clearkey/0.1/ClearKeyPersistence.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 __ClearKeyPersistence_h__
|
||||
#define __ClearKeyPersistence_h__
|
||||
|
||||
#include <string>
|
||||
#include "gmp-api/gmp-decryption.h"
|
||||
|
||||
class ClearKeySessionManager;
|
||||
|
||||
class ClearKeyPersistence {
|
||||
public:
|
||||
static void EnsureInitialized();
|
||||
|
||||
static std::string GetNewSessionId(GMPSessionType aSessionType);
|
||||
|
||||
static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
|
||||
uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const std::string& aInitDataType,
|
||||
const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize,
|
||||
GMPSessionType aSessionType);
|
||||
|
||||
static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
|
||||
uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength);
|
||||
|
||||
static bool IsPersistentSessionId(const std::string& aSid);
|
||||
|
||||
static void LoadSessionData(ClearKeySessionManager* aInstance,
|
||||
const std::string& aSid,
|
||||
uint32_t aPromiseId);
|
||||
|
||||
static void PersistentSessionRemoved(const std::string& aSid);
|
||||
};
|
||||
|
||||
#endif // __ClearKeyPersistence_h__
|
||||
94
media/gmp-clearkey/0.1/ClearKeySession.cpp
Normal file
94
media/gmp-clearkey/0.1/ClearKeySession.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 "BigEndian.h"
|
||||
#include "ClearKeyDecryptionManager.h"
|
||||
#include "ClearKeySession.h"
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "ClearKeyStorage.h"
|
||||
#include "psshparser/PsshParser.h"
|
||||
#include "gmp-task-utils.h"
|
||||
#include "gmp-api/gmp-decryption.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
ClearKeySession::ClearKeySession(const std::string& aSessionId,
|
||||
GMPDecryptorCallback* aCallback,
|
||||
GMPSessionType aSessionType)
|
||||
: mSessionId(aSessionId)
|
||||
, mCallback(aCallback)
|
||||
, mSessionType(aSessionType)
|
||||
{
|
||||
CK_LOGD("ClearKeySession ctor %p", this);
|
||||
}
|
||||
|
||||
ClearKeySession::~ClearKeySession()
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySession::Init(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const std::string& aInitDataType,
|
||||
const uint8_t* aInitData, uint32_t aInitDataSize)
|
||||
{
|
||||
CK_LOGD("ClearKeySession::Init");
|
||||
|
||||
if (aInitDataType == "cenc") {
|
||||
ParseCENCInitData(aInitData, aInitDataSize, mKeyIds);
|
||||
} else if (aInitDataType == "keyids") {
|
||||
ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds);
|
||||
} else if (aInitDataType == "webm" && aInitDataSize <= kMaxWebmInitDataSize) {
|
||||
// "webm" initData format is simply the raw bytes of the keyId.
|
||||
vector<uint8_t> keyId;
|
||||
keyId.assign(aInitData, aInitData+aInitDataSize);
|
||||
mKeyIds.push_back(keyId);
|
||||
}
|
||||
|
||||
if (!mKeyIds.size()) {
|
||||
const char message[] = "Couldn't parse init data";
|
||||
mCallback->RejectPromise(aPromiseId, kGMPTypeError, message, strlen(message));
|
||||
return;
|
||||
}
|
||||
|
||||
mCallback->SetSessionId(aCreateSessionToken, &mSessionId[0], mSessionId.length());
|
||||
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
}
|
||||
|
||||
GMPSessionType
|
||||
ClearKeySession::Type() const
|
||||
{
|
||||
return mSessionType;
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySession::AddKeyId(const KeyId& aKeyId)
|
||||
{
|
||||
mKeyIds.push_back(aKeyId);
|
||||
}
|
||||
58
media/gmp-clearkey/0.1/ClearKeySession.h
Normal file
58
media/gmp-clearkey/0.1/ClearKeySession.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 __ClearKeySession_h__
|
||||
#define __ClearKeySession_h__
|
||||
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "gmp-api/gmp-decryption.h"
|
||||
|
||||
class GMPBuffer;
|
||||
class GMPDecryptorCallback;
|
||||
class GMPDecryptorHost;
|
||||
class GMPEncryptedBufferMetadata;
|
||||
|
||||
class ClearKeySession
|
||||
{
|
||||
public:
|
||||
explicit ClearKeySession(const std::string& aSessionId,
|
||||
GMPDecryptorCallback* aCallback,
|
||||
GMPSessionType aSessionType);
|
||||
|
||||
~ClearKeySession();
|
||||
|
||||
const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; }
|
||||
|
||||
void Init(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const string& aInitDataType,
|
||||
const uint8_t* aInitData, uint32_t aInitDataSize);
|
||||
|
||||
GMPSessionType Type() const;
|
||||
|
||||
void AddKeyId(const KeyId& aKeyId);
|
||||
|
||||
const std::string& Id() const { return mSessionId; }
|
||||
|
||||
private:
|
||||
const std::string mSessionId;
|
||||
std::vector<KeyId> mKeyIds;
|
||||
|
||||
GMPDecryptorCallback* mCallback;
|
||||
const GMPSessionType mSessionType;
|
||||
};
|
||||
|
||||
#endif // __ClearKeySession_h__
|
||||
418
media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
Normal file
418
media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ClearKeyDecryptionManager.h"
|
||||
#include "ClearKeySessionManager.h"
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "ClearKeyStorage.h"
|
||||
#include "ClearKeyPersistence.h"
|
||||
#include "gmp-task-utils.h"
|
||||
#include <assert.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ClearKeySessionManager::ClearKeySessionManager()
|
||||
: mDecryptionManager(ClearKeyDecryptionManager::Get())
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager ctor %p", this);
|
||||
AddRef();
|
||||
|
||||
if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
|
||||
CK_LOGD("failed to create thread in clearkey cdm");
|
||||
mThread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ClearKeySessionManager::~ClearKeySessionManager()
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager dtor %p", this);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback,
|
||||
bool aDistinctiveIdentifierAllowed,
|
||||
bool aPersistentStateAllowed)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::Init");
|
||||
mCallback = aCallback;
|
||||
ClearKeyPersistence::EnsureInitialized();
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const char* aInitDataType,
|
||||
uint32_t aInitDataTypeSize,
|
||||
const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize,
|
||||
GMPSessionType aSessionType)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
|
||||
|
||||
string initDataType(aInitDataType, aInitDataType + aInitDataTypeSize);
|
||||
// initDataType must be "cenc", "keyids", or "webm".
|
||||
if (initDataType != "cenc" &&
|
||||
initDataType != "keyids" &&
|
||||
initDataType != "webm") {
|
||||
string message = "'" + initDataType + "' is an initDataType unsupported by ClearKey";
|
||||
mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
|
||||
message.c_str(), message.size());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
|
||||
aCreateSessionToken,
|
||||
aPromiseId,
|
||||
initDataType,
|
||||
aInitData,
|
||||
aInitDataSize,
|
||||
aSessionType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
|
||||
assert(mSessions.find(sessionId) == mSessions.end());
|
||||
|
||||
ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
|
||||
session->Init(aCreateSessionToken, aPromiseId, initDataType, aInitData, aInitDataSize);
|
||||
mSessions[sessionId] = session;
|
||||
|
||||
const vector<KeyId>& sessionKeys = session->GetKeyIds();
|
||||
vector<KeyId> neededKeys;
|
||||
for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
|
||||
// Need to request this key ID from the client. We always send a key
|
||||
// request, whether or not another session has sent a request with the same
|
||||
// key ID. Otherwise a script can end up waiting for another script to
|
||||
// respond to the request (which may not necessarily happen).
|
||||
neededKeys.push_back(*it);
|
||||
mDecryptionManager->ExpectKeyId(*it);
|
||||
}
|
||||
|
||||
if (neededKeys.empty()) {
|
||||
CK_LOGD("No keys needed from client.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a request for needed key data.
|
||||
string request;
|
||||
ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
|
||||
mCallback->SessionMessage(&sessionId[0], sessionId.length(),
|
||||
kGMPLicenseRequest,
|
||||
(uint8_t*)&request[0], request.length());
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::LoadSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::LoadSession");
|
||||
|
||||
if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
|
||||
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
|
||||
aPromiseId,
|
||||
aSessionId,
|
||||
aSessionIdLength)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string sid(aSessionId, aSessionId + aSessionIdLength);
|
||||
if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
|
||||
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Callsback PersistentSessionDataLoaded with results...
|
||||
ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus,
|
||||
uint32_t aPromiseId,
|
||||
const string& aSessionId,
|
||||
const uint8_t* aKeyData,
|
||||
uint32_t aKeyDataSize)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded");
|
||||
if (GMP_FAILED(aStatus) ||
|
||||
Contains(mSessions, aSessionId) ||
|
||||
(aKeyDataSize % (2 * CENC_KEY_LEN)) != 0) {
|
||||
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearKeySession* session = new ClearKeySession(aSessionId,
|
||||
mCallback,
|
||||
kGMPPersistentSession);
|
||||
mSessions[aSessionId] = session;
|
||||
|
||||
uint32_t numKeys = aKeyDataSize / (2 * CENC_KEY_LEN);
|
||||
|
||||
vector<GMPMediaKeyInfo> key_infos;
|
||||
vector<KeyIdPair> keyPairs;
|
||||
for (uint32_t i = 0; i < numKeys; i ++) {
|
||||
const uint8_t* base = aKeyData + 2 * CENC_KEY_LEN * i;
|
||||
|
||||
KeyIdPair keyPair;
|
||||
|
||||
keyPair.mKeyId = KeyId(base, base + CENC_KEY_LEN);
|
||||
assert(keyPair.mKeyId.size() == CENC_KEY_LEN);
|
||||
|
||||
keyPair.mKey = Key(base + CENC_KEY_LEN, base + 2 * CENC_KEY_LEN);
|
||||
assert(keyPair.mKey.size() == CENC_KEY_LEN);
|
||||
|
||||
session->AddKeyId(keyPair.mKeyId);
|
||||
|
||||
mDecryptionManager->ExpectKeyId(keyPair.mKeyId);
|
||||
mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
|
||||
mKeyIds.insert(keyPair.mKey);
|
||||
|
||||
keyPairs.push_back(keyPair);
|
||||
key_infos.push_back(GMPMediaKeyInfo(&keyPairs[i].mKeyId[0],
|
||||
keyPairs[i].mKeyId.size(),
|
||||
kGMPUsable));
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(&aSessionId[0], aSessionId.size(),
|
||||
key_infos.data(), key_infos.size());
|
||||
|
||||
mCallback->ResolveLoadSessionPromise(aPromiseId, true);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const uint8_t* aResponse,
|
||||
uint32_t aResponseSize)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::UpdateSession");
|
||||
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
||||
|
||||
auto itr = mSessions.find(sessionId);
|
||||
if (itr == mSessions.end() || !(itr->second)) {
|
||||
CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
|
||||
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
ClearKeySession* session = itr->second;
|
||||
|
||||
// Verify the size of session response.
|
||||
if (aResponseSize >= kMaxSessionResponseLength) {
|
||||
CK_LOGW("Session response size is not within a reasonable size.");
|
||||
mCallback->RejectPromise(aPromiseId, kGMPTypeError, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the response for any (key ID, key) pairs.
|
||||
vector<KeyIdPair> keyPairs;
|
||||
if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
|
||||
CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
|
||||
mCallback->RejectPromise(aPromiseId, kGMPTypeError, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
vector<GMPMediaKeyInfo> key_infos;
|
||||
for (size_t i = 0; i < keyPairs.size(); i++) {
|
||||
KeyIdPair& keyPair = keyPairs[i];
|
||||
mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
|
||||
mKeyIds.insert(keyPair.mKeyId);
|
||||
key_infos.push_back(GMPMediaKeyInfo(&keyPair.mKeyId[0],
|
||||
keyPair.mKeyId.size(),
|
||||
kGMPUsable));
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(aSessionId, aSessionIdLength,
|
||||
key_infos.data(), key_infos.size());
|
||||
|
||||
if (session->Type() != kGMPPersistentSession) {
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the keys on disk. We store a record whose name is the sessionId,
|
||||
// and simply append each keyId followed by its key.
|
||||
vector<uint8_t> keydata;
|
||||
Serialize(session, keydata);
|
||||
GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
|
||||
static const char* message = "Couldn't store cenc key init data";
|
||||
GMPTask* reject = WrapTask(mCallback,
|
||||
&GMPDecryptorCallback::RejectPromise,
|
||||
aPromiseId,
|
||||
kGMPInvalidStateError,
|
||||
message,
|
||||
strlen(message));
|
||||
StoreData(sessionId, keydata, resolve, reject);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::Serialize(const ClearKeySession* aSession,
|
||||
std::vector<uint8_t>& aOutKeyData)
|
||||
{
|
||||
const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
|
||||
for (size_t i = 0; i < keyIds.size(); i++) {
|
||||
const KeyId& keyId = keyIds[i];
|
||||
if (!mDecryptionManager->HasKeyForKeyId(keyId)) {
|
||||
continue;
|
||||
}
|
||||
assert(keyId.size() == CENC_KEY_LEN);
|
||||
aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end());
|
||||
const Key& key = mDecryptionManager->GetDecryptionKey(keyId);
|
||||
assert(key.size() == CENC_KEY_LEN);
|
||||
aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::CloseSession");
|
||||
|
||||
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
||||
auto itr = mSessions.find(sessionId);
|
||||
if (itr == mSessions.end()) {
|
||||
CK_LOGW("ClearKey CDM couldn't close non-existent session.");
|
||||
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearKeySession* session = itr->second;
|
||||
assert(session);
|
||||
|
||||
ClearInMemorySessionData(session);
|
||||
mCallback->SessionClosed(aSessionId, aSessionIdLength);
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::ClearInMemorySessionData(ClearKeySession* aSession)
|
||||
{
|
||||
mSessions.erase(aSession->Id());
|
||||
delete aSession;
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::RemoveSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::RemoveSession");
|
||||
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
||||
auto itr = mSessions.find(sessionId);
|
||||
if (itr == mSessions.end()) {
|
||||
CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
|
||||
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearKeySession* session = itr->second;
|
||||
assert(session);
|
||||
string sid = session->Id();
|
||||
bool isPersistent = session->Type() == kGMPPersistentSession;
|
||||
ClearInMemorySessionData(session);
|
||||
|
||||
if (!isPersistent) {
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearKeyPersistence::PersistentSessionRemoved(sid);
|
||||
|
||||
// Overwrite the record storing the sessionId's key data with a zero
|
||||
// length record to delete it.
|
||||
vector<uint8_t> emptyKeydata;
|
||||
GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
|
||||
static const char* message = "Could not remove session";
|
||||
GMPTask* reject = WrapTask(mCallback,
|
||||
&GMPDecryptorCallback::RejectPromise,
|
||||
aPromiseId,
|
||||
kGMPInvalidAccessError,
|
||||
message,
|
||||
strlen(message));
|
||||
StoreData(sessionId, emptyKeydata, resolve, reject);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId,
|
||||
const uint8_t* aServerCert,
|
||||
uint32_t aServerCertSize)
|
||||
{
|
||||
// ClearKey CDM doesn't support this method by spec.
|
||||
CK_LOGD("ClearKeySessionManager::SetServerCertificate");
|
||||
mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
|
||||
nullptr /* message */, 0 /* messageLen */);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::Decrypt(GMPBuffer* aBuffer,
|
||||
GMPEncryptedBufferMetadata* aMetadata)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::Decrypt");
|
||||
|
||||
if (!mThread) {
|
||||
CK_LOGW("No decrypt thread");
|
||||
mCallback->Decrypted(aBuffer, GMPGenericErr);
|
||||
return;
|
||||
}
|
||||
|
||||
mThread->Post(WrapTaskRefCounted(this,
|
||||
&ClearKeySessionManager::DoDecrypt,
|
||||
aBuffer, aMetadata));
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::DoDecrypt(GMPBuffer* aBuffer,
|
||||
GMPEncryptedBufferMetadata* aMetadata)
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::DoDecrypt");
|
||||
|
||||
GMPErr rv = mDecryptionManager->Decrypt(aBuffer->Data(), aBuffer->Size(),
|
||||
CryptoMetaData(aMetadata));
|
||||
CK_LOGD("DeDecrypt finished with code %x\n", rv);
|
||||
mCallback->Decrypted(aBuffer, rv);
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::Shutdown()
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::Shutdown %p", this);
|
||||
|
||||
for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
|
||||
delete it->second;
|
||||
}
|
||||
mSessions.clear();
|
||||
}
|
||||
|
||||
void
|
||||
ClearKeySessionManager::DecryptingComplete()
|
||||
{
|
||||
CK_LOGD("ClearKeySessionManager::DecryptingComplete %p", this);
|
||||
|
||||
GMPThread* thread = mThread;
|
||||
thread->Join();
|
||||
|
||||
Shutdown();
|
||||
mDecryptionManager = nullptr;
|
||||
Release();
|
||||
}
|
||||
100
media/gmp-clearkey/0.1/ClearKeySessionManager.h
Normal file
100
media/gmp-clearkey/0.1/ClearKeySessionManager.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 __ClearKeyDecryptor_h__
|
||||
#define __ClearKeyDecryptor_h__
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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:
|
||||
ClearKeySessionManager();
|
||||
|
||||
virtual void Init(GMPDecryptorCallback* aCallback,
|
||||
bool aDistinctiveIdentifierAllowed,
|
||||
bool aPersistentStateAllowed) override;
|
||||
|
||||
virtual void CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const char* aInitDataType,
|
||||
uint32_t aInitDataTypeSize,
|
||||
const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize,
|
||||
GMPSessionType aSessionType) override;
|
||||
|
||||
virtual void LoadSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override;
|
||||
|
||||
virtual void UpdateSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const uint8_t* aResponse,
|
||||
uint32_t aResponseSize) override;
|
||||
|
||||
virtual void CloseSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override;
|
||||
|
||||
virtual void RemoveSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override;
|
||||
|
||||
virtual void SetServerCertificate(uint32_t aPromiseId,
|
||||
const uint8_t* aServerCert,
|
||||
uint32_t aServerCertSize) override;
|
||||
|
||||
virtual void Decrypt(GMPBuffer* aBuffer,
|
||||
GMPEncryptedBufferMetadata* aMetadata) override;
|
||||
|
||||
virtual void DecryptingComplete() override;
|
||||
|
||||
void PersistentSessionDataLoaded(GMPErr aStatus,
|
||||
uint32_t aPromiseId,
|
||||
const std::string& aSessionId,
|
||||
const uint8_t* aKeyData,
|
||||
uint32_t aKeyDataSize);
|
||||
|
||||
private:
|
||||
~ClearKeySessionManager();
|
||||
|
||||
void DoDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
|
||||
void Shutdown();
|
||||
|
||||
void ClearInMemorySessionData(ClearKeySession* aSession);
|
||||
void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
|
||||
|
||||
RefPtr<ClearKeyDecryptionManager> mDecryptionManager;
|
||||
|
||||
GMPDecryptorCallback* mCallback;
|
||||
GMPThread* mThread;
|
||||
|
||||
std::set<KeyId> mKeyIds;
|
||||
std::map<std::string, ClearKeySession*> mSessions;
|
||||
};
|
||||
|
||||
#endif // __ClearKeyDecryptor_h__
|
||||
194
media/gmp-clearkey/0.1/ClearKeyStorage.cpp
Normal file
194
media/gmp-clearkey/0.1/ClearKeyStorage.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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 "ClearKeyStorage.h"
|
||||
#include "ClearKeyUtils.h"
|
||||
|
||||
#include "gmp-task-utils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include "ArrayUtils.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
static GMPErr
|
||||
RunOnMainThread(GMPTask* aTask)
|
||||
{
|
||||
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:
|
||||
/*
|
||||
* This function will take the memory ownership of the parameters and
|
||||
* delete them when done.
|
||||
*/
|
||||
static void Write(const std::string& aRecordName,
|
||||
const std::vector<uint8_t>& aData,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure) {
|
||||
(new WriteRecordClient(aData, aOnSuccess, aOnFailure))->Do(aRecordName);
|
||||
}
|
||||
|
||||
virtual void OpenComplete(GMPErr aStatus) override {
|
||||
if (GMP_FAILED(aStatus) ||
|
||||
GMP_FAILED(mRecord->Write(&mData.front(), mData.size()))) {
|
||||
Done(mOnFailure, mOnSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override {
|
||||
assert(false); // Should not reach here.
|
||||
}
|
||||
|
||||
virtual void WriteComplete(GMPErr aStatus) override {
|
||||
if (GMP_FAILED(aStatus)) {
|
||||
Done(mOnFailure, mOnSuccess);
|
||||
} else {
|
||||
Done(mOnSuccess, mOnFailure);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WriteRecordClient(const std::vector<uint8_t>& aData,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure)
|
||||
: mRecord(nullptr)
|
||||
, mOnSuccess(aOnSuccess)
|
||||
, mOnFailure(aOnFailure)
|
||||
, mData(aData) {}
|
||||
|
||||
void Do(const std::string& aName) {
|
||||
auto err = OpenRecord(aName.c_str(), aName.size(), &mRecord, this);
|
||||
if (GMP_FAILED(err) ||
|
||||
GMP_FAILED(mRecord->Open())) {
|
||||
Done(mOnFailure, mOnSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
void Done(GMPTask* aToRun, GMPTask* aToDestroy) {
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
if (mRecord) {
|
||||
mRecord->Close();
|
||||
}
|
||||
aToDestroy->Destroy();
|
||||
RunOnMainThread(aToRun);
|
||||
delete this;
|
||||
}
|
||||
|
||||
GMPRecord* mRecord;
|
||||
GMPTask* mOnSuccess;
|
||||
GMPTask* mOnFailure;
|
||||
const std::vector<uint8_t> mData;
|
||||
};
|
||||
|
||||
void
|
||||
StoreData(const std::string& aRecordName,
|
||||
const std::vector<uint8_t>& aData,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure)
|
||||
{
|
||||
WriteRecordClient::Write(aRecordName, aData, aOnSuccess, aOnFailure);
|
||||
}
|
||||
|
||||
class ReadRecordClient : public GMPRecordClient {
|
||||
public:
|
||||
/*
|
||||
* This function will take the memory ownership of the parameters and
|
||||
* delete them when done.
|
||||
*/
|
||||
static void Read(const std::string& aRecordName,
|
||||
ReadContinuation* aContinuation) {
|
||||
assert(aContinuation);
|
||||
(new ReadRecordClient(aContinuation))->Do(aRecordName);
|
||||
}
|
||||
|
||||
virtual void OpenComplete(GMPErr aStatus) override {
|
||||
auto err = aStatus;
|
||||
if (GMP_FAILED(err) ||
|
||||
GMP_FAILED(err = mRecord->Read())) {
|
||||
Done(err, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override {
|
||||
Done(aStatus, aData, aDataSize);
|
||||
}
|
||||
|
||||
virtual void WriteComplete(GMPErr aStatus) override {
|
||||
assert(false); // Should not reach here.
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ReadRecordClient(ReadContinuation* aContinuation)
|
||||
: mRecord(nullptr)
|
||||
, mContinuation(aContinuation) {}
|
||||
|
||||
void Do(const std::string& aName) {
|
||||
auto err = OpenRecord(aName.c_str(), aName.size(), &mRecord, this);
|
||||
if (GMP_FAILED(err) ||
|
||||
GMP_FAILED(err = mRecord->Open())) {
|
||||
Done(err, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Done(GMPErr err, const uint8_t* aData, uint32_t aDataSize) {
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
if (mRecord) {
|
||||
mRecord->Close();
|
||||
}
|
||||
mContinuation->ReadComplete(err, aData, aDataSize);
|
||||
delete mContinuation;
|
||||
delete this;
|
||||
}
|
||||
|
||||
GMPRecord* mRecord;
|
||||
ReadContinuation* mContinuation;
|
||||
};
|
||||
|
||||
void
|
||||
ReadData(const std::string& aRecordName,
|
||||
ReadContinuation* aContinuation)
|
||||
{
|
||||
ReadRecordClient::Read(aRecordName, aContinuation);
|
||||
}
|
||||
|
||||
GMPErr
|
||||
EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc)
|
||||
{
|
||||
return GetPlatform()->getrecordenumerator(aRecvIteratorFunc, nullptr);
|
||||
}
|
||||
48
media/gmp-clearkey/0.1/ClearKeyStorage.h
Normal file
48
media/gmp-clearkey/0.1/ClearKeyStorage.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 __ClearKeyStorage_h__
|
||||
#define __ClearKeyStorage_h__
|
||||
|
||||
#include "gmp-api/gmp-errors.h"
|
||||
#include "gmp-api/gmp-platform.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
class GMPTask;
|
||||
|
||||
// Responsible for ensuring that both aOnSuccess and aOnFailure are destroyed.
|
||||
void StoreData(const std::string& aRecordName,
|
||||
const std::vector<uint8_t>& aData,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure);
|
||||
|
||||
class ReadContinuation {
|
||||
public:
|
||||
virtual void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
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__
|
||||
541
media/gmp-clearkey/0.1/ClearKeyUtils.cpp
Normal file
541
media/gmp-clearkey/0.1/ClearKeyUtils.cpp
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
/*
|
||||
* 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 <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "ClearKeyBase64.h"
|
||||
#include "ArrayUtils.h"
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include "BigEndian.h"
|
||||
#include "openaes/oaes_lib.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void
|
||||
CK_Log(const char* aFmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, aFmt);
|
||||
vprintf(aFmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void
|
||||
IncrementIV(vector<uint8_t>& aIV) {
|
||||
using mozilla::BigEndian;
|
||||
|
||||
assert(aIV.size() == 16);
|
||||
BigEndian::writeUint64(&aIV[8], BigEndian::readUint64(&aIV[8]) + 1);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ClearKeyUtils::DecryptAES(const vector<uint8_t>& aKey,
|
||||
vector<uint8_t>& aData, vector<uint8_t>& aIV)
|
||||
{
|
||||
assert(aIV.size() == CENC_KEY_LEN);
|
||||
assert(aKey.size() == CENC_KEY_LEN);
|
||||
|
||||
OAES_CTX* aes = oaes_alloc();
|
||||
oaes_key_import_data(aes, &aKey[0], aKey.size());
|
||||
oaes_set_option(aes, OAES_OPTION_ECB, nullptr);
|
||||
|
||||
for (size_t i = 0; i < aData.size(); i += CENC_KEY_LEN) {
|
||||
size_t encLen;
|
||||
oaes_encrypt(aes, &aIV[0], CENC_KEY_LEN, nullptr, &encLen);
|
||||
|
||||
vector<uint8_t> enc(encLen);
|
||||
oaes_encrypt(aes, &aIV[0], CENC_KEY_LEN, &enc[0], &encLen);
|
||||
|
||||
assert(encLen >= 2 * OAES_BLOCK_SIZE + CENC_KEY_LEN);
|
||||
size_t blockLen = min(aData.size() - i, CENC_KEY_LEN);
|
||||
for (size_t j = 0; j < blockLen; j++) {
|
||||
aData[i + j] ^= enc[2 * OAES_BLOCK_SIZE + j];
|
||||
}
|
||||
IncrementIV(aIV);
|
||||
}
|
||||
|
||||
oaes_free(&aes);
|
||||
}
|
||||
|
||||
/**
|
||||
* ClearKey expects all Key IDs to be base64 encoded with non-standard alphabet
|
||||
* and padding.
|
||||
*/
|
||||
static bool
|
||||
EncodeBase64Web(vector<uint8_t> aBinary, string& aEncoded)
|
||||
{
|
||||
const char sAlphabet[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
const uint8_t sMask = 0x3f;
|
||||
|
||||
aEncoded.resize((aBinary.size() * 8 + 5) / 6);
|
||||
|
||||
// Pad binary data in case there's rubbish past the last byte.
|
||||
aBinary.push_back(0);
|
||||
|
||||
// Number of bytes not consumed in the previous character
|
||||
uint32_t shift = 0;
|
||||
|
||||
auto out = aEncoded.begin();
|
||||
auto data = aBinary.begin();
|
||||
for (string::size_type i = 0; i < aEncoded.length(); i++) {
|
||||
if (shift) {
|
||||
out[i] = (*data << (6 - shift)) & sMask;
|
||||
data++;
|
||||
} else {
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
out[i] += (*data >> (shift + 2)) & sMask;
|
||||
shift = (shift + 2) % 8;
|
||||
|
||||
// Cast idx to size_t before using it as an array-index,
|
||||
// to pacify clang 'Wchar-subscripts' warning:
|
||||
size_t idx = static_cast<size_t>(out[i]);
|
||||
assert(idx < MOZ_ARRAY_LENGTH(sAlphabet)); // out of bounds index for 'sAlphabet'
|
||||
out[i] = sAlphabet[idx];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
|
||||
string& aOutRequest,
|
||||
GMPSessionType aSessionType)
|
||||
{
|
||||
assert(aKeyIDs.size() && aOutRequest.empty());
|
||||
|
||||
aOutRequest.append("{\"kids\":[");
|
||||
for (size_t i = 0; i < aKeyIDs.size(); i++) {
|
||||
if (i) {
|
||||
aOutRequest.append(",");
|
||||
}
|
||||
aOutRequest.append("\"");
|
||||
|
||||
string base64key;
|
||||
EncodeBase64Web(aKeyIDs[i], base64key);
|
||||
aOutRequest.append(base64key);
|
||||
|
||||
aOutRequest.append("\"");
|
||||
}
|
||||
aOutRequest.append("],\"type\":");
|
||||
|
||||
aOutRequest.append("\"");
|
||||
aOutRequest.append(SessionTypeToString(aSessionType));
|
||||
aOutRequest.append("\"}");
|
||||
}
|
||||
|
||||
#define EXPECT_SYMBOL(CTX, X) do { \
|
||||
if (GetNextSymbol(CTX) != (X)) { \
|
||||
CK_LOGE("Unexpected symbol in JWK parser"); \
|
||||
return false; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
struct ParserContext {
|
||||
const uint8_t* mIter;
|
||||
const uint8_t* mEnd;
|
||||
};
|
||||
|
||||
static uint8_t
|
||||
PeekSymbol(ParserContext& aCtx)
|
||||
{
|
||||
for (; aCtx.mIter < aCtx.mEnd; (aCtx.mIter)++) {
|
||||
if (!isspace(*aCtx.mIter)) {
|
||||
return *aCtx.mIter;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
GetNextSymbol(ParserContext& aCtx)
|
||||
{
|
||||
uint8_t sym = PeekSymbol(aCtx);
|
||||
aCtx.mIter++;
|
||||
return sym;
|
||||
}
|
||||
|
||||
static bool SkipToken(ParserContext& aCtx);
|
||||
|
||||
static bool
|
||||
SkipString(ParserContext& aCtx)
|
||||
{
|
||||
EXPECT_SYMBOL(aCtx, '"');
|
||||
for (uint8_t sym = GetNextSymbol(aCtx); sym; sym = GetNextSymbol(aCtx)) {
|
||||
if (sym == '\\') {
|
||||
sym = GetNextSymbol(aCtx);
|
||||
} else if (sym == '"') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip whole object and values it contains.
|
||||
*/
|
||||
static bool
|
||||
SkipObject(ParserContext& aCtx)
|
||||
{
|
||||
EXPECT_SYMBOL(aCtx, '{');
|
||||
|
||||
if (PeekSymbol(aCtx) == '}') {
|
||||
GetNextSymbol(aCtx);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!SkipString(aCtx)) return false;
|
||||
EXPECT_SYMBOL(aCtx, ':');
|
||||
if (!SkipToken(aCtx)) return false;
|
||||
|
||||
if (PeekSymbol(aCtx) == '}') {
|
||||
GetNextSymbol(aCtx);
|
||||
return true;
|
||||
}
|
||||
EXPECT_SYMBOL(aCtx, ',');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip array value and the values it contains.
|
||||
*/
|
||||
static bool
|
||||
SkipArray(ParserContext& aCtx)
|
||||
{
|
||||
EXPECT_SYMBOL(aCtx, '[');
|
||||
|
||||
if (PeekSymbol(aCtx) == ']') {
|
||||
GetNextSymbol(aCtx);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (SkipToken(aCtx)) {
|
||||
if (PeekSymbol(aCtx) == ']') {
|
||||
GetNextSymbol(aCtx);
|
||||
return true;
|
||||
}
|
||||
EXPECT_SYMBOL(aCtx, ',');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip unquoted literals like numbers, |true|, and |null|.
|
||||
* (XXX and anything else that matches /([:alnum:]|[+-.])+/)
|
||||
*/
|
||||
static bool
|
||||
SkipLiteral(ParserContext& aCtx)
|
||||
{
|
||||
for (; aCtx.mIter < aCtx.mEnd; aCtx.mIter++) {
|
||||
if (!isalnum(*aCtx.mIter) &&
|
||||
*aCtx.mIter != '.' && *aCtx.mIter != '-' && *aCtx.mIter != '+') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
SkipToken(ParserContext& aCtx)
|
||||
{
|
||||
uint8_t startSym = PeekSymbol(aCtx);
|
||||
if (startSym == '"') {
|
||||
CK_LOGD("JWK parser skipping string");
|
||||
return SkipString(aCtx);
|
||||
} else if (startSym == '{') {
|
||||
CK_LOGD("JWK parser skipping object");
|
||||
return SkipObject(aCtx);
|
||||
} else if (startSym == '[') {
|
||||
CK_LOGD("JWK parser skipping array");
|
||||
return SkipArray(aCtx);
|
||||
} else {
|
||||
CK_LOGD("JWK parser skipping literal");
|
||||
return SkipLiteral(aCtx);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetNextLabel(ParserContext& aCtx, string& aOutLabel)
|
||||
{
|
||||
EXPECT_SYMBOL(aCtx, '"');
|
||||
|
||||
const uint8_t* start = aCtx.mIter;
|
||||
for (uint8_t sym = GetNextSymbol(aCtx); sym; sym = GetNextSymbol(aCtx)) {
|
||||
if (sym == '\\') {
|
||||
GetNextSymbol(aCtx);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sym == '"') {
|
||||
aOutLabel.assign(start, aCtx.mIter - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseKeyObject(ParserContext& aCtx, KeyIdPair& aOutKey)
|
||||
{
|
||||
EXPECT_SYMBOL(aCtx, '{');
|
||||
|
||||
// Reject empty objects as invalid licenses.
|
||||
if (PeekSymbol(aCtx) == '}') {
|
||||
GetNextSymbol(aCtx);
|
||||
return false;
|
||||
}
|
||||
|
||||
string keyId;
|
||||
string key;
|
||||
|
||||
while (true) {
|
||||
string label;
|
||||
string value;
|
||||
|
||||
if (!GetNextLabel(aCtx, label)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EXPECT_SYMBOL(aCtx, ':');
|
||||
if (label == "kty") {
|
||||
if (!GetNextLabel(aCtx, value)) return false;
|
||||
// By spec, type must be "oct".
|
||||
if (value != "oct") return false;
|
||||
} else if (label == "k" && PeekSymbol(aCtx) == '"') {
|
||||
// if this isn't a string we will fall through to the SkipToken() path.
|
||||
if (!GetNextLabel(aCtx, key)) return false;
|
||||
} else if (label == "kid" && PeekSymbol(aCtx) == '"') {
|
||||
if (!GetNextLabel(aCtx, keyId)) return false;
|
||||
} else {
|
||||
if (!SkipToken(aCtx)) return false;
|
||||
}
|
||||
|
||||
uint8_t sym = PeekSymbol(aCtx);
|
||||
if (!sym || sym == '}') {
|
||||
break;
|
||||
}
|
||||
EXPECT_SYMBOL(aCtx, ',');
|
||||
}
|
||||
|
||||
return !key.empty() &&
|
||||
!keyId.empty() &&
|
||||
DecodeBase64(keyId, aOutKey.mKeyId) &&
|
||||
DecodeBase64(key, aOutKey.mKey) &&
|
||||
GetNextSymbol(aCtx) == '}';
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseKeys(ParserContext& aCtx, vector<KeyIdPair>& aOutKeys)
|
||||
{
|
||||
// Consume start of array.
|
||||
EXPECT_SYMBOL(aCtx, '[');
|
||||
|
||||
while (true) {
|
||||
KeyIdPair key;
|
||||
if (!ParseKeyObject(aCtx, key)) {
|
||||
CK_LOGE("Failed to parse key object");
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!key.mKey.empty() && !key.mKeyId.empty());
|
||||
aOutKeys.push_back(key);
|
||||
|
||||
uint8_t sym = PeekSymbol(aCtx);
|
||||
if (!sym || sym == ']') {
|
||||
break;
|
||||
}
|
||||
|
||||
EXPECT_SYMBOL(aCtx, ',');
|
||||
}
|
||||
|
||||
return GetNextSymbol(aCtx) == ']';
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
||||
vector<KeyIdPair>& aOutKeys,
|
||||
GMPSessionType aSessionType)
|
||||
{
|
||||
ParserContext ctx;
|
||||
ctx.mIter = aKeyData;
|
||||
ctx.mEnd = aKeyData + aKeyDataSize;
|
||||
|
||||
// Consume '{' from start of object.
|
||||
EXPECT_SYMBOL(ctx, '{');
|
||||
|
||||
while (true) {
|
||||
string label;
|
||||
// Consume member key.
|
||||
if (!GetNextLabel(ctx, label)) return false;
|
||||
EXPECT_SYMBOL(ctx, ':');
|
||||
|
||||
if (label == "keys") {
|
||||
// Parse "keys" array.
|
||||
if (!ParseKeys(ctx, aOutKeys)) return false;
|
||||
} else if (label == "type") {
|
||||
// Consume type string.
|
||||
string type;
|
||||
if (!GetNextLabel(ctx, type)) return false;
|
||||
if (type != SessionTypeToString(aSessionType)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
SkipToken(ctx);
|
||||
}
|
||||
|
||||
// Check for end of object.
|
||||
if (PeekSymbol(ctx) == '}') {
|
||||
break;
|
||||
}
|
||||
|
||||
// Consume ',' between object members.
|
||||
EXPECT_SYMBOL(ctx, ',');
|
||||
}
|
||||
|
||||
// Consume '}' from end of object.
|
||||
EXPECT_SYMBOL(ctx, '}');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseKeyIds(ParserContext& aCtx, vector<KeyId>& aOutKeyIds)
|
||||
{
|
||||
// Consume start of array.
|
||||
EXPECT_SYMBOL(aCtx, '[');
|
||||
|
||||
while (true) {
|
||||
string label;
|
||||
vector<uint8_t> keyId;
|
||||
if (!GetNextLabel(aCtx, label) || !DecodeBase64(label, keyId)) {
|
||||
return false;
|
||||
}
|
||||
if (!keyId.empty() && keyId.size() <= kMaxKeyIdsLength) {
|
||||
aOutKeyIds.push_back(keyId);
|
||||
}
|
||||
|
||||
uint8_t sym = PeekSymbol(aCtx);
|
||||
if (!sym || sym == ']') {
|
||||
break;
|
||||
}
|
||||
|
||||
EXPECT_SYMBOL(aCtx, ',');
|
||||
}
|
||||
|
||||
return GetNextSymbol(aCtx) == ']';
|
||||
}
|
||||
|
||||
|
||||
/* static */ bool
|
||||
ClearKeyUtils::ParseKeyIdsInitData(const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize,
|
||||
vector<KeyId>& aOutKeyIds)
|
||||
{
|
||||
ParserContext ctx;
|
||||
ctx.mIter = aInitData;
|
||||
ctx.mEnd = aInitData + aInitDataSize;
|
||||
|
||||
// Consume '{' from start of object.
|
||||
EXPECT_SYMBOL(ctx, '{');
|
||||
|
||||
while (true) {
|
||||
string label;
|
||||
// Consume member kids.
|
||||
if (!GetNextLabel(ctx, label)) return false;
|
||||
EXPECT_SYMBOL(ctx, ':');
|
||||
|
||||
if (label == "kids") {
|
||||
// Parse "kids" array.
|
||||
if (!ParseKeyIds(ctx, aOutKeyIds) ||
|
||||
aOutKeyIds.empty()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
SkipToken(ctx);
|
||||
}
|
||||
|
||||
// Check for end of object.
|
||||
if (PeekSymbol(ctx) == '}') {
|
||||
break;
|
||||
}
|
||||
|
||||
// Consume ',' between object members.
|
||||
EXPECT_SYMBOL(ctx, ',');
|
||||
}
|
||||
|
||||
// Consume '}' from end of object.
|
||||
EXPECT_SYMBOL(ctx, '}');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ const char*
|
||||
ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType)
|
||||
{
|
||||
switch (aSessionType) {
|
||||
case kGMPTemporySession: return "temporary";
|
||||
case kGMPPersistentSession: return "persistent-license";
|
||||
default: {
|
||||
assert(false); // Should not reach here.
|
||||
return "invalid";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength)
|
||||
{
|
||||
if (aLength > 10) {
|
||||
// 10 is the max number of characters in UINT32_MAX when
|
||||
// represented as a string; ClearKey session ids are integers.
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < aLength; i++) {
|
||||
if (!isdigit(aBuff[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
GMPMutex* GMPCreateMutex() {
|
||||
GMPMutex* mutex;
|
||||
auto err = GetPlatform()->createmutex(&mutex);
|
||||
assert(mutex);
|
||||
return GMP_FAILED(err) ? nullptr : mutex;
|
||||
}
|
||||
114
media/gmp-clearkey/0.1/ClearKeyUtils.h
Normal file
114
media/gmp-clearkey/0.1/ClearKeyUtils.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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 __ClearKeyUtils_h__
|
||||
#define __ClearKeyUtils_h__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
#include "gmp-api/gmp-decryption.h"
|
||||
|
||||
#if 0
|
||||
void CK_Log(const char* aFmt, ...);
|
||||
#define CK_LOGE(...) CK_Log(__VA_ARGS__)
|
||||
#define CK_LOGD(...) CK_Log(__VA_ARGS__)
|
||||
#define CK_LOGW(...) CK_Log(__VA_ARGS__)
|
||||
#else
|
||||
#define CK_LOGE(...)
|
||||
#define CK_LOGD(...)
|
||||
#define CK_LOGW(...)
|
||||
#endif
|
||||
|
||||
struct GMPPlatformAPI;
|
||||
extern GMPPlatformAPI* GetPlatform();
|
||||
|
||||
typedef std::vector<uint8_t> KeyId;
|
||||
typedef std::vector<uint8_t> Key;
|
||||
|
||||
// The session response size should be within a reasonable limit.
|
||||
// The size 64 KB is referenced from web-platform-test.
|
||||
static const uint32_t kMaxSessionResponseLength = 65536;
|
||||
|
||||
// Provide limitation for KeyIds length and webm initData size.
|
||||
static const uint32_t kMaxWebmInitDataSize = 65536;
|
||||
static const uint32_t kMaxKeyIdsLength = 512;
|
||||
|
||||
struct KeyIdPair
|
||||
{
|
||||
KeyId mKeyId;
|
||||
Key mKey;
|
||||
};
|
||||
|
||||
class ClearKeyUtils
|
||||
{
|
||||
public:
|
||||
static void DecryptAES(const std::vector<uint8_t>& aKey,
|
||||
std::vector<uint8_t>& aData, std::vector<uint8_t>& aIV);
|
||||
|
||||
static bool ParseKeyIdsInitData(const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize,
|
||||
std::vector<KeyId>& aOutKeyIds);
|
||||
|
||||
static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds,
|
||||
std::string& aOutRequest,
|
||||
GMPSessionType aSessionType);
|
||||
|
||||
static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
||||
std::vector<KeyIdPair>& aOutKeys,
|
||||
GMPSessionType aSessionType);
|
||||
static const char* SessionTypeToString(GMPSessionType aSessionType);
|
||||
|
||||
static bool IsValidSessionId(const char* aBuff, uint32_t aLength);
|
||||
};
|
||||
|
||||
template<class Container, class Element>
|
||||
inline bool
|
||||
Contains(const Container& aContainer, const Element& aElement)
|
||||
{
|
||||
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>
|
||||
inline void
|
||||
Assign(std::vector<T>& aVec, const T* aData, size_t aLength)
|
||||
{
|
||||
aVec.assign(aData, aData + aLength);
|
||||
}
|
||||
|
||||
#endif // __ClearKeyUtils_h__
|
||||
116
media/gmp-clearkey/0.1/RefCounted.h
Normal file
116
media/gmp-clearkey/0.1/RefCounted.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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 __RefCount_h__
|
||||
#define __RefCount_h__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include "ClearKeyUtils.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#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.
|
||||
class RefCounted {
|
||||
public:
|
||||
void AddRef() {
|
||||
++mRefCount;
|
||||
}
|
||||
|
||||
uint32_t Release() {
|
||||
uint32_t newCount = --mRefCount;
|
||||
if (!newCount) {
|
||||
delete this;
|
||||
}
|
||||
return newCount;
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCounted()
|
||||
: mRefCount(0)
|
||||
{
|
||||
}
|
||||
virtual ~RefCounted()
|
||||
{
|
||||
assert(!mRefCount);
|
||||
}
|
||||
AtomicRefCount mRefCount;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class RefPtr {
|
||||
public:
|
||||
explicit RefPtr(T* aPtr) : mPtr(nullptr) {
|
||||
Assign(aPtr);
|
||||
}
|
||||
~RefPtr() {
|
||||
Assign(nullptr);
|
||||
}
|
||||
T* operator->() const { return mPtr; }
|
||||
|
||||
RefPtr& operator=(T* aVal) {
|
||||
Assign(aVal);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void Assign(T* aPtr) {
|
||||
if (mPtr) {
|
||||
mPtr->Release();
|
||||
}
|
||||
mPtr = aPtr;
|
||||
if (mPtr) {
|
||||
aPtr->AddRef();
|
||||
}
|
||||
}
|
||||
T* mPtr;
|
||||
};
|
||||
|
||||
#endif // __RefCount_h__
|
||||
455
media/gmp-clearkey/0.1/VideoDecoder.cpp
Normal file
455
media/gmp-clearkey/0.1/VideoDecoder.cpp
Normal file
|
|
@ -0,0 +1,455 @@
|
|||
/*
|
||||
* Copyright 2013, 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 <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include "AnnexB.h"
|
||||
#include "BigEndian.h"
|
||||
#include "ClearKeyDecryptionManager.h"
|
||||
#include "ClearKeyUtils.h"
|
||||
#include "gmp-task-utils.h"
|
||||
#include "VideoDecoder.h"
|
||||
|
||||
using namespace wmf;
|
||||
|
||||
VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI)
|
||||
: mHostAPI(aHostAPI)
|
||||
, mCallback(nullptr)
|
||||
, mWorkerThread(nullptr)
|
||||
, mMutex(nullptr)
|
||||
, mNumInputTasks(0)
|
||||
, mSentExtraData(false)
|
||||
, mIsFlushing(false)
|
||||
, mHasShutdown(false)
|
||||
{
|
||||
// We drop the ref in DecodingComplete().
|
||||
AddRef();
|
||||
}
|
||||
|
||||
VideoDecoder::~VideoDecoder()
|
||||
{
|
||||
if (mMutex) {
|
||||
mMutex->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings,
|
||||
const uint8_t* aCodecSpecific,
|
||||
uint32_t aCodecSpecificLength,
|
||||
GMPVideoDecoderCallback* aCallback,
|
||||
int32_t aCoreCount)
|
||||
{
|
||||
mCallback = aCallback;
|
||||
assert(mCallback);
|
||||
mDecoder = new WMFH264Decoder();
|
||||
HRESULT hr = mDecoder->Init(aCoreCount);
|
||||
if (FAILED(hr)) {
|
||||
CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
|
||||
mCallback->Error(GMPGenericErr);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
VideoDecoder::EnsureWorker()
|
||||
{
|
||||
if (!mWorkerThread) {
|
||||
GetPlatform()->createthread(&mWorkerThread);
|
||||
if (!mWorkerThread) {
|
||||
mCallback->Error(GMPAllocErr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
EnsureWorker();
|
||||
|
||||
{
|
||||
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");
|
||||
AutoPtr<DecodeData> d(aData);
|
||||
HRESULT hr;
|
||||
|
||||
{
|
||||
AutoLock lock(mMutex);
|
||||
mNumInputTasks--;
|
||||
assert(mNumInputTasks >= 0);
|
||||
}
|
||||
|
||||
if (mIsFlushing) {
|
||||
CK_LOGD("VideoDecoder::DecodeTask rejecting frame: flushing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aData || !mHostAPI || !mDecoder) {
|
||||
CK_LOGE("Decode job not set up correctly!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>& buffer = aData->mBuffer;
|
||||
if (aData->mCrypto.IsValid()) {
|
||||
// Plugin host should have set up its decryptor/key sessions
|
||||
// before trying to decode!
|
||||
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(),
|
||||
buffer.size(),
|
||||
aData->mTimestamp,
|
||||
aData->mDuration);
|
||||
|
||||
CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
|
||||
if (FAILED(hr)) {
|
||||
CK_LOGE("VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n",
|
||||
hr,
|
||||
((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
|
||||
return;
|
||||
}
|
||||
|
||||
while (hr == S_OK) {
|
||||
CComPtr<IMFSample> output;
|
||||
hr = mDecoder->Output(&output);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
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;
|
||||
}
|
||||
|
||||
auto vf = static_cast<GMPVideoi420Frame*>(f);
|
||||
|
||||
hr = SampleToVideoFrame(aSample, aWidth, aHeight, aStride, vf);
|
||||
ENSURE(SUCCEEDED(hr), /*void*/);
|
||||
|
||||
mCallback->Decoded(vf);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
|
||||
int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
int32_t aStride,
|
||||
GMPVideoi420Frame* aVideoFrame)
|
||||
{
|
||||
ENSURE(aSample != nullptr, E_POINTER);
|
||||
ENSURE(aVideoFrame != nullptr, E_POINTER);
|
||||
|
||||
HRESULT hr;
|
||||
CComPtr<IMFMediaBuffer> mediaBuffer;
|
||||
|
||||
// Must convert to contiguous mediaBuffer to use IMD2DBuffer interface.
|
||||
hr = aSample->ConvertToContiguousBuffer(&mediaBuffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
// Try and use the IMF2DBuffer interface if available, otherwise fallback
|
||||
// to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
|
||||
// but only some systems (Windows 8?) support it.
|
||||
BYTE* data = nullptr;
|
||||
LONG stride = 0;
|
||||
CComPtr<IMF2DBuffer> twoDBuffer;
|
||||
hr = mediaBuffer->QueryInterface(static_cast<IMF2DBuffer**>(&twoDBuffer));
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = twoDBuffer->Lock2D(&data, &stride);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
} else {
|
||||
hr = mediaBuffer->Lock(&data, NULL, NULL);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
stride = aStride;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// YV12, planar format: [YYYY....][VVVV....][UUUU....]
|
||||
// i.e., Y, then V, then U.
|
||||
uint32_t padding = 0;
|
||||
if (aHeight % 16 != 0) {
|
||||
padding = 16 - (aHeight % 16);
|
||||
}
|
||||
int32_t y_size = stride * (aHeight + padding);
|
||||
int32_t v_size = stride * (aHeight + padding) / 4;
|
||||
int32_t halfStride = (stride + 1) / 2;
|
||||
int32_t halfHeight = (aHeight + 1) / 2;
|
||||
|
||||
auto err = aVideoFrame->CreateEmptyFrame(stride, aHeight, stride, halfStride, halfStride);
|
||||
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
|
||||
|
||||
err = aVideoFrame->SetWidth(aWidth);
|
||||
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
|
||||
err = aVideoFrame->SetHeight(aHeight);
|
||||
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
|
||||
|
||||
uint8_t* outBuffer = aVideoFrame->Buffer(kGMPYPlane);
|
||||
ENSURE(outBuffer != nullptr, E_FAIL);
|
||||
assert(aVideoFrame->AllocatedSize(kGMPYPlane) >= stride*aHeight);
|
||||
memcpy(outBuffer, data, stride*aHeight);
|
||||
|
||||
outBuffer = aVideoFrame->Buffer(kGMPUPlane);
|
||||
ENSURE(outBuffer != nullptr, E_FAIL);
|
||||
assert(aVideoFrame->AllocatedSize(kGMPUPlane) >= halfStride*halfHeight);
|
||||
memcpy(outBuffer, data+y_size, halfStride*halfHeight);
|
||||
|
||||
outBuffer = aVideoFrame->Buffer(kGMPVPlane);
|
||||
ENSURE(outBuffer != nullptr, E_FAIL);
|
||||
assert(aVideoFrame->AllocatedSize(kGMPVPlane) >= halfStride*halfHeight);
|
||||
memcpy(outBuffer, data + y_size + v_size, halfStride*halfHeight);
|
||||
|
||||
if (twoDBuffer) {
|
||||
twoDBuffer->Unlock2D();
|
||||
} else {
|
||||
mediaBuffer->Unlock();
|
||||
}
|
||||
|
||||
LONGLONG hns = 0;
|
||||
hr = aSample->GetSampleTime(&hns);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
aVideoFrame->SetTimestamp(HNsToUsecs(hns));
|
||||
|
||||
hr = aSample->GetSampleDuration(&hns);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
aVideoFrame->SetDuration(HNsToUsecs(hns));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VideoDecoder::ResetCompleteTask()
|
||||
{
|
||||
mIsFlushing = false;
|
||||
if (mCallback) {
|
||||
MaybeRunOnMainThread(WrapTask(mCallback,
|
||||
&GMPVideoDecoderCallback::ResetComplete));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoDecoder::Reset()
|
||||
{
|
||||
mIsFlushing = true;
|
||||
if (mDecoder) {
|
||||
mDecoder->Reset();
|
||||
}
|
||||
|
||||
// Schedule ResetComplete callback to run after existing frames have been
|
||||
// flushed out of the task queue.
|
||||
EnsureWorker();
|
||||
mWorkerThread->Post(WrapTaskRefCounted(this,
|
||||
&VideoDecoder::ResetCompleteTask));
|
||||
}
|
||||
|
||||
void
|
||||
VideoDecoder::DrainTask()
|
||||
{
|
||||
mDecoder->Drain();
|
||||
|
||||
// Return any pending output.
|
||||
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
|
||||
VideoDecoder::DecodingComplete()
|
||||
{
|
||||
if (mWorkerThread) {
|
||||
mWorkerThread->Join();
|
||||
}
|
||||
mHasShutdown = true;
|
||||
|
||||
// Release the reference we added in the constructor. There may be
|
||||
// WrapRefCounted tasks that also hold references to us, and keep
|
||||
// us alive a little longer.
|
||||
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));
|
||||
}
|
||||
110
media/gmp-clearkey/0.1/VideoDecoder.h
Normal file
110
media/gmp-clearkey/0.1/VideoDecoder.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2013, 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 __VideoDecoder_h__
|
||||
#define __VideoDecoder_h__
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "gmp-task-utils.h"
|
||||
#include "gmp-video-decode.h"
|
||||
#include "gmp-video-host.h"
|
||||
#include "WMFH264Decoder.h"
|
||||
|
||||
#include "mfobjects.h"
|
||||
|
||||
class VideoDecoder : public GMPVideoDecoder
|
||||
, public RefCounted
|
||||
{
|
||||
public:
|
||||
VideoDecoder(GMPVideoHost *aHostAPI);
|
||||
|
||||
virtual void InitDecode(const GMPVideoCodec& aCodecSettings,
|
||||
const uint8_t* aCodecSpecific,
|
||||
uint32_t aCodecSpecificLength,
|
||||
GMPVideoDecoderCallback* aCallback,
|
||||
int32_t aCoreCount) override;
|
||||
|
||||
virtual void Decode(GMPVideoEncodedFrame* aInputFrame,
|
||||
bool aMissingFrames,
|
||||
const uint8_t* aCodecSpecific,
|
||||
uint32_t aCodecSpecificLength,
|
||||
int64_t aRenderTimeMs = -1);
|
||||
|
||||
virtual void Reset() override;
|
||||
|
||||
virtual void Drain() override;
|
||||
|
||||
virtual void DecodingComplete() override;
|
||||
|
||||
bool HasShutdown() { return mHasShutdown; }
|
||||
|
||||
private:
|
||||
|
||||
virtual ~VideoDecoder();
|
||||
|
||||
void EnsureWorker();
|
||||
|
||||
void DrainTask();
|
||||
|
||||
struct DecodeData {
|
||||
DecodeData()
|
||||
: mTimestamp(0)
|
||||
, mDuration(0)
|
||||
, mIsKeyframe(false)
|
||||
{}
|
||||
std::vector<uint8_t> mBuffer;
|
||||
uint64_t mTimestamp;
|
||||
uint64_t mDuration;
|
||||
bool mIsKeyframe;
|
||||
CryptoMetaData mCrypto;
|
||||
};
|
||||
|
||||
void DecodeTask(DecodeData* aData);
|
||||
|
||||
void ResetCompleteTask();
|
||||
|
||||
void ReturnOutput(IMFSample* aSample,
|
||||
int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
int32_t aStride);
|
||||
|
||||
HRESULT SampleToVideoFrame(IMFSample* aSample,
|
||||
int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
int32_t aStride,
|
||||
GMPVideoi420Frame* aVideoFrame);
|
||||
|
||||
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;
|
||||
|
||||
std::vector<uint8_t> mExtraData;
|
||||
std::vector<uint8_t> mAnnexB;
|
||||
|
||||
int32_t mNumInputTasks;
|
||||
bool mSentExtraData;
|
||||
|
||||
std::atomic<bool> mIsFlushing;
|
||||
|
||||
bool mHasShutdown;
|
||||
};
|
||||
|
||||
#endif // __VideoDecoder_h__
|
||||
366
media/gmp-clearkey/0.1/WMFAACDecoder.cpp
Normal file
366
media/gmp-clearkey/0.1/WMFAACDecoder.cpp
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright 2013, 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 "WMFAACDecoder.h"
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace wmf {
|
||||
|
||||
WMFAACDecoder::WMFAACDecoder()
|
||||
: mDecoder(nullptr)
|
||||
, mChannels(0)
|
||||
, mRate(0)
|
||||
{
|
||||
memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO));
|
||||
memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO));
|
||||
}
|
||||
|
||||
WMFAACDecoder::~WMFAACDecoder()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
HRESULT
|
||||
AACAudioSpecificConfigToUserData(BYTE* aAudioSpecConfig, UINT32 aConfigLength,
|
||||
BYTE** aOutUserData, UINT32* aOutUserDataLength)
|
||||
{
|
||||
// For MFAudioFormat_AAC, MF_MT_USER_DATA
|
||||
// Contains the portion of the HEAACWAVEINFO structure that appears
|
||||
// after the WAVEFORMATEX structure (that is, after the wfx member).
|
||||
// This is followed by the AudioSpecificConfig() data, as defined
|
||||
// by ISO/IEC 14496-3.
|
||||
// ...
|
||||
// The length of the AudioSpecificConfig() data is 2 bytes for AAC-LC
|
||||
// or HE-AAC with implicit signaling of SBR/PS. It is more than 2 bytes
|
||||
// for HE-AAC with explicit signaling of SBR/PS.
|
||||
//
|
||||
// The value of audioObjectType as defined in AudioSpecificConfig()
|
||||
// must be 2, indicating AAC-LC. The value of extensionAudioObjectType
|
||||
// must be 5 for SBR or 29 for PS.
|
||||
//
|
||||
// See:
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx
|
||||
//
|
||||
// HEAACWAVEINFO structure:
|
||||
// typedef struct heaacwaveinfo_tag {
|
||||
// WAVEFORMATEX wfx;
|
||||
// WORD wPayloadType;
|
||||
// WORD wAudioProfileLevelIndication;
|
||||
// WORD wStructType;
|
||||
// WORD wReserved1;
|
||||
// DWORD dwReserved2;
|
||||
// }
|
||||
const UINT32 heeInfoLen = 4 * sizeof(WORD) + sizeof(DWORD);
|
||||
BYTE heeInfo[heeInfoLen] = {0};
|
||||
WORD* w = (WORD*)heeInfo;
|
||||
w[0] = 0x0; // Payload type raw AAC
|
||||
w[1] = 0; // Profile level unspecified
|
||||
|
||||
const UINT32 len = heeInfoLen + aConfigLength;
|
||||
BYTE* data = new BYTE[len];
|
||||
memcpy(data, heeInfo, heeInfoLen);
|
||||
memcpy(data+heeInfoLen, aAudioSpecConfig, aConfigLength);
|
||||
*aOutUserData = data;
|
||||
*aOutUserDataLength = len;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::Init(int32_t aChannelCount,
|
||||
int32_t aSampleRate,
|
||||
BYTE* aAACAudioSpecificConfig,
|
||||
UINT32 aAudioConfigLength)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
// AAC decoder is in msauddecmft on Win8, and msmpeg2adec in earlier versions.
|
||||
hr = CreateMFT(CLSID_CMSAACDecMFT,
|
||||
WMFDecoderDllNameFor(AAC),
|
||||
mDecoder);
|
||||
if (FAILED(hr)) {
|
||||
hr = CreateMFT(CLSID_CMSAACDecMFT,
|
||||
WMFDecoderDllNameFor(AAC),
|
||||
mDecoder);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Failed to create AAC decoder\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
BYTE* userData = nullptr;
|
||||
UINT32 userDataLength;
|
||||
hr = AACAudioSpecificConfigToUserData(aAACAudioSpecificConfig,
|
||||
aAudioConfigLength,
|
||||
&userData,
|
||||
&userDataLength);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
hr = SetDecoderInputType(aChannelCount, aSampleRate, userData, userDataLength);
|
||||
delete userData;
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SetDecoderOutputType();
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::SetDecoderInputType(int32_t aChannelCount,
|
||||
int32_t aSampleRate,
|
||||
BYTE* aUserData,
|
||||
UINT32 aUserDataLength)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
CComPtr<IMFMediaType> type;
|
||||
hr = MFCreateMediaType(&type);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
mRate = aSampleRate;
|
||||
hr = type->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, mRate);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
mChannels = aChannelCount;
|
||||
hr = type->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, mChannels);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->SetBlob(MF_MT_USER_DATA, aUserData, aUserDataLength);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->SetInputType(0, type, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::SetDecoderOutputType()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
CComPtr<IMFMediaType> type;
|
||||
|
||||
UINT32 typeIndex = 0;
|
||||
while (type = nullptr, SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, &type))) {
|
||||
GUID subtype;
|
||||
hr = type->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||
if (FAILED(hr)) {
|
||||
continue;
|
||||
}
|
||||
if (subtype == MFAudioFormat_PCM) {
|
||||
hr = mDecoder->SetOutputType(0, type, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &mChannels);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &mRate);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData)
|
||||
{
|
||||
ENSURE(mDecoder != nullptr, E_POINTER);
|
||||
HRESULT hr = mDecoder->ProcessMessage(aMsg, aData);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::CreateInputSample(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp,
|
||||
IMFSample** aOutSample)
|
||||
{
|
||||
HRESULT hr;
|
||||
CComPtr<IMFSample> sample = nullptr;
|
||||
hr = MFCreateSample(&sample);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
CComPtr<IMFMediaBuffer> buffer = nullptr;
|
||||
int32_t bufferSize = std::max<uint32_t>(uint32_t(mInputStreamInfo.cbSize), aDataSize);
|
||||
UINT32 alignment = (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0;
|
||||
hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
DWORD maxLength = 0;
|
||||
DWORD currentLength = 0;
|
||||
BYTE* dst = nullptr;
|
||||
hr = buffer->Lock(&dst, &maxLength, ¤tLength);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
// Copy data into sample's buffer.
|
||||
memcpy(dst, aData, aDataSize);
|
||||
|
||||
hr = buffer->Unlock();
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = buffer->SetCurrentLength(aDataSize);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->AddBuffer(buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->SetSampleTime(UsecsToHNs(aTimestamp));
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
*aOutSample = sample.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::CreateOutputSample(IMFSample** aOutSample)
|
||||
{
|
||||
HRESULT hr;
|
||||
CComPtr<IMFSample> sample = nullptr;
|
||||
hr = MFCreateSample(&sample);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
CComPtr<IMFMediaBuffer> buffer = nullptr;
|
||||
int32_t bufferSize = mOutputStreamInfo.cbSize;
|
||||
UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1) ? mOutputStreamInfo.cbAlignment - 1 : 0;
|
||||
hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->AddBuffer(buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
*aOutSample = sample.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::GetOutputSample(IMFSample** aOutSample)
|
||||
{
|
||||
HRESULT hr;
|
||||
// We allocate samples for MFT output.
|
||||
MFT_OUTPUT_DATA_BUFFER output = {0};
|
||||
|
||||
CComPtr<IMFSample> sample = nullptr;
|
||||
hr = CreateOutputSample(&sample);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
output.pSample = sample;
|
||||
|
||||
DWORD status = 0;
|
||||
hr = mDecoder->ProcessOutput(0, 1, &output, &status);
|
||||
CComPtr<IMFCollection> events = output.pEvents; // Ensure this is released.
|
||||
|
||||
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
|
||||
// Type change. Probably geometric apperature change.
|
||||
hr = SetDecoderOutputType();
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return GetOutputSample(aOutSample);
|
||||
} else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || !sample) {
|
||||
return MF_E_TRANSFORM_NEED_MORE_INPUT;
|
||||
}
|
||||
// Treat other errors as fatal.
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
assert(sample);
|
||||
|
||||
*aOutSample = sample.Detach();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::Input(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp)
|
||||
{
|
||||
CComPtr<IMFSample> input = nullptr;
|
||||
HRESULT hr = CreateInputSample(aData, aDataSize, aTimestamp, &input);
|
||||
ENSURE(SUCCEEDED(hr) && input!=nullptr, hr);
|
||||
|
||||
hr = mDecoder->ProcessInput(0, input, 0);
|
||||
if (hr == MF_E_NOTACCEPTING) {
|
||||
// MFT *already* has enough data to produce a sample. Retrieve it.
|
||||
LOG("ProcessInput returned MF_E_NOTACCEPTING\n");
|
||||
return MF_E_NOTACCEPTING;
|
||||
}
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::Output(IMFSample** aOutput)
|
||||
{
|
||||
CComPtr<IMFSample> outputSample = nullptr;
|
||||
HRESULT hr = GetOutputSample(&outputSample);
|
||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
return MF_E_TRANSFORM_NEED_MORE_INPUT;
|
||||
}
|
||||
// Treat other errors as fatal.
|
||||
ENSURE(SUCCEEDED(hr) && outputSample, hr);
|
||||
|
||||
*aOutput = outputSample.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::Reset()
|
||||
{
|
||||
HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFAACDecoder::Drain()
|
||||
{
|
||||
HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
}
|
||||
74
media/gmp-clearkey/0.1/WMFAACDecoder.h
Normal file
74
media/gmp-clearkey/0.1/WMFAACDecoder.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2013, 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.
|
||||
*/
|
||||
|
||||
#if !defined(WMFAACDecoder_h_)
|
||||
#define WMFAACDecoder_h_
|
||||
|
||||
#include "WMFUtils.h"
|
||||
|
||||
namespace wmf {
|
||||
|
||||
class WMFAACDecoder {
|
||||
public:
|
||||
WMFAACDecoder();
|
||||
~WMFAACDecoder();
|
||||
|
||||
HRESULT Init(int32_t aChannelCount,
|
||||
int32_t aSampleRate,
|
||||
BYTE* aUserData,
|
||||
UINT32 aUserDataLength);
|
||||
|
||||
HRESULT Input(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp);
|
||||
|
||||
HRESULT Output(IMFSample** aOutput);
|
||||
|
||||
HRESULT Reset();
|
||||
|
||||
HRESULT Drain();
|
||||
|
||||
UINT32 Channels() const { return mChannels; }
|
||||
UINT32 Rate() const { return mRate; }
|
||||
|
||||
private:
|
||||
|
||||
HRESULT GetOutputSample(IMFSample** aOutSample);
|
||||
HRESULT CreateOutputSample(IMFSample** aOutSample);
|
||||
HRESULT CreateInputSample(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp,
|
||||
IMFSample** aOutSample);
|
||||
|
||||
HRESULT SetDecoderInputType(int32_t aChannelCount,
|
||||
int32_t aSampleRate,
|
||||
BYTE* aUserData,
|
||||
UINT32 aUserDataLength);
|
||||
HRESULT SetDecoderOutputType();
|
||||
HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData);
|
||||
|
||||
MFT_INPUT_STREAM_INFO mInputStreamInfo;
|
||||
MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
|
||||
|
||||
CComPtr<IMFTransform> mDecoder;
|
||||
|
||||
UINT32 mChannels;
|
||||
UINT32 mRate;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
365
media/gmp-clearkey/0.1/WMFH264Decoder.cpp
Normal file
365
media/gmp-clearkey/0.1/WMFH264Decoder.cpp
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
* Copyright 2013, 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 "WMFH264Decoder.h"
|
||||
#include <algorithm>
|
||||
#include <codecapi.h>
|
||||
|
||||
namespace wmf {
|
||||
|
||||
WMFH264Decoder::WMFH264Decoder()
|
||||
: mDecoder(nullptr)
|
||||
{
|
||||
memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO));
|
||||
memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO));
|
||||
}
|
||||
|
||||
WMFH264Decoder::~WMFH264Decoder()
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::Init(int32_t aCoreCount)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = CreateMFT(__uuidof(CMSH264DecoderMFT),
|
||||
WMFDecoderDllNameFor(H264),
|
||||
mDecoder);
|
||||
if (FAILED(hr)) {
|
||||
// Windows 7 Enterprise Server N (which is what Mozilla's mochitests run
|
||||
// on) need a different CLSID to instantiate the H.264 decoder.
|
||||
hr = CreateMFT(CLSID_CMSH264DecMFT,
|
||||
WMFDecoderDllNameFor(H264),
|
||||
mDecoder);
|
||||
}
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
CComPtr<IMFAttributes> attr;
|
||||
hr = mDecoder->GetAttributes(&attr);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
hr = attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
|
||||
GetNumThreads(aCoreCount));
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SetDecoderInputType();
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SetDecoderOutputType();
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType)
|
||||
{
|
||||
ENSURE(aMediaType != nullptr, E_POINTER);
|
||||
HRESULT hr;
|
||||
|
||||
IntRect pictureRegion;
|
||||
hr = wmf::GetPictureRegion(aMediaType, pictureRegion);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
UINT32 width = 0, height = 0;
|
||||
hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
|
||||
ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
|
||||
|
||||
UINT32 stride = 0;
|
||||
hr = GetDefaultStride(aMediaType, &stride);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
ENSURE(stride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
|
||||
|
||||
// Success! Save state.
|
||||
mStride = stride;
|
||||
mVideoWidth = width;
|
||||
mVideoHeight = height;
|
||||
mPictureRegion = pictureRegion;
|
||||
|
||||
LOG("WMFH264Decoder frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d)\n",
|
||||
width, height,
|
||||
mStride,
|
||||
mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
WMFH264Decoder::GetFrameWidth() const
|
||||
{
|
||||
return mVideoWidth;
|
||||
}
|
||||
|
||||
int32_t
|
||||
WMFH264Decoder::GetFrameHeight() const
|
||||
{
|
||||
return mVideoHeight;
|
||||
}
|
||||
|
||||
const IntRect&
|
||||
WMFH264Decoder::GetPictureRegion() const
|
||||
{
|
||||
return mPictureRegion;
|
||||
}
|
||||
|
||||
int32_t
|
||||
WMFH264Decoder::GetStride() const
|
||||
{
|
||||
return mStride;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::SetDecoderInputType()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
CComPtr<IMFMediaType> type;
|
||||
hr = MFCreateMediaType(&type);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = mDecoder->SetInputType(0, type, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::SetDecoderOutputType()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
CComPtr<IMFMediaType> type;
|
||||
|
||||
UINT32 typeIndex = 0;
|
||||
while (type = nullptr, SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, &type))) {
|
||||
GUID subtype;
|
||||
hr = type->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||
if (FAILED(hr)) {
|
||||
continue;
|
||||
}
|
||||
if (subtype == MFVideoFormat_I420 || subtype == MFVideoFormat_IYUV) {
|
||||
// On Windows 7 Enterprise N the MFT reports it reports IYUV instead
|
||||
// of I420. Other Windows' report I420. The formats are the same, so
|
||||
// support both.
|
||||
hr = mDecoder->SetOutputType(0, type, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = ConfigureVideoFrameGeometry(type);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData)
|
||||
{
|
||||
ENSURE(mDecoder != nullptr, E_POINTER);
|
||||
HRESULT hr = mDecoder->ProcessMessage(aMsg, aData);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::CreateInputSample(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp,
|
||||
Microseconds aDuration,
|
||||
IMFSample** aOutSample)
|
||||
{
|
||||
HRESULT hr;
|
||||
CComPtr<IMFSample> sample;
|
||||
hr = MFCreateSample(&sample);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
CComPtr<IMFMediaBuffer> buffer;
|
||||
int32_t bufferSize = std::max<uint32_t>(uint32_t(mInputStreamInfo.cbSize), aDataSize);
|
||||
UINT32 alignment = (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0;
|
||||
hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
DWORD maxLength = 0;
|
||||
DWORD currentLength = 0;
|
||||
BYTE* dst = nullptr;
|
||||
hr = buffer->Lock(&dst, &maxLength, ¤tLength);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
// Copy data into sample's buffer.
|
||||
memcpy(dst, aData, aDataSize);
|
||||
|
||||
hr = buffer->Unlock();
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = buffer->SetCurrentLength(aDataSize);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->AddBuffer(buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->SetSampleTime(UsecsToHNs(aTimestamp));
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
sample->SetSampleDuration(UsecsToHNs(aDuration));
|
||||
|
||||
*aOutSample = sample.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::CreateOutputSample(IMFSample** aOutSample)
|
||||
{
|
||||
HRESULT hr;
|
||||
CComPtr<IMFSample> sample;
|
||||
hr = MFCreateSample(&sample);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
CComPtr<IMFMediaBuffer> buffer;
|
||||
int32_t bufferSize = mOutputStreamInfo.cbSize;
|
||||
UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1) ? mOutputStreamInfo.cbAlignment - 1 : 0;
|
||||
hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = sample->AddBuffer(buffer);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
*aOutSample = sample.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::GetOutputSample(IMFSample** aOutSample)
|
||||
{
|
||||
HRESULT hr;
|
||||
// We allocate samples for MFT output.
|
||||
MFT_OUTPUT_DATA_BUFFER output = {0};
|
||||
|
||||
CComPtr<IMFSample> sample;
|
||||
hr = CreateOutputSample(&sample);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
output.pSample = sample;
|
||||
|
||||
DWORD status = 0;
|
||||
hr = mDecoder->ProcessOutput(0, 1, &output, &status);
|
||||
//LOG(L"WMFH264Decoder::GetOutputSample() ProcessOutput returned 0x%x\n", hr);
|
||||
CComPtr<IMFCollection> events = output.pEvents; // Ensure this is released.
|
||||
|
||||
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
|
||||
// Type change. Probably geometric apperature change.
|
||||
hr = SetDecoderOutputType();
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return GetOutputSample(aOutSample);
|
||||
} else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
return MF_E_TRANSFORM_NEED_MORE_INPUT;
|
||||
}
|
||||
// Treat other errors as fatal.
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
assert(sample);
|
||||
|
||||
// output.pSample
|
||||
*aOutSample = sample.Detach(); // AddRefs
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::Input(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp,
|
||||
Microseconds aDuration)
|
||||
{
|
||||
HRESULT hr;
|
||||
CComPtr<IMFSample> input = nullptr;
|
||||
hr = CreateInputSample(aData, aDataSize, aTimestamp, aDuration, &input);
|
||||
ENSURE(SUCCEEDED(hr) && input!=nullptr, hr);
|
||||
|
||||
hr = mDecoder->ProcessInput(0, input, 0);
|
||||
if (hr == MF_E_NOTACCEPTING) {
|
||||
// MFT *already* has enough data to produce a sample. Retrieve it.
|
||||
LOG("ProcessInput returned MF_E_NOTACCEPTING\n");
|
||||
return MF_E_NOTACCEPTING;
|
||||
}
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::Output(IMFSample** aOutput)
|
||||
{
|
||||
HRESULT hr;
|
||||
CComPtr<IMFSample> outputSample;
|
||||
hr = GetOutputSample(&outputSample);
|
||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
return MF_E_TRANSFORM_NEED_MORE_INPUT;
|
||||
}
|
||||
// Treat other errors as fatal.
|
||||
ENSURE(SUCCEEDED(hr) && outputSample, hr);
|
||||
|
||||
*aOutput = outputSample.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::Reset()
|
||||
{
|
||||
HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFH264Decoder::Drain()
|
||||
{
|
||||
HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} // namespace wmf
|
||||
78
media/gmp-clearkey/0.1/WMFH264Decoder.h
Normal file
78
media/gmp-clearkey/0.1/WMFH264Decoder.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2013, 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.
|
||||
*/
|
||||
|
||||
#if !defined(WMFH264Decoder_h_)
|
||||
#define WMFH264Decoder_h_
|
||||
|
||||
#include "WMFUtils.h"
|
||||
|
||||
namespace wmf {
|
||||
|
||||
class WMFH264Decoder {
|
||||
public:
|
||||
WMFH264Decoder();
|
||||
~WMFH264Decoder();
|
||||
|
||||
HRESULT Init(int32_t aCoreCount);
|
||||
|
||||
HRESULT Input(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp,
|
||||
Microseconds aDuration);
|
||||
|
||||
HRESULT Output(IMFSample** aOutput);
|
||||
|
||||
HRESULT Reset();
|
||||
|
||||
int32_t GetFrameWidth() const;
|
||||
int32_t GetFrameHeight() const;
|
||||
const IntRect& GetPictureRegion() const;
|
||||
int32_t GetStride() const;
|
||||
|
||||
HRESULT Drain();
|
||||
|
||||
private:
|
||||
|
||||
HRESULT SetDecoderInputType();
|
||||
HRESULT SetDecoderOutputType();
|
||||
HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData);
|
||||
|
||||
HRESULT CreateInputSample(const uint8_t* aData,
|
||||
uint32_t aDataSize,
|
||||
Microseconds aTimestamp,
|
||||
Microseconds aDuration,
|
||||
IMFSample** aOutSample);
|
||||
|
||||
HRESULT CreateOutputSample(IMFSample** aOutSample);
|
||||
|
||||
HRESULT GetOutputSample(IMFSample** aOutSample);
|
||||
HRESULT ConfigureVideoFrameGeometry(IMFMediaType* aMediaType);
|
||||
|
||||
MFT_INPUT_STREAM_INFO mInputStreamInfo;
|
||||
MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
|
||||
|
||||
CComPtr<IMFTransform> mDecoder;
|
||||
|
||||
int32_t mVideoWidth;
|
||||
int32_t mVideoHeight;
|
||||
IntRect mPictureRegion;
|
||||
int32_t mStride;
|
||||
|
||||
};
|
||||
|
||||
} // namespace wmf
|
||||
|
||||
#endif
|
||||
20
media/gmp-clearkey/0.1/WMFSymbols.h
Normal file
20
media/gmp-clearkey/0.1/WMFSymbols.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
MFPLAT_FUNC(MFCreateSample, "mfplat.dll");
|
||||
MFPLAT_FUNC(MFCreateAlignedMemoryBuffer, "mfplat.dll");
|
||||
MFPLAT_FUNC(MFGetStrideForBitmapInfoHeader, "evr.dll");
|
||||
MFPLAT_FUNC(MFCreateMediaType, "mfplat.dll");
|
||||
276
media/gmp-clearkey/0.1/WMFUtils.cpp
Normal file
276
media/gmp-clearkey/0.1/WMFUtils.cpp
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright 2013, 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 "WMFUtils.h"
|
||||
#include "ClearKeyUtils.h"
|
||||
#include <versionhelpers.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
|
||||
#define INITGUID
|
||||
#include <guiddef.h>
|
||||
|
||||
#pragma comment(lib, "mfuuid.lib")
|
||||
#pragma comment(lib, "wmcodecdspuuid")
|
||||
|
||||
void LOG(const char* format, ...)
|
||||
{
|
||||
#ifdef WMF_DECODER_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
|
||||
// Some SDK versions don't define the AAC decoder CLSID.
|
||||
// {32D186A7-218F-4C75-8876-DD77273A8999}
|
||||
DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
|
||||
#endif
|
||||
|
||||
DEFINE_GUID(CLSID_CMSH264DecMFT, 0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45, 0x28, 0x31, 0xA8, 0x7D, 0x9D);
|
||||
|
||||
namespace wmf {
|
||||
|
||||
|
||||
#define MFPLAT_FUNC(_func, _dllname) \
|
||||
decltype(::_func)* _func;
|
||||
#include "WMFSymbols.h"
|
||||
#undef MFPLAT_FUNC
|
||||
|
||||
static bool
|
||||
LinkMfplat()
|
||||
{
|
||||
static bool sInitDone = false;
|
||||
static bool sInitOk = false;
|
||||
if (!sInitDone) {
|
||||
sInitDone = true;
|
||||
HMODULE handle;
|
||||
|
||||
#define MFPLAT_FUNC(_func, _dllname) \
|
||||
handle = GetModuleHandleA(_dllname); \
|
||||
if (!(_func = (decltype(_func))(GetProcAddress(handle, #_func)))) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#include "WMFSymbols.h"
|
||||
#undef MFPLAT_FUNC
|
||||
sInitOk = true;
|
||||
}
|
||||
return sInitOk;
|
||||
}
|
||||
|
||||
const char*
|
||||
WMFDecoderDllNameFor(CodecType aCodec)
|
||||
{
|
||||
if (aCodec == H264) {
|
||||
// For H.264 decoding, we need msmpeg2vdec.dll on Win 7 & 8,
|
||||
// and mfh264dec.dll on Vista.
|
||||
if (IsWindows7OrGreater()) {
|
||||
return "msmpeg2vdec.dll";
|
||||
} else {
|
||||
return "mfh264dec.dll";
|
||||
}
|
||||
} else if (aCodec == AAC) {
|
||||
// For AAC decoding, we need to use msauddecmft.dll on Win8,
|
||||
// msmpeg2adec.dll on Win7, and msheaacdec.dll on Vista.
|
||||
if (IsWindows8OrGreater()) {
|
||||
return "msauddecmft.dll";
|
||||
} else if (IsWindows7OrGreater()) {
|
||||
return "msmpeg2adec.dll";
|
||||
} else {
|
||||
return "mfheaacdec.dll";
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
EnsureLibs()
|
||||
{
|
||||
static bool sInitDone = false;
|
||||
static bool sInitOk = false;
|
||||
if (!sInitDone) {
|
||||
sInitOk = LinkMfplat() &&
|
||||
!!GetModuleHandleA(WMFDecoderDllNameFor(AAC)) &&
|
||||
!!GetModuleHandleA(WMFDecoderDllNameFor(H264));
|
||||
sInitDone = true;
|
||||
}
|
||||
return sInitOk;
|
||||
}
|
||||
|
||||
int32_t
|
||||
MFOffsetToInt32(const MFOffset& aOffset)
|
||||
{
|
||||
return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
|
||||
}
|
||||
|
||||
// Gets the sub-region of the video frame that should be displayed.
|
||||
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
|
||||
HRESULT
|
||||
GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion)
|
||||
{
|
||||
// Determine if "pan and scan" is enabled for this media. If it is, we
|
||||
// only display a region of the video frame, not the entire frame.
|
||||
BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
|
||||
|
||||
// If pan and scan mode is enabled. Try to get the display region.
|
||||
HRESULT hr = E_FAIL;
|
||||
MFVideoArea videoArea;
|
||||
memset(&videoArea, 0, sizeof(MFVideoArea));
|
||||
if (panScan) {
|
||||
hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE,
|
||||
(UINT8*)&videoArea,
|
||||
sizeof(MFVideoArea),
|
||||
NULL);
|
||||
}
|
||||
|
||||
// If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
|
||||
// check for a minimimum display aperture.
|
||||
if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
|
||||
hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
|
||||
(UINT8*)&videoArea,
|
||||
sizeof(MFVideoArea),
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (hr == MF_E_ATTRIBUTENOTFOUND) {
|
||||
// Minimum display aperture is not set, for "backward compatibility with
|
||||
// some components", check for a geometric aperture.
|
||||
hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
|
||||
(UINT8*)&videoArea,
|
||||
sizeof(MFVideoArea),
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
// The media specified a picture region, return it.
|
||||
IntRect picture = IntRect(MFOffsetToInt32(videoArea.OffsetX),
|
||||
MFOffsetToInt32(videoArea.OffsetY),
|
||||
videoArea.Area.cx,
|
||||
videoArea.Area.cy);
|
||||
ENSURE(picture.width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
|
||||
ENSURE(picture.height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
|
||||
aOutPictureRegion = picture;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// No picture region defined, fall back to using the entire video area.
|
||||
UINT32 width = 0, height = 0;
|
||||
hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
|
||||
ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
|
||||
aOutPictureRegion = IntRect(0, 0, width, height);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
|
||||
{
|
||||
// Try to get the default stride from the media type.
|
||||
UINT32 stride = 0;
|
||||
HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ENSURE(stride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
|
||||
*aOutStride = stride;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Stride attribute not set, calculate it.
|
||||
GUID subtype = GUID_NULL;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
|
||||
hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
|
||||
ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
|
||||
|
||||
LONG lstride = 0;
|
||||
hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lstride);
|
||||
ENSURE(SUCCEEDED(hr), hr);
|
||||
ENSURE(lstride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
|
||||
ENSURE(lstride >= 0, E_FAIL);
|
||||
*aOutStride = lstride;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void dump(const uint8_t* data, uint32_t len, const char* filename)
|
||||
{
|
||||
FILE* f = 0;
|
||||
fopen_s(&f, filename, "wb");
|
||||
fwrite(data, len, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
CreateMFT(const CLSID& clsid,
|
||||
const char* aDllName,
|
||||
CComPtr<IMFTransform>& aOutMFT)
|
||||
{
|
||||
HMODULE module = ::GetModuleHandleA(aDllName);
|
||||
if (!module) {
|
||||
LOG("Failed to get %S\n", aDllName);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
typedef HRESULT (WINAPI* DllGetClassObjectFnPtr)(const CLSID& clsid,
|
||||
const IID& iid,
|
||||
void** object);
|
||||
|
||||
DllGetClassObjectFnPtr GetClassObjPtr =
|
||||
reinterpret_cast<DllGetClassObjectFnPtr>(GetProcAddress(module, "DllGetClassObject"));
|
||||
if (!GetClassObjPtr) {
|
||||
LOG("Failed to get DllGetClassObject\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
CComPtr<IClassFactory> classFactory;
|
||||
HRESULT hr = GetClassObjPtr(clsid,
|
||||
__uuidof(IClassFactory),
|
||||
reinterpret_cast<void**>(static_cast<IClassFactory**>(&classFactory)));
|
||||
if (FAILED(hr)) {
|
||||
LOG("Failed to get H264 IClassFactory\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
hr = classFactory->CreateInstance(NULL,
|
||||
__uuidof(IMFTransform),
|
||||
reinterpret_cast<void**>(static_cast<IMFTransform**>(&aOutMFT)));
|
||||
if (FAILED(hr)) {
|
||||
LOG("Failed to get create MFT\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
GetNumThreads(int32_t aCoreCount)
|
||||
{
|
||||
return aCoreCount > 4 ? -1 : (std::max)(aCoreCount - 1, 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
269
media/gmp-clearkey/0.1/WMFUtils.h
Normal file
269
media/gmp-clearkey/0.1/WMFUtils.h
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright 2013, 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 __WMFUtils_h__
|
||||
#define __WMFUtils_h__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <assert.h>
|
||||
#include <mfapi.h>
|
||||
#include <mferror.h>
|
||||
#include <mfobjects.h>
|
||||
#include <mftransform.h>
|
||||
#include <wmcodecdsp.h>
|
||||
#include "VideoLimits.h"
|
||||
|
||||
#include "gmp-platform.h"
|
||||
|
||||
void LOG(const char* format, ...);
|
||||
|
||||
#ifdef LOG_SAMPLE_DECODE
|
||||
#define SAMPLE_LOG LOG
|
||||
#else
|
||||
#define SAMPLE_LOG(...)
|
||||
#endif
|
||||
|
||||
#ifndef CLSID_CMSAACDecMFT
|
||||
#define WMF_MUST_DEFINE_AAC_MFT_CLSID
|
||||
extern "C" const CLSID CLSID_CMSAACDecMFT;
|
||||
#endif
|
||||
|
||||
extern "C" const CLSID CLSID_CMSH264DecMFT;
|
||||
|
||||
namespace wmf {
|
||||
|
||||
// Reimplementation of CComPtr to reduce dependence on system
|
||||
// shared libraries.
|
||||
template<class T>
|
||||
class CComPtr {
|
||||
public:
|
||||
CComPtr(CComPtr&& aOther) : mPtr(aOther.Detach()) { }
|
||||
CComPtr& operator=(CComPtr&& other) { mPtr = other.Detach(); }
|
||||
|
||||
CComPtr(const CComPtr& aOther) : mPtr(nullptr) { Set(aOther.Get()); }
|
||||
CComPtr() : mPtr(nullptr) { }
|
||||
CComPtr(T* const & aPtr) : mPtr(nullptr) { Set(aPtr); }
|
||||
CComPtr(const std::nullptr_t& aNullPtr) : mPtr(aNullPtr) { }
|
||||
T** operator&() { return &mPtr; }
|
||||
T* operator->(){ return mPtr; }
|
||||
operator T*() { return mPtr; }
|
||||
T* operator=(T* const & aPtr) { return Set(aPtr); }
|
||||
T* operator=(const std::nullptr_t& aPtr) { return mPtr = aPtr; }
|
||||
|
||||
T* Get() const { return mPtr; }
|
||||
|
||||
T* Detach() {
|
||||
T* tmp = mPtr;
|
||||
mPtr = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
~CComPtr() {
|
||||
if (mPtr) {
|
||||
mPtr->Release();
|
||||
}
|
||||
mPtr = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T* Set(T* aPtr) {
|
||||
if (mPtr == aPtr) {
|
||||
return aPtr;
|
||||
}
|
||||
if (mPtr) {
|
||||
mPtr->Release();
|
||||
}
|
||||
mPtr = aPtr;
|
||||
if (mPtr) {
|
||||
mPtr->AddRef();
|
||||
}
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
T* mPtr;
|
||||
};
|
||||
|
||||
class IntRect {
|
||||
public:
|
||||
IntRect(int32_t _x, int32_t _y, int32_t _w, int32_t _h)
|
||||
: x(_x), y(_y), width(_w), height(_h) {}
|
||||
IntRect()
|
||||
: x(0), y(0), width(0), height(0) {}
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
};
|
||||
|
||||
typedef int64_t Microseconds;
|
||||
|
||||
#ifdef ENSURE
|
||||
#undef ENSURE
|
||||
#endif
|
||||
|
||||
#define ENSURE(condition, ret) \
|
||||
{ if (!(condition)) { LOG("##condition## FAILED %S:%d\n", __FILE__, __LINE__); return ret; } }
|
||||
|
||||
#define GMP_SUCCEEDED(x) ((x) == GMPNoErr)
|
||||
#define GMP_FAILED(x) ((x) != GMPNoErr)
|
||||
|
||||
#define MFPLAT_FUNC(_func, _dllname) \
|
||||
extern decltype(::_func)* _func;
|
||||
#include "WMFSymbols.h"
|
||||
#undef MFPLAT_FUNC
|
||||
|
||||
bool
|
||||
EnsureLibs();
|
||||
|
||||
HRESULT
|
||||
GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion);
|
||||
|
||||
HRESULT
|
||||
GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride);
|
||||
|
||||
// Converts from microseconds to hundreds of nanoseconds.
|
||||
// We use microseconds for our timestamps, whereas WMF uses
|
||||
// hundreds of nanoseconds.
|
||||
inline int64_t
|
||||
UsecsToHNs(int64_t aUsecs) {
|
||||
return aUsecs * 10;
|
||||
}
|
||||
|
||||
// Converts from hundreds of nanoseconds to microseconds.
|
||||
// We use microseconds for our timestamps, whereas WMF uses
|
||||
// hundreds of nanoseconds.
|
||||
inline int64_t
|
||||
HNsToUsecs(int64_t hNanoSecs) {
|
||||
return hNanoSecs / 10;
|
||||
}
|
||||
|
||||
inline std::string narrow(std::wstring &wide) {
|
||||
std::string ns(wide.begin(), wide.end());
|
||||
return ns;
|
||||
}
|
||||
|
||||
inline std::wstring widen(std::string &narrow) {
|
||||
std::wstring ws(narrow.begin(), narrow.end());
|
||||
return ws;
|
||||
}
|
||||
|
||||
#define ARRAY_LENGTH(array_) \
|
||||
(sizeof(array_)/sizeof(array_[0]))
|
||||
|
||||
template<class Type>
|
||||
class AutoPtr {
|
||||
public:
|
||||
AutoPtr()
|
||||
: mPtr(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
AutoPtr(AutoPtr<Type>& aPtr)
|
||||
: mPtr(aPtr.Forget())
|
||||
{
|
||||
}
|
||||
|
||||
AutoPtr(Type* aPtr)
|
||||
: mPtr(aPtr)
|
||||
{
|
||||
}
|
||||
|
||||
~AutoPtr() {
|
||||
if (mPtr) {
|
||||
delete mPtr;
|
||||
}
|
||||
}
|
||||
|
||||
Type* Forget() {
|
||||
Type* rv = mPtr;
|
||||
mPtr = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
AutoPtr<Type>& operator=(Type* aOther) {
|
||||
Assign(aOther);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AutoPtr<Type>& operator=(AutoPtr<Type>& aOther) {
|
||||
Assign(aOther.Forget());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Type* Get() const {
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
Type* operator->() const {
|
||||
assert(mPtr);
|
||||
return Get();
|
||||
}
|
||||
|
||||
operator Type*() const {
|
||||
return Get();
|
||||
}
|
||||
|
||||
Type** Receive() {
|
||||
return &mPtr;
|
||||
}
|
||||
private:
|
||||
|
||||
void Assign(Type* aPtr) {
|
||||
if (mPtr) {
|
||||
delete mPtr;
|
||||
}
|
||||
mPtr = aPtr;
|
||||
}
|
||||
|
||||
Type* mPtr;
|
||||
};
|
||||
|
||||
// Video frame microseconds are (currently) in 90kHz units, as used by RTP.
|
||||
// Use this to convert to microseconds...
|
||||
inline Microseconds RTPTimeToMicroseconds(int64_t rtptime) {
|
||||
return (rtptime * 1000000) / 90000;
|
||||
}
|
||||
|
||||
inline uint32_t MicrosecondsToRTPTime(Microseconds us) {
|
||||
return uint32_t(0xffffffff & (us * 90000) / 1000000);
|
||||
}
|
||||
|
||||
void dump(const uint8_t* data, uint32_t len, const char* filename);
|
||||
|
||||
HRESULT
|
||||
CreateMFT(const CLSID& clsid,
|
||||
const char* aDllName,
|
||||
CComPtr<IMFTransform>& aOutMFT);
|
||||
|
||||
enum CodecType {
|
||||
H264,
|
||||
AAC,
|
||||
};
|
||||
|
||||
// Returns the name of the DLL that is needed to decode H.264 or AAC on
|
||||
// the given windows version we're running on.
|
||||
const char* WMFDecoderDllNameFor(CodecType aCodec);
|
||||
|
||||
// Returns the maximum number of threads we want WMF to use for decoding
|
||||
// given the number of logical processors available.
|
||||
int32_t GetNumThreads(int32_t aCoreCount);
|
||||
|
||||
} // namespace wmf
|
||||
|
||||
#endif // __WMFUtils_h__
|
||||
10
media/gmp-clearkey/0.1/clearkey.info.in
Normal file
10
media/gmp-clearkey/0.1/clearkey.info.in
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
Name: clearkey
|
||||
Description: ClearKey Gecko Media Plugin
|
||||
Version: 1
|
||||
#ifdef ENABLE_WMF
|
||||
APIs: eme-decrypt-v9[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey]
|
||||
Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll, evr.dll, mfheaacdec.dll, mfh264dec.dll, mfplat.dll
|
||||
#else
|
||||
APIs: eme-decrypt-v9[org.w3.clearkey]
|
||||
Libraries:
|
||||
#endif
|
||||
89
media/gmp-clearkey/0.1/gmp-clearkey.cpp
Normal file
89
media/gmp-clearkey/0.1/gmp-clearkey.cpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ClearKeyAsyncShutdown.h"
|
||||
#include "ClearKeySessionManager.h"
|
||||
#include "gmp-api/gmp-async-shutdown.h"
|
||||
#include "gmp-api/gmp-decryption.h"
|
||||
#include "gmp-api/gmp-platform.h"
|
||||
|
||||
#if defined(ENABLE_WMF)
|
||||
#include "WMFUtils.h"
|
||||
#include "AudioDecoder.h"
|
||||
#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" {
|
||||
|
||||
GMP_EXPORT GMPErr
|
||||
GMPInit(GMPPlatformAPI* aPlatformAPI)
|
||||
{
|
||||
sPlatform = aPlatformAPI;
|
||||
return GMPNoErr;
|
||||
}
|
||||
|
||||
GMP_EXPORT GMPErr
|
||||
GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
|
||||
{
|
||||
CK_LOGD("ClearKey GMPGetAPI |%s|", aApiName);
|
||||
assert(!*aPluginAPI);
|
||||
|
||||
if (!strcmp(aApiName, GMP_API_DECRYPTOR)) {
|
||||
*aPluginAPI = new ClearKeySessionManager();
|
||||
}
|
||||
#if defined(ENABLE_WMF)
|
||||
else if (!strcmp(aApiName, GMP_API_AUDIO_DECODER) &&
|
||||
wmf::EnsureLibs()) {
|
||||
*aPluginAPI = new AudioDecoder(static_cast<GMPAudioHost*>(aHostAPI));
|
||||
} else if (!strcmp(aApiName, GMP_API_VIDEO_DECODER) &&
|
||||
wmf::EnsureLibs()) {
|
||||
*aPluginAPI = new VideoDecoder(static_cast<GMPVideoHost*>(aHostAPI));
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
|
||||
}
|
||||
|
||||
GMP_EXPORT GMPErr
|
||||
GMPShutdown(void)
|
||||
{
|
||||
CK_LOGD("ClearKey GMPShutdown");
|
||||
return GMPNoErr;
|
||||
}
|
||||
|
||||
}
|
||||
1938
media/gmp-clearkey/0.1/gmp-task-utils-generated.h
Normal file
1938
media/gmp-clearkey/0.1/gmp-task-utils-generated.h
Normal file
File diff suppressed because it is too large
Load diff
47
media/gmp-clearkey/0.1/gmp-task-utils.h
Normal file
47
media/gmp-clearkey/0.1/gmp-task-utils.h
Normal file
|
|
@ -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_
|
||||
93
media/gmp-clearkey/0.1/gtest/TestClearKeyUtils.cpp
Normal file
93
media/gmp-clearkey/0.1/gtest/TestClearKeyUtils.cpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "../ClearKeyBase64.cpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct B64Test {
|
||||
string b64;
|
||||
vector<uint8_t> raw;
|
||||
bool shouldPass;
|
||||
};
|
||||
|
||||
const B64Test tests[] = {
|
||||
{
|
||||
"AAAAADk4AU4AAAAAAAAAAA",
|
||||
{ 0x0, 0x0, 0x0, 0x0, 0x39, 0x38, 0x1, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
|
||||
true
|
||||
},
|
||||
{
|
||||
"h2mqp1zAJjDIC34YXEXBxA==",
|
||||
{ 0x87, 0x69, 0xaa, 0xa7, 0x5c, 0xc0, 0x26, 0x30, 0xc8, 0xb, 0x7e, 0x18, 0x5c, 0x45, 0xc1, 0xc4 },
|
||||
true
|
||||
},
|
||||
{
|
||||
"flcdA35XHQN-Vx0DflcdAw",
|
||||
{ 0x7e, 0x57, 0x1d, 0x3, 0x7e, 0x57, 0x1d, 0x3, 0x7e, 0x57, 0x1d, 0x3, 0x7e, 0x57, 0x1d, 0x3 },
|
||||
true
|
||||
},
|
||||
{
|
||||
"flczM35XMzN-VzMzflczMw",
|
||||
{ 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33 },
|
||||
true,
|
||||
},
|
||||
{
|
||||
"flcdBH5XHQR-Vx0EflcdBA",
|
||||
{ 0x7e, 0x57, 0x1d, 0x4, 0x7e, 0x57, 0x1d, 0x4, 0x7e, 0x57, 0x1d, 0x4, 0x7e, 0x57, 0x1d, 0x4 },
|
||||
true
|
||||
},
|
||||
{
|
||||
"fldERH5XRER-V0REfldERA",
|
||||
{ 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44 },
|
||||
true
|
||||
},
|
||||
{
|
||||
"fuzzbiz=",
|
||||
{ 0x7e, 0xec, 0xf3, 0x6e, 0x2c },
|
||||
true
|
||||
},
|
||||
{
|
||||
"fuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbiz",
|
||||
{
|
||||
0x7e, 0xec, 0xf3, 0x6e, 0x2c, 0xdf, 0xbb, 0x3c, 0xdb, 0x8b,
|
||||
0x37, 0xee, 0xcf, 0x36, 0xe2, 0xcd, 0xfb, 0xb3, 0xcd, 0xb8,
|
||||
0xb3, 0x7e, 0xec, 0xf3, 0x6e, 0x2c, 0xdf, 0xbb, 0x3c, 0xdb,
|
||||
0x8b, 0x37, 0xee, 0xcf, 0x36, 0xe2, 0xcd, 0xfb, 0xb3, 0xcd,
|
||||
0xb8, 0xb3
|
||||
},
|
||||
true
|
||||
},
|
||||
{ "", { }, true },
|
||||
{ "00", { 0xd3 }, true },
|
||||
{ "000", { 0xd3, 0x4d }, true },
|
||||
|
||||
{ "invalid", { 0x8a, 0x7b, 0xda, 0x96, 0x27 }, true },
|
||||
{ "invalic", { 0x8a, 0x7b, 0xda, 0x96, 0x27 }, true },
|
||||
|
||||
// Failure tests
|
||||
{ "A", { }, false }, // 1 character is too few.
|
||||
{ "_", { }, false }, // 1 character is too few.
|
||||
};
|
||||
|
||||
TEST(ClearKey, DecodeBase64) {
|
||||
for (const B64Test& test : tests) {
|
||||
vector<uint8_t> v;
|
||||
bool rv = DecodeBase64(string(test.b64), v);
|
||||
EXPECT_EQ(test.shouldPass, rv);
|
||||
if (test.shouldPass) {
|
||||
EXPECT_EQ(test.raw.size(), v.size());
|
||||
for (size_t k = 0; k < test.raw.size(); k++) {
|
||||
EXPECT_EQ(test.raw[k], v[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
media/gmp-clearkey/0.1/gtest/moz.build
Normal file
15
media/gmp-clearkey/0.1/gtest/moz.build
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'TestClearKeyUtils.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'..',
|
||||
]
|
||||
75
media/gmp-clearkey/0.1/moz.build
Normal file
75
media/gmp-clearkey/0.1/moz.build
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SharedLibrary('clearkey')
|
||||
|
||||
FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
|
||||
|
||||
FINAL_TARGET_PP_FILES += ['clearkey.info.in']
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ClearKeyAsyncShutdown.cpp',
|
||||
'ClearKeyBase64.cpp',
|
||||
'ClearKeyDecryptionManager.cpp',
|
||||
'ClearKeyPersistence.cpp',
|
||||
'ClearKeySession.cpp',
|
||||
'ClearKeySessionManager.cpp',
|
||||
'ClearKeyStorage.cpp',
|
||||
'ClearKeyUtils.cpp',
|
||||
'gmp-clearkey.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'openaes/oaes_lib.c',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
UNIFIED_SOURCES += [
|
||||
'AnnexB.cpp',
|
||||
'AudioDecoder.cpp',
|
||||
'VideoDecoder.cpp',
|
||||
'WMFAACDecoder.cpp',
|
||||
'WMFH264Decoder.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'WMFUtils.cpp',
|
||||
]
|
||||
|
||||
OS_LIBS += [
|
||||
'mfuuid',
|
||||
]
|
||||
|
||||
DEFINES['ENABLE_WMF'] = True
|
||||
|
||||
TEST_DIRS += [
|
||||
'gtest',
|
||||
]
|
||||
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/media/gmp',
|
||||
]
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
DEFINES['MOZ_NO_MOZALLOC'] = True
|
||||
|
||||
USE_LIBS += ['psshparser']
|
||||
|
||||
# Suppress warnings in third-party code.
|
||||
if CONFIG['GNU_CXX']:
|
||||
CFLAGS += [
|
||||
'-Wno-missing-braces',
|
||||
'-Wno-pointer-to-int-cast',
|
||||
'-Wno-sign-compare',
|
||||
'-include', 'stdio.h', # for sprintf() prototype
|
||||
'-include', 'unistd.h', # for getpid() prototype
|
||||
]
|
||||
elif CONFIG['_MSC_VER']:
|
||||
CFLAGS += [
|
||||
'-FI', 'stdio.h', # for sprintf() prototype
|
||||
'-wd4090', # '=' : different 'const' qualifiers
|
||||
]
|
||||
27
media/gmp-clearkey/0.1/openaes/LICENSE
Normal file
27
media/gmp-clearkey/0.1/openaes/LICENSE
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---------------------------------------------------------------------------
|
||||
OpenAES Licence
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
---------------------------------------------------------------------------
|
||||
77
media/gmp-clearkey/0.1/openaes/oaes_common.h
Normal file
77
media/gmp-clearkey/0.1/openaes/oaes_common.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* ---------------------------------------------------------------------------
|
||||
* OpenAES License
|
||||
* ---------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, Nabil S. Al Ramli, www.nalramli.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _OAES_COMMON_H
|
||||
#define _OAES_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef OAES_SHARED
|
||||
# ifdef oaes_lib_EXPORTS
|
||||
# define OAES_API __declspec(dllexport)
|
||||
# else
|
||||
# define OAES_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define OAES_API
|
||||
# endif
|
||||
#else
|
||||
# define OAES_API
|
||||
#endif // WIN32
|
||||
|
||||
#define OAES_VERSION "0.9.0"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OAES_RET_FIRST = 0,
|
||||
OAES_RET_SUCCESS = 0,
|
||||
OAES_RET_ERROR,
|
||||
OAES_RET_ARG1,
|
||||
OAES_RET_ARG2,
|
||||
OAES_RET_ARG3,
|
||||
OAES_RET_ARG4,
|
||||
OAES_RET_ARG5,
|
||||
OAES_RET_NOKEY,
|
||||
OAES_RET_MEM,
|
||||
OAES_RET_BUF,
|
||||
OAES_RET_HEADER,
|
||||
OAES_RET_COUNT
|
||||
} OAES_RET;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _OAES_COMMON_H
|
||||
46
media/gmp-clearkey/0.1/openaes/oaes_config.h
Normal file
46
media/gmp-clearkey/0.1/openaes/oaes_config.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* ---------------------------------------------------------------------------
|
||||
* OpenAES License
|
||||
* ---------------------------------------------------------------------------
|
||||
* Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _OAES_CONFIG_H
|
||||
#define _OAES_CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef OAES_DEBUG
|
||||
#define OAES_DEBUG 0
|
||||
#endif // OAES_DEBUG
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _OAES_CONFIG_H
|
||||
1393
media/gmp-clearkey/0.1/openaes/oaes_lib.c
Normal file
1393
media/gmp-clearkey/0.1/openaes/oaes_lib.c
Normal file
File diff suppressed because it is too large
Load diff
168
media/gmp-clearkey/0.1/openaes/oaes_lib.h
Normal file
168
media/gmp-clearkey/0.1/openaes/oaes_lib.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* ---------------------------------------------------------------------------
|
||||
* OpenAES License
|
||||
* ---------------------------------------------------------------------------
|
||||
* Copyright (c) 2013, Nabil S. Al Ramli, www.nalramli.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _OAES_LIB_H
|
||||
#define _OAES_LIB_H
|
||||
|
||||
#include "oaes_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef OAES_SHARED
|
||||
# ifdef oaes_lib_EXPORTS
|
||||
# define OAES_API __declspec(dllexport)
|
||||
# else
|
||||
# define OAES_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define OAES_API
|
||||
# endif
|
||||
#else
|
||||
# define OAES_API
|
||||
#endif // WIN32
|
||||
|
||||
#define OAES_BLOCK_SIZE 16
|
||||
|
||||
typedef void OAES_CTX;
|
||||
|
||||
/*
|
||||
* oaes_set_option() takes one of these values for its [option] parameter
|
||||
* some options accept either an optional or a required [value] parameter
|
||||
*/
|
||||
// no option
|
||||
#define OAES_OPTION_NONE 0
|
||||
// enable ECB mode, disable CBC mode
|
||||
#define OAES_OPTION_ECB 1
|
||||
// enable CBC mode, disable ECB mode
|
||||
// value is optional, may pass uint8_t iv[OAES_BLOCK_SIZE] to specify
|
||||
// the value of the initialization vector, iv
|
||||
#define OAES_OPTION_CBC 2
|
||||
|
||||
#ifdef OAES_DEBUG
|
||||
typedef int ( * oaes_step_cb ) (
|
||||
const uint8_t state[OAES_BLOCK_SIZE],
|
||||
const char * step_name,
|
||||
int step_count,
|
||||
void * user_data );
|
||||
// enable state stepping mode
|
||||
// value is required, must pass oaes_step_cb to receive the state at each step
|
||||
#define OAES_OPTION_STEP_ON 4
|
||||
// disable state stepping mode
|
||||
#define OAES_OPTION_STEP_OFF 8
|
||||
#endif // OAES_DEBUG
|
||||
|
||||
typedef uint16_t OAES_OPTION;
|
||||
|
||||
/*
|
||||
* // usage:
|
||||
*
|
||||
* OAES_CTX * ctx = oaes_alloc();
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* {
|
||||
* oaes_gen_key_xxx( ctx );
|
||||
* {
|
||||
* oaes_key_export( ctx, _buf, &_buf_len );
|
||||
* // or
|
||||
* oaes_key_export_data( ctx, _buf, &_buf_len );\
|
||||
* }
|
||||
* }
|
||||
* // or
|
||||
* {
|
||||
* oaes_key_import( ctx, _buf, _buf_len );
|
||||
* // or
|
||||
* oaes_key_import_data( ctx, _buf, _buf_len );
|
||||
* }
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* oaes_encrypt( ctx, m, m_len, c, &c_len );
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* oaes_decrypt( ctx, c, c_len, m, &m_len );
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* oaes_free( &ctx );
|
||||
*/
|
||||
|
||||
OAES_API OAES_CTX * oaes_alloc();
|
||||
|
||||
OAES_API OAES_RET oaes_free( OAES_CTX ** ctx );
|
||||
|
||||
OAES_API OAES_RET oaes_set_option( OAES_CTX * ctx,
|
||||
OAES_OPTION option, const void * value );
|
||||
|
||||
OAES_API OAES_RET oaes_key_gen_128( OAES_CTX * ctx );
|
||||
|
||||
OAES_API OAES_RET oaes_key_gen_192( OAES_CTX * ctx );
|
||||
|
||||
OAES_API OAES_RET oaes_key_gen_256( OAES_CTX * ctx );
|
||||
|
||||
// export key with header information
|
||||
// set data == NULL to get the required data_len
|
||||
OAES_API OAES_RET oaes_key_export( OAES_CTX * ctx,
|
||||
uint8_t * data, size_t * data_len );
|
||||
|
||||
// directly export the data from key
|
||||
// set data == NULL to get the required data_len
|
||||
OAES_API OAES_RET oaes_key_export_data( OAES_CTX * ctx,
|
||||
uint8_t * data, size_t * data_len );
|
||||
|
||||
// import key with header information
|
||||
OAES_API OAES_RET oaes_key_import( OAES_CTX * ctx,
|
||||
const uint8_t * data, size_t data_len );
|
||||
|
||||
// directly import data into key
|
||||
OAES_API OAES_RET oaes_key_import_data( OAES_CTX * ctx,
|
||||
const uint8_t * data, size_t data_len );
|
||||
|
||||
// set c == NULL to get the required c_len
|
||||
OAES_API OAES_RET oaes_encrypt( OAES_CTX * ctx,
|
||||
const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len );
|
||||
|
||||
// set m == NULL to get the required m_len
|
||||
OAES_API OAES_RET oaes_decrypt( OAES_CTX * ctx,
|
||||
const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len );
|
||||
|
||||
// set buf == NULL to get the required buf_len
|
||||
OAES_API OAES_RET oaes_sprintf(
|
||||
char * buf, size_t * buf_len, const uint8_t * data, size_t data_len );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _OAES_LIB_H
|
||||
57
media/gmp-clearkey/0.1/openaes/standard.h
Normal file
57
media/gmp-clearkey/0.1/openaes/standard.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
------------------------------------------------------------------------------
|
||||
Standard definitions and types, Bob Jenkins
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef STANDARD
|
||||
# define STANDARD
|
||||
# ifndef STDIO
|
||||
# include <stdio.h>
|
||||
# define STDIO
|
||||
# endif
|
||||
# ifndef STDDEF
|
||||
# include <stddef.h>
|
||||
# define STDDEF
|
||||
# endif
|
||||
typedef unsigned long long ub8;
|
||||
#define UB8MAXVAL 0xffffffffffffffffLL
|
||||
#define UB8BITS 64
|
||||
typedef signed long long sb8;
|
||||
#define SB8MAXVAL 0x7fffffffffffffffLL
|
||||
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
|
||||
#define UB4MAXVAL 0xffffffff
|
||||
typedef signed long int sb4;
|
||||
#define UB4BITS 32
|
||||
#define SB4MAXVAL 0x7fffffff
|
||||
typedef unsigned short int ub2;
|
||||
#define UB2MAXVAL 0xffff
|
||||
#define UB2BITS 16
|
||||
typedef signed short int sb2;
|
||||
#define SB2MAXVAL 0x7fff
|
||||
typedef unsigned char ub1;
|
||||
#define UB1MAXVAL 0xff
|
||||
#define UB1BITS 8
|
||||
typedef signed char sb1; /* signed 1-byte quantities */
|
||||
#define SB1MAXVAL 0x7f
|
||||
typedef int word; /* fastest type available */
|
||||
|
||||
#define bis(target,mask) ((target) |= (mask))
|
||||
#define bic(target,mask) ((target) &= ~(mask))
|
||||
#define bit(target,mask) ((target) & (mask))
|
||||
// #ifndef min
|
||||
// # define min(a,b) (((a)<(b)) ? (a) : (b))
|
||||
// #endif /* min */
|
||||
// #ifndef max
|
||||
// # define max(a,b) (((a)<(b)) ? (b) : (a))
|
||||
// #endif /* max */
|
||||
#ifndef align
|
||||
# define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1)))
|
||||
#endif /* align */
|
||||
// #ifndef abs
|
||||
// # define abs(a) (((a)>0) ? (a) : -(a))
|
||||
// #endif
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define SUCCESS 0 /* 1 on VAX */
|
||||
|
||||
#endif /* STANDARD */
|
||||
Loading…
Add table
Add a link
Reference in a new issue