diff options
Diffstat (limited to 'media/gmp-clearkey')
45 files changed, 9182 insertions, 0 deletions
diff --git a/media/gmp-clearkey/0.1/AnnexB.cpp b/media/gmp-clearkey/0.1/AnnexB.cpp new file mode 100644 index 000000000..952936deb --- /dev/null +++ b/media/gmp-clearkey/0.1/AnnexB.cpp @@ -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); +} diff --git a/media/gmp-clearkey/0.1/AnnexB.h b/media/gmp-clearkey/0.1/AnnexB.h new file mode 100644 index 000000000..81ca87e0c --- /dev/null +++ b/media/gmp-clearkey/0.1/AnnexB.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ArrayUtils.h b/media/gmp-clearkey/0.1/ArrayUtils.h new file mode 100644 index 000000000..c5e17689e --- /dev/null +++ b/media/gmp-clearkey/0.1/ArrayUtils.h @@ -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 diff --git a/media/gmp-clearkey/0.1/AudioDecoder.cpp b/media/gmp-clearkey/0.1/AudioDecoder.cpp new file mode 100644 index 000000000..b02e1a854 --- /dev/null +++ b/media/gmp-clearkey/0.1/AudioDecoder.cpp @@ -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)); +} diff --git a/media/gmp-clearkey/0.1/AudioDecoder.h b/media/gmp-clearkey/0.1/AudioDecoder.h new file mode 100644 index 000000000..98915bb7a --- /dev/null +++ b/media/gmp-clearkey/0.1/AudioDecoder.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/BigEndian.h b/media/gmp-clearkey/0.1/BigEndian.h new file mode 100644 index 000000000..853cd4a02 --- /dev/null +++ b/media/gmp-clearkey/0.1/BigEndian.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.cpp b/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.cpp new file mode 100644 index 000000000..ed1cac738 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.cpp @@ -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)); +} diff --git a/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h b/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h new file mode 100644 index 000000000..b828aae41 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ClearKeyBase64.cpp b/media/gmp-clearkey/0.1/ClearKeyBase64.cpp new file mode 100644 index 000000000..c4c530a34 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyBase64.cpp @@ -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; +}
\ No newline at end of file diff --git a/media/gmp-clearkey/0.1/ClearKeyBase64.h b/media/gmp-clearkey/0.1/ClearKeyBase64.h new file mode 100644 index 000000000..c0c928813 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyBase64.h @@ -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 diff --git a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp new file mode 100644 index 000000000..87b316563 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp @@ -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; +} diff --git a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h new file mode 100644 index 000000000..7a6c9a560 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp new file mode 100644 index 000000000..2e14d822e --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp @@ -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())); +} diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.h b/media/gmp-clearkey/0.1/ClearKeyPersistence.h new file mode 100644 index 000000000..ddab9f783 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ClearKeySession.cpp b/media/gmp-clearkey/0.1/ClearKeySession.cpp new file mode 100644 index 000000000..fb45dd2eb --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp @@ -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); +} diff --git a/media/gmp-clearkey/0.1/ClearKeySession.h b/media/gmp-clearkey/0.1/ClearKeySession.h new file mode 100644 index 000000000..2783b2253 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeySession.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp new file mode 100644 index 000000000..4dbb06299 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp @@ -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(); +} diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.h b/media/gmp-clearkey/0.1/ClearKeySessionManager.h new file mode 100644 index 000000000..041b3f036 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ClearKeyStorage.cpp b/media/gmp-clearkey/0.1/ClearKeyStorage.cpp new file mode 100644 index 000000000..0db51c9b7 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyStorage.cpp @@ -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); +} diff --git a/media/gmp-clearkey/0.1/ClearKeyStorage.h b/media/gmp-clearkey/0.1/ClearKeyStorage.h new file mode 100644 index 000000000..695f67d9e --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyStorage.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp new file mode 100644 index 000000000..b99a50a4d --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp @@ -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; +} diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.h b/media/gmp-clearkey/0.1/ClearKeyUtils.h new file mode 100644 index 000000000..fc29b9211 --- /dev/null +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/RefCounted.h b/media/gmp-clearkey/0.1/RefCounted.h new file mode 100644 index 000000000..6da694295 --- /dev/null +++ b/media/gmp-clearkey/0.1/RefCounted.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/VideoDecoder.cpp b/media/gmp-clearkey/0.1/VideoDecoder.cpp new file mode 100644 index 000000000..a3f25402b --- /dev/null +++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp @@ -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)); +} diff --git a/media/gmp-clearkey/0.1/VideoDecoder.h b/media/gmp-clearkey/0.1/VideoDecoder.h new file mode 100644 index 000000000..cf021efd7 --- /dev/null +++ b/media/gmp-clearkey/0.1/VideoDecoder.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/WMFAACDecoder.cpp b/media/gmp-clearkey/0.1/WMFAACDecoder.cpp new file mode 100644 index 000000000..495f840e8 --- /dev/null +++ b/media/gmp-clearkey/0.1/WMFAACDecoder.cpp @@ -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; +} + +} diff --git a/media/gmp-clearkey/0.1/WMFAACDecoder.h b/media/gmp-clearkey/0.1/WMFAACDecoder.h new file mode 100644 index 000000000..3099eeea2 --- /dev/null +++ b/media/gmp-clearkey/0.1/WMFAACDecoder.h @@ -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 diff --git a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp new file mode 100644 index 000000000..8a3ed4fff --- /dev/null +++ b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp @@ -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 diff --git a/media/gmp-clearkey/0.1/WMFH264Decoder.h b/media/gmp-clearkey/0.1/WMFH264Decoder.h new file mode 100644 index 000000000..91b7e046f --- /dev/null +++ b/media/gmp-clearkey/0.1/WMFH264Decoder.h @@ -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 diff --git a/media/gmp-clearkey/0.1/WMFSymbols.h b/media/gmp-clearkey/0.1/WMFSymbols.h new file mode 100644 index 000000000..60a0a6854 --- /dev/null +++ b/media/gmp-clearkey/0.1/WMFSymbols.h @@ -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"); diff --git a/media/gmp-clearkey/0.1/WMFUtils.cpp b/media/gmp-clearkey/0.1/WMFUtils.cpp new file mode 100644 index 000000000..cb3cfe9fc --- /dev/null +++ b/media/gmp-clearkey/0.1/WMFUtils.cpp @@ -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 diff --git a/media/gmp-clearkey/0.1/WMFUtils.h b/media/gmp-clearkey/0.1/WMFUtils.h new file mode 100644 index 000000000..51d46d70d --- /dev/null +++ b/media/gmp-clearkey/0.1/WMFUtils.h @@ -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__ diff --git a/media/gmp-clearkey/0.1/clearkey.info.in b/media/gmp-clearkey/0.1/clearkey.info.in new file mode 100644 index 000000000..8b3445d59 --- /dev/null +++ b/media/gmp-clearkey/0.1/clearkey.info.in @@ -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 diff --git a/media/gmp-clearkey/0.1/gmp-clearkey.cpp b/media/gmp-clearkey/0.1/gmp-clearkey.cpp new file mode 100644 index 000000000..fae7ce9f8 --- /dev/null +++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp @@ -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; +} + +} diff --git a/media/gmp-clearkey/0.1/gmp-task-utils-generated.h b/media/gmp-clearkey/0.1/gmp-task-utils-generated.h new file mode 100644 index 000000000..597ed4721 --- /dev/null +++ b/media/gmp-clearkey/0.1/gmp-task-utils-generated.h @@ -0,0 +1,1938 @@ +/* + * 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 "RefCounted.h" + +// 0 arguments -- +template<typename M> class gmp_task_args_nm_0 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_0(M m) : + m_(m) {} + + void Run() { + m_(); + } + + private: + M m_; +}; + + + +// 0 arguments -- +template<typename M, typename R> class gmp_task_args_nm_0_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_0_ret(M m, R *r) : + m_(m), r_(r) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(); + } + + private: + M m_; + R* r_; +}; + + + +// 0 arguments -- +template<typename C, typename M> class gmp_task_args_m_0 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_0(C o, M m) : + o_(o), m_(m) {} + + void Run() { + ((*o_).*m_)(); + } + + private: + C o_; + M m_; +}; + + + +// 0 arguments -- +template<typename C, typename M, typename R> class gmp_task_args_m_0_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_0_ret(C o, M m, R *r) : + o_(o), m_(m), r_(r) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(); + } + + private: + C o_; + M m_; + R* r_; +}; + + + +// 1 arguments -- +template<typename M, typename A0> class gmp_task_args_nm_1 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_1(M m, A0 a0) : + m_(m), a0_(a0) {} + + void Run() { + m_(a0_); + } + + private: + M m_; + A0 a0_; +}; + + + +// 1 arguments -- +template<typename M, typename A0, typename R> class gmp_task_args_nm_1_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_1_ret(M m, A0 a0, R *r) : + m_(m), r_(r), a0_(a0) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_); + } + + private: + M m_; + R* r_; + A0 a0_; +}; + + + +// 1 arguments -- +template<typename C, typename M, typename A0> class gmp_task_args_m_1 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_1(C o, M m, A0 a0) : + o_(o), m_(m), a0_(a0) {} + + void Run() { + ((*o_).*m_)(a0_); + } + + private: + C o_; + M m_; + A0 a0_; +}; + + + +// 1 arguments -- +template<typename C, typename M, typename A0, typename R> class gmp_task_args_m_1_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_1_ret(C o, M m, A0 a0, R *r) : + o_(o), m_(m), r_(r), a0_(a0) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; +}; + + + +// 2 arguments -- +template<typename M, typename A0, typename A1> class gmp_task_args_nm_2 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_2(M m, A0 a0, A1 a1) : + m_(m), a0_(a0), a1_(a1) {} + + void Run() { + m_(a0_, a1_); + } + + private: + M m_; + A0 a0_; + A1 a1_; +}; + + + +// 2 arguments -- +template<typename M, typename A0, typename A1, typename R> class gmp_task_args_nm_2_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_2_ret(M m, A0 a0, A1 a1, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; +}; + + + +// 2 arguments -- +template<typename C, typename M, typename A0, typename A1> class gmp_task_args_m_2 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_2(C o, M m, A0 a0, A1 a1) : + o_(o), m_(m), a0_(a0), a1_(a1) {} + + void Run() { + ((*o_).*m_)(a0_, a1_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; +}; + + + +// 2 arguments -- +template<typename C, typename M, typename A0, typename A1, typename R> class gmp_task_args_m_2_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_2_ret(C o, M m, A0 a0, A1 a1, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; +}; + + + +// 3 arguments -- +template<typename M, typename A0, typename A1, typename A2> class gmp_task_args_nm_3 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_3(M m, A0 a0, A1 a1, A2 a2) : + m_(m), a0_(a0), a1_(a1), a2_(a2) {} + + void Run() { + m_(a0_, a1_, a2_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; +}; + + + +// 3 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename R> class gmp_task_args_nm_3_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_3_ret(M m, A0 a0, A1 a1, A2 a2, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; +}; + + + +// 3 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2> class gmp_task_args_m_3 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_3(C o, M m, A0 a0, A1 a1, A2 a2) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; +}; + + + +// 3 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename R> class gmp_task_args_m_3_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_3_ret(C o, M m, A0 a0, A1 a1, A2 a2, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; +}; + + + +// 4 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3> class gmp_task_args_nm_4 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_4(M m, A0 a0, A1 a1, A2 a2, A3 a3) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3) {} + + void Run() { + m_(a0_, a1_, a2_, a3_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; +}; + + + +// 4 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename R> class gmp_task_args_nm_4_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_4_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; +}; + + + +// 4 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3> class gmp_task_args_m_4 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_4(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; +}; + + + +// 4 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R> class gmp_task_args_m_4_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_4_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; +}; + + + +// 5 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4> class gmp_task_args_nm_5 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_5(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; +}; + + + +// 5 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> class gmp_task_args_nm_5_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_5_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; +}; + + + +// 5 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4> class gmp_task_args_m_5 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_5(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; +}; + + + +// 5 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> class gmp_task_args_m_5_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_5_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; +}; + + + +// 6 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> class gmp_task_args_nm_6 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_6(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; +}; + + + +// 6 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> class gmp_task_args_nm_6_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_6_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; +}; + + + +// 6 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> class gmp_task_args_m_6 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_6(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; +}; + + + +// 6 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> class gmp_task_args_m_6_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_6_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; +}; + + + +// 7 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> class gmp_task_args_nm_7 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_7(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; +}; + + + +// 7 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> class gmp_task_args_nm_7_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_7_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; +}; + + + +// 7 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> class gmp_task_args_m_7 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_7(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; +}; + + + +// 7 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> class gmp_task_args_m_7_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_7_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; +}; + + + +// 8 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> class gmp_task_args_nm_8 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_8(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; +}; + + + +// 8 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> class gmp_task_args_nm_8_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_8_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; +}; + + + +// 8 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> class gmp_task_args_m_8 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_8(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; +}; + + + +// 8 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> class gmp_task_args_m_8_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_8_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; +}; + + + +// 9 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> class gmp_task_args_nm_9 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_9(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; +}; + + + +// 9 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> class gmp_task_args_nm_9_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_9_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; +}; + + + +// 9 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> class gmp_task_args_m_9 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_9(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; +}; + + + +// 9 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> class gmp_task_args_m_9_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_9_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; +}; + + + +// 10 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> class gmp_task_args_nm_10 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_10(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; +}; + + + +// 10 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> class gmp_task_args_nm_10_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_10_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; +}; + + + +// 10 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> class gmp_task_args_m_10 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_10(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; +}; + + + +// 10 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> class gmp_task_args_m_10_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_10_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; +}; + + + +// 11 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> class gmp_task_args_nm_11 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_11(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; +}; + + + +// 11 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> class gmp_task_args_nm_11_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_11_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; +}; + + + +// 11 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> class gmp_task_args_m_11 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_11(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; +}; + + + +// 11 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> class gmp_task_args_m_11_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_11_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; +}; + + + +// 12 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> class gmp_task_args_nm_12 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_12(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; +}; + + + +// 12 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> class gmp_task_args_nm_12_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_12_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; +}; + + + +// 12 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> class gmp_task_args_m_12 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_12(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; +}; + + + +// 12 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> class gmp_task_args_m_12_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_12_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; +}; + + + +// 13 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> class gmp_task_args_nm_13 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_13(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; +}; + + + +// 13 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> class gmp_task_args_nm_13_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_13_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; +}; + + + +// 13 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> class gmp_task_args_m_13 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_13(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; +}; + + + +// 13 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> class gmp_task_args_m_13_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_13_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; +}; + + + +// 14 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> class gmp_task_args_nm_14 : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_14(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) : + m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13) {} + + void Run() { + m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_); + } + + private: + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; + A13 a13_; +}; + + + +// 14 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> class gmp_task_args_nm_14_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_nm_14_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R *r) : + m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_); + } + + private: + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; + A13 a13_; +}; + + + +// 14 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> class gmp_task_args_m_14 : public gmp_task_args_base { + public: + explicit gmp_task_args_m_14(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) : + o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13) {} + + void Run() { + ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_); + } + + private: + C o_; + M m_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; + A13 a13_; +}; + + + +// 14 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> class gmp_task_args_m_14_ret : public gmp_task_args_base { + public: + explicit gmp_task_args_m_14_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R *r) : + o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13) {} + virtual bool returns_value() const { return true; } + + void Run() { + *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_); + } + + private: + C o_; + M m_; + R* r_; + A0 a0_; + A1 a1_; + A2 a2_; + A3 a3_; + A4 a4_; + A5 a5_; + A6 a6_; + A7 a7_; + A8 a8_; + A9 a9_; + A10 a10_; + A11 a11_; + A12 a12_; + A13 a13_; +}; + + + + + + +// 0 arguments -- +template<typename M> +gmp_task_args_nm_0<M>* WrapTaskNM(M m) { + return new gmp_task_args_nm_0<M> + (m); +} + +// 0 arguments -- +template<typename M, typename R> +gmp_task_args_nm_0_ret<M, R>* WrapTaskNMRet(M m, R* r) { + return new gmp_task_args_nm_0_ret<M, R> + (m, r); +} + +// 0 arguments -- +template<typename C, typename M> +gmp_task_args_m_0<C, M>* WrapTask(C o, M m) { + return new gmp_task_args_m_0<C, M> + (o, m); +} + +// 0 arguments -- +template<typename C, typename M, typename R> +gmp_task_args_m_0_ret<C, M, R>* WrapTaskRet(C o, M m, R* r) { + return new gmp_task_args_m_0_ret<C, M, R> + (o, m, r); +} + +// 1 arguments -- +template<typename M, typename A0> +gmp_task_args_nm_1<M, A0>* WrapTaskNM(M m, A0 a0) { + return new gmp_task_args_nm_1<M, A0> + (m, a0); +} + +// 1 arguments -- +template<typename M, typename A0, typename R> +gmp_task_args_nm_1_ret<M, A0, R>* WrapTaskNMRet(M m, A0 a0, R* r) { + return new gmp_task_args_nm_1_ret<M, A0, R> + (m, a0, r); +} + +// 1 arguments -- +template<typename C, typename M, typename A0> +gmp_task_args_m_1<C, M, A0>* WrapTask(C o, M m, A0 a0) { + return new gmp_task_args_m_1<C, M, A0> + (o, m, a0); +} + +// 1 arguments -- +template<typename C, typename M, typename A0, typename R> +gmp_task_args_m_1_ret<C, M, A0, R>* WrapTaskRet(C o, M m, A0 a0, R* r) { + return new gmp_task_args_m_1_ret<C, M, A0, R> + (o, m, a0, r); +} + +// 2 arguments -- +template<typename M, typename A0, typename A1> +gmp_task_args_nm_2<M, A0, A1>* WrapTaskNM(M m, A0 a0, A1 a1) { + return new gmp_task_args_nm_2<M, A0, A1> + (m, a0, a1); +} + +// 2 arguments -- +template<typename M, typename A0, typename A1, typename R> +gmp_task_args_nm_2_ret<M, A0, A1, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, R* r) { + return new gmp_task_args_nm_2_ret<M, A0, A1, R> + (m, a0, a1, r); +} + +// 2 arguments -- +template<typename C, typename M, typename A0, typename A1> +gmp_task_args_m_2<C, M, A0, A1>* WrapTask(C o, M m, A0 a0, A1 a1) { + return new gmp_task_args_m_2<C, M, A0, A1> + (o, m, a0, a1); +} + +// 2 arguments -- +template<typename C, typename M, typename A0, typename A1, typename R> +gmp_task_args_m_2_ret<C, M, A0, A1, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, R* r) { + return new gmp_task_args_m_2_ret<C, M, A0, A1, R> + (o, m, a0, a1, r); +} + +// 3 arguments -- +template<typename M, typename A0, typename A1, typename A2> +gmp_task_args_nm_3<M, A0, A1, A2>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2) { + return new gmp_task_args_nm_3<M, A0, A1, A2> + (m, a0, a1, a2); +} + +// 3 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename R> +gmp_task_args_nm_3_ret<M, A0, A1, A2, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, R* r) { + return new gmp_task_args_nm_3_ret<M, A0, A1, A2, R> + (m, a0, a1, a2, r); +} + +// 3 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2> +gmp_task_args_m_3<C, M, A0, A1, A2>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2) { + return new gmp_task_args_m_3<C, M, A0, A1, A2> + (o, m, a0, a1, a2); +} + +// 3 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename R> +gmp_task_args_m_3_ret<C, M, A0, A1, A2, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, R* r) { + return new gmp_task_args_m_3_ret<C, M, A0, A1, A2, R> + (o, m, a0, a1, a2, r); +} + +// 4 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3> +gmp_task_args_nm_4<M, A0, A1, A2, A3>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3) { + return new gmp_task_args_nm_4<M, A0, A1, A2, A3> + (m, a0, a1, a2, a3); +} + +// 4 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename R> +gmp_task_args_nm_4_ret<M, A0, A1, A2, A3, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, R* r) { + return new gmp_task_args_nm_4_ret<M, A0, A1, A2, A3, R> + (m, a0, a1, a2, a3, r); +} + +// 4 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3> +gmp_task_args_m_4<C, M, A0, A1, A2, A3>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) { + return new gmp_task_args_m_4<C, M, A0, A1, A2, A3> + (o, m, a0, a1, a2, a3); +} + +// 4 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R> +gmp_task_args_m_4_ret<C, M, A0, A1, A2, A3, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R* r) { + return new gmp_task_args_m_4_ret<C, M, A0, A1, A2, A3, R> + (o, m, a0, a1, a2, a3, r); +} + +// 5 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4> +gmp_task_args_nm_5<M, A0, A1, A2, A3, A4>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return new gmp_task_args_nm_5<M, A0, A1, A2, A3, A4> + (m, a0, a1, a2, a3, a4); +} + +// 5 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> +gmp_task_args_nm_5_ret<M, A0, A1, A2, A3, A4, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R* r) { + return new gmp_task_args_nm_5_ret<M, A0, A1, A2, A3, A4, R> + (m, a0, a1, a2, a3, a4, r); +} + +// 5 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4> +gmp_task_args_m_5<C, M, A0, A1, A2, A3, A4>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return new gmp_task_args_m_5<C, M, A0, A1, A2, A3, A4> + (o, m, a0, a1, a2, a3, a4); +} + +// 5 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> +gmp_task_args_m_5_ret<C, M, A0, A1, A2, A3, A4, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R* r) { + return new gmp_task_args_m_5_ret<C, M, A0, A1, A2, A3, A4, R> + (o, m, a0, a1, a2, a3, a4, r); +} + +// 6 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> +gmp_task_args_nm_6<M, A0, A1, A2, A3, A4, A5>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + return new gmp_task_args_nm_6<M, A0, A1, A2, A3, A4, A5> + (m, a0, a1, a2, a3, a4, a5); +} + +// 6 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> +gmp_task_args_nm_6_ret<M, A0, A1, A2, A3, A4, A5, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R* r) { + return new gmp_task_args_nm_6_ret<M, A0, A1, A2, A3, A4, A5, R> + (m, a0, a1, a2, a3, a4, a5, r); +} + +// 6 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> +gmp_task_args_m_6<C, M, A0, A1, A2, A3, A4, A5>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + return new gmp_task_args_m_6<C, M, A0, A1, A2, A3, A4, A5> + (o, m, a0, a1, a2, a3, a4, a5); +} + +// 6 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> +gmp_task_args_m_6_ret<C, M, A0, A1, A2, A3, A4, A5, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R* r) { + return new gmp_task_args_m_6_ret<C, M, A0, A1, A2, A3, A4, A5, R> + (o, m, a0, a1, a2, a3, a4, a5, r); +} + +// 7 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> +gmp_task_args_nm_7<M, A0, A1, A2, A3, A4, A5, A6>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + return new gmp_task_args_nm_7<M, A0, A1, A2, A3, A4, A5, A6> + (m, a0, a1, a2, a3, a4, a5, a6); +} + +// 7 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> +gmp_task_args_nm_7_ret<M, A0, A1, A2, A3, A4, A5, A6, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R* r) { + return new gmp_task_args_nm_7_ret<M, A0, A1, A2, A3, A4, A5, A6, R> + (m, a0, a1, a2, a3, a4, a5, a6, r); +} + +// 7 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> +gmp_task_args_m_7<C, M, A0, A1, A2, A3, A4, A5, A6>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + return new gmp_task_args_m_7<C, M, A0, A1, A2, A3, A4, A5, A6> + (o, m, a0, a1, a2, a3, a4, a5, a6); +} + +// 7 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> +gmp_task_args_m_7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R* r) { + return new gmp_task_args_m_7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, r); +} + +// 8 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> +gmp_task_args_nm_8<M, A0, A1, A2, A3, A4, A5, A6, A7>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { + return new gmp_task_args_nm_8<M, A0, A1, A2, A3, A4, A5, A6, A7> + (m, a0, a1, a2, a3, a4, a5, a6, a7); +} + +// 8 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> +gmp_task_args_nm_8_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R* r) { + return new gmp_task_args_nm_8_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, R> + (m, a0, a1, a2, a3, a4, a5, a6, a7, r); +} + +// 8 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> +gmp_task_args_m_8<C, M, A0, A1, A2, A3, A4, A5, A6, A7>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { + return new gmp_task_args_m_8<C, M, A0, A1, A2, A3, A4, A5, A6, A7> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7); +} + +// 8 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> +gmp_task_args_m_8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R* r) { + return new gmp_task_args_m_8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, r); +} + +// 9 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> +gmp_task_args_nm_9<M, A0, A1, A2, A3, A4, A5, A6, A7, A8>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { + return new gmp_task_args_nm_9<M, A0, A1, A2, A3, A4, A5, A6, A7, A8> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8); +} + +// 9 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> +gmp_task_args_nm_9_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R* r) { + return new gmp_task_args_nm_9_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, r); +} + +// 9 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> +gmp_task_args_m_9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { + return new gmp_task_args_m_9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8); +} + +// 9 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> +gmp_task_args_m_9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R* r) { + return new gmp_task_args_m_9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, r); +} + +// 10 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> +gmp_task_args_nm_10<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { + return new gmp_task_args_nm_10<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); +} + +// 10 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> +gmp_task_args_nm_10_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R* r) { + return new gmp_task_args_nm_10_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, r); +} + +// 10 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> +gmp_task_args_m_10<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { + return new gmp_task_args_m_10<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); +} + +// 10 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> +gmp_task_args_m_10_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R* r) { + return new gmp_task_args_m_10_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, r); +} + +// 11 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> +gmp_task_args_nm_11<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { + return new gmp_task_args_nm_11<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); +} + +// 11 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> +gmp_task_args_nm_11_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R* r) { + return new gmp_task_args_nm_11_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, r); +} + +// 11 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> +gmp_task_args_m_11<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { + return new gmp_task_args_m_11<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); +} + +// 11 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> +gmp_task_args_m_11_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R* r) { + return new gmp_task_args_m_11_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, r); +} + +// 12 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> +gmp_task_args_nm_12<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { + return new gmp_task_args_nm_12<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); +} + +// 12 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> +gmp_task_args_nm_12_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R* r) { + return new gmp_task_args_nm_12_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, r); +} + +// 12 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> +gmp_task_args_m_12<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) { + return new gmp_task_args_m_12<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); +} + +// 12 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> +gmp_task_args_m_12_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R* r) { + return new gmp_task_args_m_12_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, r); +} + +// 13 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> +gmp_task_args_nm_13<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { + return new gmp_task_args_nm_13<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); +} + +// 13 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> +gmp_task_args_nm_13_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R* r) { + return new gmp_task_args_nm_13_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, r); +} + +// 13 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> +gmp_task_args_m_13<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) { + return new gmp_task_args_m_13<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); +} + +// 13 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> +gmp_task_args_m_13_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R* r) { + return new gmp_task_args_m_13_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, r); +} + +// 14 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> +gmp_task_args_nm_14<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { + return new gmp_task_args_nm_14<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); +} + +// 14 arguments -- +template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> +gmp_task_args_nm_14_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R* r) { + return new gmp_task_args_nm_14_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R> + (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, r); +} + +// 14 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> +gmp_task_args_m_14<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) { + return new gmp_task_args_m_14<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13); +} + +// 14 arguments -- +template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> +gmp_task_args_m_14_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R* r) { + return new gmp_task_args_m_14_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R> + (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, r); +} + +class RefCountTaskWrapper : public gmp_task_args_base { +public: + RefCountTaskWrapper(GMPTask* aTask, RefCounted* aRefCounted) + : mTask(aTask) + , mRefCounted(aRefCounted) + {} + virtual void Run() override { + mTask->Run(); + } + virtual void Destroy() override { + mTask->Destroy(); + gmp_task_args_base::Destroy(); + } +private: + ~RefCountTaskWrapper() {} + + GMPTask* mTask; + RefPtr<RefCounted> mRefCounted; +}; + +template<typename Type, typename Method, typename... Args> +GMPTask* +WrapTaskRefCounted(Type* aType, Method aMethod, Args&&... args) +{ + GMPTask* t = WrapTask(aType, aMethod, std::forward<Args>(args)...); + return new RefCountTaskWrapper(t, aType); +} diff --git a/media/gmp-clearkey/0.1/gmp-task-utils.h b/media/gmp-clearkey/0.1/gmp-task-utils.h new file mode 100644 index 000000000..82e08373f --- /dev/null +++ b/media/gmp-clearkey/0.1/gmp-task-utils.h @@ -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_ diff --git a/media/gmp-clearkey/0.1/gtest/TestClearKeyUtils.cpp b/media/gmp-clearkey/0.1/gtest/TestClearKeyUtils.cpp new file mode 100644 index 000000000..5ff85970e --- /dev/null +++ b/media/gmp-clearkey/0.1/gtest/TestClearKeyUtils.cpp @@ -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]); + } + } + } +} diff --git a/media/gmp-clearkey/0.1/gtest/moz.build b/media/gmp-clearkey/0.1/gtest/moz.build new file mode 100644 index 000000000..52d1df6f9 --- /dev/null +++ b/media/gmp-clearkey/0.1/gtest/moz.build @@ -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 += [ + '..', +] diff --git a/media/gmp-clearkey/0.1/moz.build b/media/gmp-clearkey/0.1/moz.build new file mode 100644 index 000000000..b6a03168f --- /dev/null +++ b/media/gmp-clearkey/0.1/moz.build @@ -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 + ] diff --git a/media/gmp-clearkey/0.1/openaes/LICENSE b/media/gmp-clearkey/0.1/openaes/LICENSE new file mode 100644 index 000000000..d824e13dd --- /dev/null +++ b/media/gmp-clearkey/0.1/openaes/LICENSE @@ -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. +--------------------------------------------------------------------------- diff --git a/media/gmp-clearkey/0.1/openaes/oaes_common.h b/media/gmp-clearkey/0.1/openaes/oaes_common.h new file mode 100644 index 000000000..7e2bf513f --- /dev/null +++ b/media/gmp-clearkey/0.1/openaes/oaes_common.h @@ -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 diff --git a/media/gmp-clearkey/0.1/openaes/oaes_config.h b/media/gmp-clearkey/0.1/openaes/oaes_config.h new file mode 100644 index 000000000..cb9f4e7be --- /dev/null +++ b/media/gmp-clearkey/0.1/openaes/oaes_config.h @@ -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 diff --git a/media/gmp-clearkey/0.1/openaes/oaes_lib.c b/media/gmp-clearkey/0.1/openaes/oaes_lib.c new file mode 100644 index 000000000..aa13623ce --- /dev/null +++ b/media/gmp-clearkey/0.1/openaes/oaes_lib.c @@ -0,0 +1,1393 @@ +/* + * --------------------------------------------------------------------------- + * 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. + * --------------------------------------------------------------------------- + */ + +#include <stdlib.h> +#include <stddef.h> +#include <time.h> +#include <string.h> + +#include "mozilla/Sprintf.h" + +#ifdef WIN32 +#include <process.h> +#endif + +#include "oaes_config.h" +#include "oaes_lib.h" + +#ifdef OAES_HAVE_ISAAC +#include "rand.h" +#define OAES_RAND(x) rand(x) +#else +#define OAES_RAND(x) rand() +#endif // OAES_HAVE_ISAAC + +#define OAES_RKEY_LEN 4 +#define OAES_COL_LEN 4 +#define OAES_ROUND_BASE 7 + +// the block is padded +#define OAES_FLAG_PAD 0x01 + +#ifndef min +# define min(a,b) (((a)<(b)) ? (a) : (b)) +#endif /* min */ + +typedef struct _oaes_key +{ + size_t data_len; + uint8_t *data; + size_t exp_data_len; + uint8_t *exp_data; + size_t num_keys; + size_t key_base; +} oaes_key; + +typedef struct _oaes_ctx +{ +#ifdef OAES_HAVE_ISAAC + randctx * rctx; +#endif // OAES_HAVE_ISAAC + +#ifdef OAES_DEBUG + oaes_step_cb step_cb; +#endif // OAES_DEBUG + + oaes_key * key; + OAES_OPTION options; + uint8_t iv[OAES_BLOCK_SIZE]; +} oaes_ctx; + +// "OAES<8-bit header version><8-bit type><16-bit options><8-bit flags><56-bit reserved>" +static uint8_t oaes_header[OAES_BLOCK_SIZE] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x4f, 0x41, 0x45, 0x53, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static uint8_t oaes_gf_8[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +static uint8_t oaes_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + /*1*/ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + /*2*/ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + /*3*/ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + /*4*/ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + /*5*/ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + /*6*/ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + /*7*/ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + /*8*/ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + /*9*/ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + /*a*/ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + /*b*/ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + /*c*/ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + /*d*/ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + /*e*/ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + /*f*/ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +}; + +static uint8_t oaes_inv_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + /*1*/ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + /*2*/ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + /*3*/ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + /*4*/ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + /*5*/ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + /*6*/ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + /*7*/ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + /*8*/ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + /*9*/ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + /*a*/ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + /*b*/ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + /*c*/ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + /*d*/ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + /*e*/ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + /*f*/ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, +}; + +static uint8_t oaes_gf_mul_2[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, + /*1*/ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, + /*2*/ 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, + /*3*/ 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, + /*4*/ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, + /*5*/ 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, + /*6*/ 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, + /*7*/ 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, + /*8*/ 0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05, + /*9*/ 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25, + /*a*/ 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45, + /*b*/ 0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65, + /*c*/ 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85, + /*d*/ 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, + /*e*/ 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5, + /*f*/ 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5, +}; + +static uint8_t oaes_gf_mul_3[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, + /*1*/ 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, + /*2*/ 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71, + /*3*/ 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41, + /*4*/ 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, + /*5*/ 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1, + /*6*/ 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1, + /*7*/ 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, + /*8*/ 0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a, + /*9*/ 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba, + /*a*/ 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea, + /*b*/ 0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda, + /*c*/ 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a, + /*d*/ 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a, + /*e*/ 0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a, + /*f*/ 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a, +}; + +static uint8_t oaes_gf_mul_9[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, + /*1*/ 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, + /*2*/ 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, + /*3*/ 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc, + /*4*/ 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01, + /*5*/ 0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91, + /*6*/ 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a, + /*7*/ 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa, + /*8*/ 0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b, + /*9*/ 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b, + /*a*/ 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0, + /*b*/ 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30, + /*c*/ 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed, + /*d*/ 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, + /*e*/ 0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6, + /*f*/ 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46, +}; + +static uint8_t oaes_gf_mul_b[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, + /*1*/ 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, + /*2*/ 0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12, + /*3*/ 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2, + /*4*/ 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f, + /*5*/ 0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f, + /*6*/ 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4, + /*7*/ 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54, + /*8*/ 0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e, + /*9*/ 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e, + /*a*/ 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5, + /*b*/ 0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55, + /*c*/ 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68, + /*d*/ 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, + /*e*/ 0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13, + /*f*/ 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3, +}; + +static uint8_t oaes_gf_mul_d[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, + /*1*/ 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, + /*2*/ 0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0, + /*3*/ 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20, + /*4*/ 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26, + /*5*/ 0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6, + /*6*/ 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d, + /*7*/ 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d, + /*8*/ 0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91, + /*9*/ 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41, + /*a*/ 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a, + /*b*/ 0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa, + /*c*/ 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc, + /*d*/ 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, + /*e*/ 0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47, + /*f*/ 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97, +}; + +static uint8_t oaes_gf_mul_e[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, + /*1*/ 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, + /*2*/ 0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81, + /*3*/ 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61, + /*4*/ 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, + /*5*/ 0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17, + /*6*/ 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c, + /*7*/ 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc, + /*8*/ 0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b, + /*9*/ 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb, + /*a*/ 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0, + /*b*/ 0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20, + /*c*/ 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6, + /*d*/ 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56, + /*e*/ 0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d, + /*f*/ 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d, +}; + +static OAES_RET oaes_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_inv_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_word_rot_left( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + memcpy( _temp, word + 1, OAES_COL_LEN - 1 ); + _temp[OAES_COL_LEN - 1] = word[0]; + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x05]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x0f]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x09]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x03]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x0d]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x07]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x01]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x0b]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x0d]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x07]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x01]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x0b]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x05]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x0f]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x09]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x03]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static uint8_t oaes_gf_mul(uint8_t left, uint8_t right) +{ + size_t _x, _y; + + _x = _y = left; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + + switch( right ) + { + case 0x02: + return oaes_gf_mul_2[_y][_x]; + break; + case 0x03: + return oaes_gf_mul_3[_y][_x]; + break; + case 0x09: + return oaes_gf_mul_9[_y][_x]; + break; + case 0x0b: + return oaes_gf_mul_b[_y][_x]; + break; + case 0x0d: + return oaes_gf_mul_d[_y][_x]; + break; + case 0x0e: + return oaes_gf_mul_e[_y][_x]; + break; + default: + return left; + break; + } +} + +static OAES_RET oaes_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul(word[0], 0x02) ^ oaes_gf_mul( word[1], 0x03 ) ^ + word[2] ^ word[3]; + _temp[1] = word[0] ^ oaes_gf_mul( word[1], 0x02 ) ^ + oaes_gf_mul( word[2], 0x03 ) ^ word[3]; + _temp[2] = word[0] ^ word[1] ^ + oaes_gf_mul( word[2], 0x02 ) ^ oaes_gf_mul( word[3], 0x03 ); + _temp[3] = oaes_gf_mul( word[0], 0x03 ) ^ word[1] ^ + word[2] ^ oaes_gf_mul( word[3], 0x02 ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul( word[0], 0x0e ) ^ oaes_gf_mul( word[1], 0x0b ) ^ + oaes_gf_mul( word[2], 0x0d ) ^ oaes_gf_mul( word[3], 0x09 ); + _temp[1] = oaes_gf_mul( word[0], 0x09 ) ^ oaes_gf_mul( word[1], 0x0e ) ^ + oaes_gf_mul( word[2], 0x0b ) ^ oaes_gf_mul( word[3], 0x0d ); + _temp[2] = oaes_gf_mul( word[0], 0x0d ) ^ oaes_gf_mul( word[1], 0x09 ) ^ + oaes_gf_mul( word[2], 0x0e ) ^ oaes_gf_mul( word[3], 0x0b ); + _temp[3] = oaes_gf_mul( word[0], 0x0b ) ^ oaes_gf_mul( word[1], 0x0d ) ^ + oaes_gf_mul( word[2], 0x09 ) ^ oaes_gf_mul( word[3], 0x0e ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_sprintf( + char * buf, size_t * buf_len, const uint8_t * data, size_t data_len ) +{ + size_t _i, _buf_len_in; + char _temp[4]; + + if( NULL == buf_len ) + return OAES_RET_ARG2; + + _buf_len_in = *buf_len; + *buf_len = data_len * 3 + data_len / OAES_BLOCK_SIZE + 1; + + if( NULL == buf ) + return OAES_RET_SUCCESS; + + if( *buf_len > _buf_len_in ) + return OAES_RET_BUF; + + if( NULL == data ) + return OAES_RET_ARG3; + + strcpy( buf, "" ); + + for( _i = 0; _i < data_len; _i++ ) + { + snprintf( _temp, sizeof(_temp), "%02x ", data[_i] ); + strcat( buf, _temp ); + if( _i && 0 == ( _i + 1 ) % OAES_BLOCK_SIZE ) + strcat( buf, "\n" ); + } + + return OAES_RET_SUCCESS; +} + +/* +#ifdef OAES_HAVE_ISAAC +static void oaes_get_seed( char buf[RANDSIZ + 1] ) +{ + struct timeb timer; + struct tm *gmTimer; + char * _test = NULL; + + ftime (&timer); + gmTimer = gmtime( &timer.time ); + _test = (char *) calloc( sizeof( char ), timer.millitm ); + sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", + gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, + gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.millitm, + _test + timer.millitm, getpid() ); + + if( _test ) + free( _test ); +} +#else +static uint32_t oaes_get_seed() +{ + struct timeb timer; + struct tm *gmTimer; + char * _test = NULL; + uint32_t _ret = 0; + + ftime (&timer); + gmTimer = gmtime( &timer.time ); + _test = (char *) calloc( sizeof( char ), timer.millitm ); + _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm + + (uint32_t) ( _test + timer.millitm ) + getpid(); + + if( _test ) + free( _test ); + + return _ret; +} +#endif // OAES_HAVE_ISAAC +*/ + +static OAES_RET oaes_key_destroy( oaes_key ** key ) +{ + if( NULL == *key ) + return OAES_RET_SUCCESS; + + if( (*key)->data ) + { + free( (*key)->data ); + (*key)->data = NULL; + } + + if( (*key)->exp_data ) + { + free( (*key)->exp_data ); + (*key)->exp_data = NULL; + } + + (*key)->data_len = 0; + (*key)->exp_data_len = 0; + (*key)->num_keys = 0; + (*key)->key_base = 0; + free( *key ); + *key = NULL; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_key_expand( OAES_CTX * ctx ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + _ctx->key->key_base = _ctx->key->data_len / OAES_RKEY_LEN; + _ctx->key->num_keys = _ctx->key->key_base + OAES_ROUND_BASE; + + _ctx->key->exp_data_len = _ctx->key->num_keys * OAES_RKEY_LEN * OAES_COL_LEN; + _ctx->key->exp_data = (uint8_t *) + calloc( _ctx->key->exp_data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->exp_data ) + return OAES_RET_MEM; + + // the first _ctx->key->data_len are a direct copy + memcpy( _ctx->key->exp_data, _ctx->key->data, _ctx->key->data_len ); + + // apply ExpandKey algorithm for remainder + for( _i = _ctx->key->key_base; _i < _ctx->key->num_keys * OAES_RKEY_LEN; _i++ ) + { + uint8_t _temp[OAES_COL_LEN]; + + memcpy( _temp, + _ctx->key->exp_data + ( _i - 1 ) * OAES_RKEY_LEN, OAES_COL_LEN ); + + // transform key column + if( 0 == _i % _ctx->key->key_base ) + { + oaes_word_rot_left( _temp ); + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + + _temp[0] = _temp[0] ^ oaes_gf_8[ _i / _ctx->key->key_base - 1 ]; + } + else if( _ctx->key->key_base > 6 && 4 == _i % _ctx->key->key_base ) + { + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + } + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + { + _ctx->key->exp_data[ _i * OAES_RKEY_LEN + _j ] = + _ctx->key->exp_data[ ( _i - _ctx->key->key_base ) * + OAES_RKEY_LEN + _j ] ^ _temp[_j]; + } + } + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_key_gen( OAES_CTX * ctx, size_t key_size ) +{ + size_t _i; + oaes_key * _key = NULL; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + _key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _key ) + return OAES_RET_MEM; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _key->data_len = key_size; + _key->data = (uint8_t *) calloc( key_size, sizeof( uint8_t )); + + if( NULL == _key->data ) + { + oaes_key_destroy( &_key ); + return OAES_RET_MEM; + } + + for( _i = 0; _i < key_size; _i++ ) + _key->data[_i] = (uint8_t) OAES_RAND(_ctx->rctx); + + _ctx->key = _key; + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_gen_128( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 16 ); +} + +OAES_RET oaes_key_gen_192( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 24 ); +} + +OAES_RET oaes_key_gen_256( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 32 ); +} + +OAES_RET oaes_key_export( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + // data + header + *data_len = _ctx->key->data_len + OAES_BLOCK_SIZE; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + // header + memcpy( data, oaes_header, OAES_BLOCK_SIZE ); + data[5] = 0x01; + data[7] = _ctx->key->data_len; + memcpy( data + OAES_BLOCK_SIZE, _ctx->key->data, _ctx->key->data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_export_data( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + *data_len = _ctx->key->data_len; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + memcpy( data, _ctx->key->data, *data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_import( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + int _key_length; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16 + OAES_BLOCK_SIZE: + case 24 + OAES_BLOCK_SIZE: + case 32 + OAES_BLOCK_SIZE: + break; + default: + return OAES_RET_ARG3; + } + + // header + if( 0 != memcmp( data, oaes_header, 4 ) ) + return OAES_RET_HEADER; + + // header version + switch( data[4] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // header type + switch( data[5] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // options + _key_length = data[7]; + switch( _key_length ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_HEADER; + } + + if( data_len != _key_length + OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = _key_length; + _ctx->key->data = (uint8_t *) + calloc( _key_length, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data + OAES_BLOCK_SIZE, _key_length ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_import_data( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_ARG3; + } + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = data_len; + _ctx->key->data = (uint8_t *) + calloc( data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data, data_len ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_CTX * oaes_alloc() +{ + oaes_ctx * _ctx = (oaes_ctx *) calloc( sizeof( oaes_ctx ), 1 ); + + if( NULL == _ctx ) + return NULL; + +#ifdef OAES_HAVE_ISAAC + { + ub4 _i = 0; + char _seed[RANDSIZ + 1]; + + _ctx->rctx = (randctx *) calloc( sizeof( randctx ), 1 ); + + if( NULL == _ctx->rctx ) + { + free( _ctx ); + return NULL; + } + + oaes_get_seed( _seed ); + memset( _ctx->rctx->randrsl, 0, RANDSIZ ); + memcpy( _ctx->rctx->randrsl, _seed, RANDSIZ ); + randinit( _ctx->rctx, TRUE); + } +#else + //srand( oaes_get_seed() ); +#endif // OAES_HAVE_ISAAC + + _ctx->key = NULL; + oaes_set_option( _ctx, OAES_OPTION_CBC, NULL ); + +#ifdef OAES_DEBUG + _ctx->step_cb = NULL; + oaes_set_option( _ctx, OAES_OPTION_STEP_OFF, NULL ); +#endif // OAES_DEBUG + + return (OAES_CTX *) _ctx; +} + +OAES_RET oaes_free( OAES_CTX ** ctx ) +{ + oaes_ctx ** _ctx = (oaes_ctx **) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == *_ctx ) + return OAES_RET_SUCCESS; + + if( (*_ctx)->key ) + oaes_key_destroy( &((*_ctx)->key) ); + +#ifdef OAES_HAVE_ISAAC + if( (*_ctx)->rctx ) + { + free( (*_ctx)->rctx ); + (*_ctx)->rctx = NULL; + } +#endif // OAES_HAVE_ISAAC + + free( *_ctx ); + *_ctx = NULL; + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_set_option( OAES_CTX * ctx, + OAES_OPTION option, const void * value ) +{ + size_t _i; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + switch( option ) + { + case OAES_OPTION_ECB: + _ctx->options &= ~OAES_OPTION_CBC; + memset( _ctx->iv, 0, OAES_BLOCK_SIZE ); + break; + + case OAES_OPTION_CBC: + _ctx->options &= ~OAES_OPTION_ECB; + if( value ) + memcpy( _ctx->iv, value, OAES_BLOCK_SIZE ); + else + { + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + _ctx->iv[_i] = (uint8_t) OAES_RAND(_ctx->rctx); + } + break; + +#ifdef OAES_DEBUG + + case OAES_OPTION_STEP_ON: + if( value ) + { + _ctx->options &= ~OAES_OPTION_STEP_OFF; + _ctx->step_cb = value; + } + else + { + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->options |= OAES_OPTION_STEP_OFF; + _ctx->step_cb = NULL; + return OAES_RET_ARG3; + } + break; + + case OAES_OPTION_STEP_OFF: + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->step_cb = NULL; + break; + +#endif // OAES_DEBUG + + default: + return OAES_RET_ARG2; + } + + _ctx->options |= option; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_encrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "input", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(State, K0) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "k_sch", 1, NULL ); + _ctx->step_cb( c, "k_add", 1, NULL ); + } +#endif // OAES_DEBUG + + // for round = 1 step 1 to Nr–1 + for( _i = 1; _i < _ctx->key->num_keys - 1; _i++ ) + { + // SubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _i, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _i, NULL ); +#endif // OAES_DEBUG + + // MixColumns(state) + oaes_mix_cols( c ); + oaes_mix_cols( c + 4 ); + oaes_mix_cols( c + 8 ); + oaes_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "m_col", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _i, NULL ); + _ctx->step_cb( c, "k_add", _i, NULL ); + } +#endif // OAES_DEBUG + + } + + // SubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "output", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_decrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "iinput", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "ik_add", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + for( _i = _ctx->key->num_keys - 2; _i > 0; _i-- ) + { + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", _i, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_inv_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _i, NULL ); + _ctx->step_cb( c, "ik_add", _i, NULL ); + } +#endif // OAES_DEBUG + + // InvMixColums(state) + oaes_inv_mix_cols( c ); + oaes_inv_mix_cols( c + 4 ); + oaes_inv_mix_cols( c + 8 ); + oaes_inv_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "im_col", _i, NULL ); +#endif // OAES_DEBUG + + } + + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", 1, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_inv_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[0, Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "ik_sch", 1, NULL ); + _ctx->step_cb( c, "ioutput", 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_encrypt( OAES_CTX * ctx, + const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len ) +{ + size_t _i, _j, _c_len_in, _c_data_len; + size_t _pad_len = m_len % OAES_BLOCK_SIZE == 0 ? + 0 : OAES_BLOCK_SIZE - m_len % OAES_BLOCK_SIZE; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _flags = _pad_len ? OAES_FLAG_PAD : 0; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == m ) + return OAES_RET_ARG2; + + if( NULL == c_len ) + return OAES_RET_ARG5; + + _c_len_in = *c_len; + // data + pad + _c_data_len = m_len + _pad_len; + // header + iv + data + pad + *c_len = 2 * OAES_BLOCK_SIZE + m_len + _pad_len; + + if( NULL == c ) + return OAES_RET_SUCCESS; + + if( _c_len_in < *c_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // fill with random data first + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + c[_i] = (uint8_t) OAES_RAND(_ctx->rctx); + // header + memcpy(c + 6, &_ctx->options, sizeof(_ctx->options)); + memcpy(c + 8, &_flags, sizeof(_flags)); + // iv + memcpy(c + OAES_BLOCK_SIZE, _ctx->iv, OAES_BLOCK_SIZE ); + // data + memcpy(c + 2 * OAES_BLOCK_SIZE, m, m_len ); + + for( _i = 0; _i < _c_data_len; _i += OAES_BLOCK_SIZE ) + { + uint8_t _block[OAES_BLOCK_SIZE]; + size_t _block_size = min( m_len - _i, OAES_BLOCK_SIZE ); + + memcpy( _block, c + 2 * OAES_BLOCK_SIZE + _i, _block_size ); + + // insert pad + for( _j = 0; _j < OAES_BLOCK_SIZE - _block_size; _j++ ) + _block[ _block_size + _j ] = _j + 1; + + // CBC + if( _ctx->options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + _block[_j] = _block[_j] ^ _ctx->iv[_j]; + } + + _rc = _rc || + oaes_encrypt_block( ctx, _block, OAES_BLOCK_SIZE ); + memcpy( c + 2 * OAES_BLOCK_SIZE + _i, _block, OAES_BLOCK_SIZE ); + + if( _ctx->options & OAES_OPTION_CBC ) + memcpy( _ctx->iv, _block, OAES_BLOCK_SIZE ); + } + + return _rc; +} + +OAES_RET oaes_decrypt( OAES_CTX * ctx, + const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len ) +{ + size_t _i, _j, _m_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _iv[OAES_BLOCK_SIZE]; + uint8_t _flags; + OAES_OPTION _options; + + if( NULL == ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len % OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == m_len ) + return OAES_RET_ARG5; + + _m_len_in = *m_len; + *m_len = c_len - 2 * OAES_BLOCK_SIZE; + + if( NULL == m ) + return OAES_RET_SUCCESS; + + if( _m_len_in < *m_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // options + memcpy(&_options, c + 6, sizeof(_options)); + // validate that all options are valid + if( _options & ~( + OAES_OPTION_ECB + | OAES_OPTION_CBC +#ifdef OAES_DEBUG + | OAES_OPTION_STEP_ON + | OAES_OPTION_STEP_OFF +#endif // OAES_DEBUG + ) ) + return OAES_RET_HEADER; + if( ( _options & OAES_OPTION_ECB ) && + ( _options & OAES_OPTION_CBC ) ) + return OAES_RET_HEADER; + if( _options == OAES_OPTION_NONE ) + return OAES_RET_HEADER; + + // flags + memcpy(&_flags, c + 8, sizeof(_flags)); + // validate that all flags are valid + if( _flags & ~( + OAES_FLAG_PAD + ) ) + return OAES_RET_HEADER; + + // iv + memcpy( _iv, c + OAES_BLOCK_SIZE, OAES_BLOCK_SIZE); + // data + pad + memcpy( m, c + 2 * OAES_BLOCK_SIZE, *m_len ); + + for( _i = 0; _i < *m_len; _i += OAES_BLOCK_SIZE ) + { + if( ( _options & OAES_OPTION_CBC ) && _i > 0 ) + memcpy( _iv, c + OAES_BLOCK_SIZE + _i, OAES_BLOCK_SIZE ); + + _rc = _rc || + oaes_decrypt_block( ctx, m + _i, min( *m_len - _i, OAES_BLOCK_SIZE ) ); + + // CBC + if( _options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + m[ _i + _j ] = m[ _i + _j ] ^ _iv[_j]; + } + } + + // remove pad + if( _flags & OAES_FLAG_PAD ) + { + int _is_pad = 1; + size_t _temp = (size_t) m[*m_len - 1]; + + if( _temp <= 0x00 || _temp > 0x0f ) + return OAES_RET_HEADER; + for( _i = 0; _i < _temp; _i++ ) + if( m[*m_len - 1 - _i] != _temp - _i ) + _is_pad = 0; + if( _is_pad ) + { + memset( m + *m_len - _temp, 0, _temp ); + *m_len -= _temp; + } + else + return OAES_RET_HEADER; + } + + return OAES_RET_SUCCESS; +} diff --git a/media/gmp-clearkey/0.1/openaes/oaes_lib.h b/media/gmp-clearkey/0.1/openaes/oaes_lib.h new file mode 100644 index 000000000..2344480e8 --- /dev/null +++ b/media/gmp-clearkey/0.1/openaes/oaes_lib.h @@ -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 diff --git a/media/gmp-clearkey/0.1/openaes/standard.h b/media/gmp-clearkey/0.1/openaes/standard.h new file mode 100644 index 000000000..fbe6ef5be --- /dev/null +++ b/media/gmp-clearkey/0.1/openaes/standard.h @@ -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 */ |