new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/AnnexB.cpp
@@ -0,0 +1,65 @@
+/* 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 "AnnexB.h"
+#include "mozilla/Endian.h"
+
+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);
+}
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/AnnexB.h
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __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__
--- a/media/gmp-clearkey/0.1/AudioDecoder.cpp
+++ b/media/gmp-clearkey/0.1/AudioDecoder.cpp
@@ -9,19 +9,25 @@
*
* 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 "stdafx.h"
+#include <cstdint>
+#include <limits>
-#ifdef TEST_DECODING
+#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)
{
@@ -38,38 +44,44 @@ AudioDecoder::InitDecode(const GMPAudioC
{
mCallback = aCallback;
assert(mCallback);
mDecoder = new WMFAACDecoder();
HRESULT hr = mDecoder->Init(aConfig.mChannelCount,
aConfig.mSamplesPerSecond,
(BYTE*)aConfig.mExtraData,
aConfig.mExtraDataLen);
- LOG(L"[%p] WMFDecodingModule::InitializeAudioDecoder() hr=0x%x\n", this, hr);
+ LOG("[%p] AudioDecoder::InitializeAudioDecoder() hr=0x%x\n", this, hr);
if (FAILED(hr)) {
mCallback->Error(GMPGenericErr);
return;
}
- auto err = GMPCreateMutex(&mMutex);
+ auto err = GetPlatform()->createmutex(&mMutex);
if (GMP_FAILED(err)) {
mCallback->Error(GMPGenericErr);
return;
}
}
void
-AudioDecoder::Decode(GMPAudioSamples* aInput)
+AudioDecoder::EnsureWorker()
{
if (!mWorkerThread) {
- GMPCreateThread(&mWorkerThread);
+ GetPlatform()->createthread(&mWorkerThread);
if (!mWorkerThread) {
mCallback->Error(GMPAllocErr);
return;
}
}
+}
+
+void
+AudioDecoder::Decode(GMPAudioSamples* aInput)
+{
+ EnsureWorker();
{
AutoLock lock(mMutex);
mNumInputTasks++;
}
mWorkerThread->Post(WrapTask(this,
&AudioDecoder::DecodeTask,
aInput));
}
@@ -81,103 +93,99 @@ AudioDecoder::DecodeTask(GMPAudioSamples
{
AutoLock lock(mMutex);
mNumInputTasks--;
assert(mNumInputTasks >= 0);
}
if (!aInput || !mHostAPI || !mDecoder) {
- LOG(L"Decode job not set up correctly!");
+ LOG("Decode job not set up correctly!");
return;
}
const uint8_t* inBuffer = aInput->Buffer();
if (!inBuffer) {
- LOG(L"No buffer for encoded samples!\n");
+ LOG("No buffer for encoded samples!\n");
return;
}
const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData();
- std::vector<uint8_t> buffer;
+ std::vector<uint8_t> buffer(inBuffer, inBuffer + aInput->Size());
if (crypto) {
- const uint8_t* iv = crypto->IV();
- uint32_t sz = crypto->IVSize();
// Plugin host should have set up its decryptor/key sessions
// before trying to decode!
- auto decryptor = Decryptor::Get(crypto);
- if (!decryptor ||
- !decryptor->Decrypt(inBuffer, aInput->Size(), crypto, buffer)) {
- LOG(L"Audio decryption error!");
- mCallback->Error(GMPNoKeyErr);
+ GMPErr rv =
+ ClearKeyDecryptionManager::Get()->Decrypt(&buffer[0], buffer.size(), crypto);
+
+ if (GMP_FAILED(rv)) {
+ GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv));
return;
}
- inBuffer = buffer.data();
- assert(buffer.size() == aInput->Size());
}
- hr = mDecoder->Input(inBuffer,
- aInput->Size(),
+ hr = mDecoder->Input(&buffer[0],
+ buffer.size(),
aInput->TimeStamp());
// We must delete the input sample!
- GMPRunOnMainThread(WrapTask(aInput, &GMPAudioSamples::Destroy));
+ GetPlatform()->runonmainthread(WrapTask(aInput, &GMPAudioSamples::Destroy));
- SAMPLE_LOG(L"AudioDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
+ SAMPLE_LOG("AudioDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
if (FAILED(hr)) {
- LOG(L"AudioDecoder::DecodeTask() decode failed ret=0x%x%s\n",
+ 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(L"AudioDecoder::DecodeTask() output ret=0x%x\n", hr);
+ 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.
- GMPRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted));
+ GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted));
}
} else if (FAILED(hr)) {
- LOG(L"AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr);
+ LOG("AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr);
}
}
}
void
AudioDecoder::ReturnOutput(IMFSample* aSample)
{
- SAMPLE_LOG(L"[%p] WMFDecodingModule::OutputVideoFrame()\n", this);
+ SAMPLE_LOG("[%p] AudioDecoder::ReturnOutput()\n", this);
assert(aSample);
HRESULT hr;
GMPAudioSamples* samples = nullptr;
mHostAPI->CreateSamples(kGMPAudioIS16Samples, &samples);
if (!samples) {
- LOG(L"Failed to create i420 frame!\n");
+ LOG("Failed to create i420 frame!\n");
return;
}
hr = MFToGMPSample(aSample, samples);
if (FAILED(hr)) {
samples->Destroy();
- LOG(L"Failed to prepare output sample!");
+ LOG("Failed to prepare output sample!");
return;
}
ENSURE(SUCCEEDED(hr), /*void*/);
- GMPRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
+ GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
}
HRESULT
AudioDecoder::MFToGMPSample(IMFSample* aInput,
GMPAudioSamples* aOutput)
{
ENSURE(aInput != nullptr, E_POINTER);
ENSURE(aOutput != nullptr, E_POINTER);
@@ -216,41 +224,40 @@ AudioDecoder::Reset()
mDecoder->Reset();
mCallback->ResetComplete();
}
void
AudioDecoder::DrainTask()
{
if (FAILED(mDecoder->Drain())) {
- GMPSyncRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
+ GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
}
// Return any pending output.
HRESULT hr = S_OK;
while (hr == S_OK) {
CComPtr<IMFSample> output;
hr = mDecoder->Output(&output);
- SAMPLE_LOG(L"AudioDecoder::DrainTask() output ret=0x%x\n", hr);
+ SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr);
if (hr == S_OK) {
ReturnOutput(output);
}
}
- GMPSyncRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
+ GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
}
void
AudioDecoder::Drain()
{
+ EnsureWorker();
mWorkerThread->Post(WrapTask(this,
&AudioDecoder::DrainTask));
}
void
AudioDecoder::DecodingComplete()
{
if (mWorkerThread) {
mWorkerThread->Join();
}
delete this;
}
-
-#endif
--- a/media/gmp-clearkey/0.1/AudioDecoder.h
+++ b/media/gmp-clearkey/0.1/AudioDecoder.h
@@ -9,19 +9,24 @@
*
* 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 "stdafx.h"
+#ifndef __AudioDecoder_h__
+#define __AudioDecoder_h__
-#ifdef TEST_DECODING
+#include "gmp-audio-decode.h"
+#include "gmp-audio-host.h"
+#include "WMFAACDecoder.h"
+
+#include "mfobjects.h"
class AudioDecoder : public GMPAudioDecoder
{
public:
AudioDecoder(GMPAudioHost *aHostAPI);
virtual ~AudioDecoder();
@@ -33,26 +38,28 @@ public:
virtual void Reset() override;
virtual void Drain() override;
virtual void DecodingComplete() override;
private:
+ void EnsureWorker();
+
void DecodeTask(GMPAudioSamples* aEncodedSamples);
void DrainTask();
void ReturnOutput(IMFSample* aSample);
HRESULT MFToGMPSample(IMFSample* aSample,
GMPAudioSamples* aAudioFrame);
GMPAudioHost *mHostAPI; // host-owned, invalid at DecodingComplete
GMPAudioDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
GMPThread* mWorkerThread;
GMPMutex* mMutex;
- AutoPtr<WMFAACDecoder> mDecoder;
+ wmf::AutoPtr<wmf::WMFAACDecoder> mDecoder;
int32_t mNumInputTasks;
};
-#endif // TEST_DECODING
+#endif // __AudioDecoder_h__
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -14,17 +14,17 @@ class ClearKeyDecryptor : public RefCoun
{
public:
MOZ_IMPLICIT ClearKeyDecryptor();
void InitKey(const Key& aKey);
bool HasKey() const { return !!mKey.size(); }
GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
- GMPEncryptedBufferMetadata* aMetadata);
+ const GMPEncryptedBufferMetadata* aMetadata);
const Key& DecryptionKey() const { return mKey; }
private:
~ClearKeyDecryptor();
Key mKey;
};
@@ -114,17 +114,17 @@ ClearKeyDecryptionManager::ReleaseKeyId(
ClearKeyDecryptor* decryptor = mDecryptors[aKeyId];
if (!decryptor->Release()) {
mDecryptors.erase(aKeyId);
}
}
GMPErr
ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
- GMPEncryptedBufferMetadata* aMetadata)
+ const GMPEncryptedBufferMetadata* aMetadata)
{
CK_LOGD("ClearKeyDecryptionManager::Decrypt");
KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
if (!HasKeyForKeyId(keyId)) {
return GMPNoKeyErr;
}
@@ -144,17 +144,17 @@ ClearKeyDecryptor::~ClearKeyDecryptor()
void
ClearKeyDecryptor::InitKey(const Key& aKey)
{
mKey = aKey;
}
GMPErr
ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
- GMPEncryptedBufferMetadata* aMetadata)
+ const GMPEncryptedBufferMetadata* 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
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -29,17 +29,17 @@ public:
const Key& GetDecryptionKey(const KeyId& aKeyId);
// Create a decryptor for the given KeyId if one does not already exist.
void InitKey(KeyId aKeyId, Key aKey);
void ExpectKeyId(KeyId aKeyId);
void ReleaseKeyId(KeyId aKeyId);
GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
- GMPEncryptedBufferMetadata* aMetadata);
+ const GMPEncryptedBufferMetadata* aMetadata);
void Shutdown();
private:
bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const;
std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
};
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -4,16 +4,17 @@
#include "ClearKeyPersistence.h"
#include "ClearKeyUtils.h"
#include "ClearKeyStorage.h"
#include "ClearKeySessionManager.h"
#include "mozilla/RefPtr.h"
#include <stdint.h>
+#include <string.h>
#include <set>
#include <vector>
#include <sstream>
using namespace mozilla;
using namespace std;
// Whether we've loaded the persistent session ids from GMPStorage yet.
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -1,12 +1,13 @@
/* 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 "ClearKeyDecryptionManager.h"
#include "ClearKeySession.h"
#include "ClearKeyUtils.h"
#include "ClearKeyStorage.h"
#include "gmp-task-utils.h"
#include "gmp-api/gmp-decryption.h"
#include "mozilla/Endian.h"
#include "pk11pub.h"
@@ -21,16 +22,26 @@ ClearKeySession::ClearKeySession(const s
, mSessionType(aSessionType)
{
CK_LOGD("ClearKeySession ctor %p", this);
}
ClearKeySession::~ClearKeySession()
{
CK_LOGD("ClearKeySession dtor %p", this);
+
+ auto& keyIds = GetKeyIds();
+ for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
+ MOZ_ASSERT(ClearKeyDecryptionManager::Get()->HasKeyForKeyId(*it));
+
+ ClearKeyDecryptionManager::Get()->ReleaseKeyId(*it);
+ mCallback->KeyStatusChanged(&mSessionId[0], mSessionId.size(),
+ &(*it)[0], it->size(),
+ kGMPUnknown);
+ }
}
void
ClearKeySession::Init(uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const uint8_t* aInitData, uint32_t aInitDataSize)
{
CK_LOGD("ClearKeySession::Init");
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -30,23 +30,34 @@ ClearKeySessionManager::ClearKeySessionM
}
ClearKeySessionManager::~ClearKeySessionManager()
{
CK_LOGD("ClearKeySessionManager dtor");
MOZ_ASSERT(!mRefCount);
}
+static bool
+CanDecode()
+{
+ return
+#if defined(ENABLE_WMF)
+ wmf::EnsureLibs() ||
+#endif
+ false;
+}
+
void
ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback)
{
CK_LOGD("ClearKeySessionManager::Init");
mCallback = aCallback;
- mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
- GMP_EME_CAP_DECRYPT_VIDEO);
+ mCallback->SetCapabilities(CanDecode() ?
+ GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO | GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO :
+ GMP_EME_CAP_DECRYPT_AUDIO | GMP_EME_CAP_DECRYPT_VIDEO);
ClearKeyPersistence::EnsureInitialized();
}
void
ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
uint32_t aPromiseId,
const char* aInitDataType,
uint32_t aInitDataTypeSize,
@@ -268,29 +279,16 @@ ClearKeySessionManager::CloseSession(uin
ClearInMemorySessionData(session);
mCallback->ResolvePromise(aPromiseId);
mCallback->SessionClosed(aSessionId, aSessionIdLength);
}
void
ClearKeySessionManager::ClearInMemorySessionData(ClearKeySession* aSession)
{
- MOZ_ASSERT(aSession);
-
- const vector<KeyId>& keyIds = aSession->GetKeyIds();
- for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
- MOZ_ASSERT(mDecryptionManager->HasKeyForKeyId(*it));
- mDecryptionManager->ReleaseKeyId(*it);
-
- const string& sessionId = aSession->Id();
- mCallback->KeyStatusChanged(&sessionId[0], sessionId.size(),
- &(*it)[0], it->size(),
- kGMPUnknown);
- }
-
mSessions.erase(aSession->Id());
delete aSession;
}
void
ClearKeySessionManager::RemoveSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength)
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h
@@ -9,18 +9,18 @@
#include <set>
#include <string>
#include <vector>
#include "ClearKeyDecryptionManager.h"
#include "ClearKeySession.h"
#include "ClearKeyUtils.h"
#include "gmp-api/gmp-decryption.h"
+#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
-#include "ScopedNSSTypes.h"
#include "RefCounted.h"
class ClearKeySessionManager MOZ_FINAL : public GMPDecryptor
, public RefCounted
{
public:
ClearKeySessionManager();
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
@@ -59,17 +59,17 @@ ClearKeyUtils::DecryptAES(const vector<u
for (size_t i = 0; i < aData.size(); i += CLEARKEY_KEY_LEN) {
size_t encLen;
oaes_encrypt(aes, &aIV[0], CLEARKEY_KEY_LEN, nullptr, &encLen);
vector<uint8_t> enc(encLen);
oaes_encrypt(aes, &aIV[0], CLEARKEY_KEY_LEN, &enc[0], &encLen);
MOZ_ASSERT(encLen >= 2 * OAES_BLOCK_SIZE + CLEARKEY_KEY_LEN);
- size_t blockLen = std::min(aData.size() - i, CLEARKEY_KEY_LEN);
+ size_t blockLen = min(aData.size() - i, CLEARKEY_KEY_LEN);
for (size_t j = 0; j < blockLen; j++) {
aData[i + j] ^= enc[2 * OAES_BLOCK_SIZE + j];
}
IncrementIV(aIV);
}
oaes_free(&aes);
}
--- a/media/gmp-clearkey/0.1/VideoDecoder.cpp
+++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp
@@ -9,19 +9,27 @@
*
* 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 "stdafx.h"
+#include <cstdint>
+#include <limits>
-#ifdef TEST_DECODING
+#include "AnnexB.h"
+#include "ClearKeyDecryptionManager.h"
+#include "ClearKeyUtils.h"
+#include "gmp-task-utils.h"
+#include "mozilla/Endian.h"
+#include "VideoDecoder.h"
+
+using namespace wmf;
VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI)
: mHostAPI(aHostAPI)
, mCallback(nullptr)
, mWorkerThread(nullptr)
, mMutex(nullptr)
, mNumInputTasks(0)
, mSentExtraData(false)
@@ -44,174 +52,174 @@ VideoDecoder::InitDecode(const GMPVideoC
assert(mCallback);
mDecoder = new WMFH264Decoder();
HRESULT hr = mDecoder->Init();
if (FAILED(hr)) {
mCallback->Error(GMPGenericErr);
return;
}
- auto err = GMPCreateMutex(&mMutex);
+ auto err = GetPlatform()->createmutex(&mMutex);
if (GMP_FAILED(err)) {
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);
- if (!mAVCC.Parse(mExtraData) ||
- !AVC::ConvertConfigToAnnexB(mAVCC, &mAnnexB)) {
- mCallback->Error(GMPGenericErr);
- return;
+ 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;
}
- if (!mWorkerThread) {
- GMPCreateThread(&mWorkerThread);
- if (!mWorkerThread) {
- mCallback->Error(GMPAllocErr);
- 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.
mWorkerThread->Post(WrapTask(this,
&VideoDecoder::DecodeTask,
aInputFrame));
}
-static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 };
-
void
VideoDecoder::DecodeTask(GMPVideoEncodedFrame* aInput)
{
+ CK_LOGD("VideoDecoder::DecodeTask");
HRESULT hr;
{
AutoLock lock(mMutex);
mNumInputTasks--;
assert(mNumInputTasks >= 0);
}
if (!aInput || !mHostAPI || !mDecoder) {
- LOG(L"Decode job not set up correctly!");
+ CK_LOGE("Decode job not set up correctly!");
return;
}
const uint8_t* inBuffer = aInput->Buffer();
if (!inBuffer) {
- LOG(L"No buffer for encoded frame!\n");
+ CK_LOGE("No buffer for encoded frame!\n");
return;
}
const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData();
- std::vector<uint8_t> buffer;
+ 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!
- auto decryptor = Decryptor::Get(crypto);
- if (!decryptor ||
- !decryptor->Decrypt(inBuffer, aInput->Size(), crypto, buffer)) {
- LOG(L"Video decryption error!");
- mCallback->Error(GMPNoKeyErr);
+ GMPErr rv =
+ ClearKeyDecryptionManager::Get()->Decrypt(&buffer[0], buffer.size(), crypto);
+
+ if (GMP_FAILED(rv)) {
+ GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::Error, rv));
return;
}
- buffer.data();
- } else {
- buffer.insert(buffer.end(), inBuffer, inBuffer+aInput->Size());
}
- AVC::ConvertFrameToAnnexB(4, &buffer);
+ AnnexB::ConvertFrameInPlace(buffer);
+
if (aInput->FrameType() == kGMPKeyFrame) {
// 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(),
aInput->TimeStamp(),
aInput->Duration());
// We must delete the input sample!
- GMPRunOnMainThread(WrapTask(aInput, &GMPVideoEncodedFrame::Destroy));
+ GetPlatform()->runonmainthread(WrapTask(aInput, &GMPVideoEncodedFrame::Destroy));
- SAMPLE_LOG(L"VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
+ CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
if (FAILED(hr)) {
- LOG(L"VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n",
+ 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);
- SAMPLE_LOG(L"VideoDecoder::DecodeTask() output ret=0x%x\n", hr);
+ CK_LOGD("VideoDecoder::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.
- GMPRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::InputDataExhausted));
+ GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::InputDataExhausted));
}
}
if (FAILED(hr)) {
- LOG(L"VideoDecoder::DecodeTask() output failed hr=0x%x\n", hr);
+ CK_LOGE("VideoDecoder::DecodeTask() output failed hr=0x%x\n", hr);
}
}
}
void
VideoDecoder::ReturnOutput(IMFSample* aSample)
{
- SAMPLE_LOG(L"[%p] WMFDecodingModule::OutputVideoFrame()\n", this);
+ CK_LOGD("[%p] VideoDecoder::ReturnOutput()\n", this);
assert(aSample);
HRESULT hr;
GMPVideoFrame* f = nullptr;
mHostAPI->CreateFrame(kGMPI420VideoFrame, &f);
if (!f) {
- LOG(L"Failed to create i420 frame!\n");
+ CK_LOGE("Failed to create i420 frame!\n");
return;
}
auto vf = static_cast<GMPVideoi420Frame*>(f);
hr = SampleToVideoFrame(aSample, vf);
ENSURE(SUCCEEDED(hr), /*void*/);
- GMPRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::Decoded, vf));
+ GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::Decoded, vf));
}
HRESULT
VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
GMPVideoi420Frame* aVideoFrame)
{
ENSURE(aSample != nullptr, E_POINTER);
@@ -252,17 +260,17 @@ VideoDecoder::SampleToVideoFrame(IMFSamp
}
int32_t y_size = stride * (height + padding);
int32_t v_size = stride * (height + padding) / 4;
int32_t halfStride = (stride + 1) / 2;
int32_t halfHeight = (height + 1) / 2;
int32_t halfWidth = (width + 1) / 2;
int32_t totalSize = y_size + 2 * v_size;
- GMPSyncRunOnMainThread(WrapTask(aVideoFrame,
+ GetPlatform()->syncrunonmainthread(WrapTask(aVideoFrame,
&GMPVideoi420Frame::CreateEmptyFrame,
stride, height, stride, halfStride, halfStride));
auto err = aVideoFrame->SetWidth(width);
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
err = aVideoFrame->SetHeight(height);
ENSURE(GMP_SUCCEEDED(err), E_FAIL);
@@ -305,41 +313,40 @@ VideoDecoder::Reset()
mDecoder->Reset();
mCallback->ResetComplete();
}
void
VideoDecoder::DrainTask()
{
if (FAILED(mDecoder->Drain())) {
- GMPSyncRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
+ GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
}
// Return any pending output.
HRESULT hr = S_OK;
while (hr == S_OK) {
CComPtr<IMFSample> output;
hr = mDecoder->Output(&output);
- SAMPLE_LOG(L"VideoDecoder::DrainTask() output ret=0x%x\n", hr);
+ CK_LOGD("VideoDecoder::DrainTask() output ret=0x%x\n", hr);
if (hr == S_OK) {
ReturnOutput(output);
}
}
- GMPSyncRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
+ GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
}
void
VideoDecoder::Drain()
{
+ EnsureWorker();
mWorkerThread->Post(WrapTask(this,
&VideoDecoder::DrainTask));
}
void
VideoDecoder::DecodingComplete()
{
if (mWorkerThread) {
mWorkerThread->Join();
}
delete this;
}
-
-#endif
--- a/media/gmp-clearkey/0.1/VideoDecoder.h
+++ b/media/gmp-clearkey/0.1/VideoDecoder.h
@@ -9,19 +9,24 @@
*
* 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 "stdafx.h"
+#ifndef __VideoDecoder_h__
+#define __VideoDecoder_h__
-#ifdef TEST_DECODING
+#include "gmp-video-decode.h"
+#include "gmp-video-host.h"
+#include "WMFH264Decoder.h"
+
+#include "mfobjects.h"
class VideoDecoder : public GMPVideoDecoder
{
public:
VideoDecoder(GMPVideoHost *aHostAPI);
virtual ~VideoDecoder();
@@ -40,32 +45,33 @@ public:
virtual void Reset() override;
virtual void Drain() override;
virtual void DecodingComplete() override;
private:
+ void EnsureWorker();
+
void DrainTask();
void DecodeTask(GMPVideoEncodedFrame* aInputFrame);
void ReturnOutput(IMFSample* aSample);
HRESULT SampleToVideoFrame(IMFSample* aSample,
GMPVideoi420Frame* aVideoFrame);
GMPVideoHost *mHostAPI; // host-owned, invalid at DecodingComplete
GMPVideoDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
GMPThread* mWorkerThread;
GMPMutex* mMutex;
- AutoPtr<WMFH264Decoder> mDecoder;
+ wmf::AutoPtr<wmf::WMFH264Decoder> mDecoder;
std::vector<uint8_t> mExtraData;
- AVCDecoderConfigurationRecord mAVCC;
std::vector<uint8_t> mAnnexB;
int32_t mNumInputTasks;
bool mSentExtraData;
};
-#endif
+#endif // __VideoDecoder_h__
--- a/media/gmp-clearkey/0.1/WMFAACDecoder.cpp
+++ b/media/gmp-clearkey/0.1/WMFAACDecoder.cpp
@@ -9,22 +9,21 @@
*
* 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 "stdafx.h"
-
-#ifdef TEST_DECODING
+#include "WMFAACDecoder.h"
using std::vector;
-using std::unique_ptr;
+
+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));
@@ -85,24 +84,24 @@ WMFAACDecoder::Init(int32_t aChannelCoun
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,
- L"msauddecmft.dll",
+ "msauddecmft.dll",
mDecoder);
if (FAILED(hr)) {
hr = CreateMFT(CLSID_CMSAACDecMFT,
- L"msmpeg2adec.dll",
+ "msmpeg2adec.dll",
mDecoder);
if (FAILED(hr)) {
- LOG(L"Failed to create AAC decoder\n");
+ LOG("Failed to create AAC decoder\n");
return E_FAIL;
}
}
BYTE* userData = nullptr;
UINT32 userDataLength;
hr = AACAudioSpecificConfigToUserData(aAACAudioSpecificConfig,
aAudioConfigLength,
@@ -152,19 +151,16 @@ WMFAACDecoder::SetDecoderInputType(int32
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->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 0x0); // Raw AAC
- 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;
}
@@ -321,17 +317,17 @@ WMFAACDecoder::Input(const uint8_t* aDat
{
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(L"ProcessInput returned MF_E_NOTACCEPTING\n");
+ LOG("ProcessInput returned MF_E_NOTACCEPTING\n");
return MF_E_NOTACCEPTING;
}
ENSURE(SUCCEEDED(hr), hr);
return S_OK;
}
HRESULT
@@ -363,9 +359,9 @@ HRESULT
WMFAACDecoder::Drain()
{
HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
ENSURE(SUCCEEDED(hr), hr);
return S_OK;
}
-#endif
+}
--- a/media/gmp-clearkey/0.1/WMFAACDecoder.h
+++ b/media/gmp-clearkey/0.1/WMFAACDecoder.h
@@ -9,21 +9,23 @@
*
* 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.
*/
-#ifdef TEST_DECODING
-
#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,
@@ -62,11 +64,11 @@ private:
MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
CComPtr<IMFTransform> mDecoder;
UINT32 mChannels;
UINT32 mRate;
};
-#endif
+}
#endif
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
@@ -9,19 +9,19 @@
*
* 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 "stdafx.h"
+#include "WMFH264Decoder.h"
-#ifdef TEST_DECODING
+namespace wmf {
WMFH264Decoder::WMFH264Decoder()
: mDecoder(nullptr)
{
memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO));
memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO));
}
@@ -30,17 +30,17 @@ WMFH264Decoder::~WMFH264Decoder()
}
HRESULT
WMFH264Decoder::Init()
{
HRESULT hr;
hr = CreateMFT(__uuidof(CMSH264DecoderMFT),
- L"msmpeg2vdec.dll",
+ "msmpeg2vdec.dll",
mDecoder);
ENSURE(SUCCEEDED(hr), hr);
hr = SetDecoderInputType();
ENSURE(SUCCEEDED(hr), hr);
hr = SetDecoderOutputType();
ENSURE(SUCCEEDED(hr), hr);
@@ -62,30 +62,30 @@ WMFH264Decoder::Init()
HRESULT
WMFH264Decoder::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType)
{
ENSURE(aMediaType != nullptr, E_POINTER);
HRESULT hr;
IntRect pictureRegion;
- hr = ::GetPictureRegion(aMediaType, 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);
// Success! Save state.
GetDefaultStride(aMediaType, (UINT32*)&mStride);
mVideoWidth = width;
mVideoHeight = height;
mPictureRegion = pictureRegion;
- LOG(L"WMFH264Decoder frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d)\n",
+ 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
@@ -113,17 +113,17 @@ WMFH264Decoder::GetStride() const
}
HRESULT
WMFH264Decoder::SetDecoderInputType()
{
HRESULT hr;
CComPtr<IMFMediaType> type;
- hr = ::MFCreateMediaType(&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);
@@ -292,17 +292,17 @@ WMFH264Decoder::Input(const uint8_t* aDa
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(L"ProcessInput returned MF_E_NOTACCEPTING\n");
+ LOG("ProcessInput returned MF_E_NOTACCEPTING\n");
return MF_E_NOTACCEPTING;
}
ENSURE(SUCCEEDED(hr), hr);
return S_OK;
}
HRESULT
@@ -335,9 +335,9 @@ HRESULT
WMFH264Decoder::Drain()
{
HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
ENSURE(SUCCEEDED(hr), hr);
return S_OK;
}
-#endif
+} // namespace wmf
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.h
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.h
@@ -9,21 +9,22 @@
*
* 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.
*/
-#ifdef TEST_DECODING
-
#if !defined(WMFH264Decoder_h_)
#define WMFH264Decoder_h_
+#include "WMFUtils.h"
+
+namespace wmf {
class WMFH264Decoder {
public:
WMFH264Decoder();
~WMFH264Decoder();
HRESULT Init();
@@ -67,11 +68,11 @@ private:
int32_t mVideoWidth;
int32_t mVideoHeight;
IntRect mPictureRegion;
int32_t mStride;
};
-#endif
+} // namespace wmf
#endif
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/WMFSymbols.h
@@ -0,0 +1,8 @@
+/* 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/. */
+
+MFPLAT_FUNC(MFCreateSample);
+MFPLAT_FUNC(MFCreateAlignedMemoryBuffer);
+MFPLAT_FUNC(MFGetStrideForBitmapInfoHeader);
+MFPLAT_FUNC(MFCreateMediaType);
--- a/media/gmp-clearkey/0.1/WMFUtils.cpp
+++ b/media/gmp-clearkey/0.1/WMFUtils.cpp
@@ -9,33 +9,83 @@
*
* 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 "stdafx.h"
+#include "WMFUtils.h"
+
+#include <stdio.h>
+
+#define INITGUID
+#include <guiddef.h>
-void LOG(wchar_t* format, ...)
+#pragma comment(lib, "mfuuid.lib")
+#pragma comment(lib, "wmcodecdspuuid")
+#pragma comment(lib, "mfplat.lib")
+
+void LOG(const char* format, ...)
{
+#ifdef WMF_DECODER_LOG
va_list args;
va_start(args, format);
-
- WCHAR msg[MAX_PATH];
- if (FAILED(StringCbVPrintf(msg, sizeof(msg), format, args))) {
- return;
- }
-
- OutputDebugString(msg);
+ 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
-#ifdef TEST_DECODING
+namespace wmf {
+
+
+#define MFPLAT_FUNC(_func) \
+ decltype(::_func)* _func;
+#include "WMFSymbols.h"
+#undef MFPLAT_FUNC
+
+static bool
+LinkMfplat()
+{
+ static bool sInitDone = false;
+ static bool sInitOk = false;
+ if (!sInitDone) {
+ sInitDone = true;
+ auto handle = GetModuleHandle("mfplat.dll");
+#define MFPLAT_FUNC(_func) \
+ if (!(_func = (decltype(_func))(GetProcAddress(handle, #_func)))) { \
+ return false; \
+ }
+#include "WMFSymbols.h"
+#undef MFPLAT_FUNC
+ sInitOk = true;
+ }
+ return sInitOk;
+}
+
+bool
+EnsureLibs()
+{
+ static bool sInitDone = false;
+ static bool sInitOk = false;
+ if (!sInitDone) {
+ sInitOk = LinkMfplat() &&
+ !!GetModuleHandle("msauddecmft.dll") &&
+ !!GetModuleHandle("msmpeg2adec.dll") &&
+ !!GetModuleHandle("msmpeg2vdec.dll");
+ 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.
@@ -115,59 +165,59 @@ GetDefaultStride(IMFMediaType *aType, ui
ENSURE(SUCCEEDED(hr), hr);
hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
ENSURE(SUCCEEDED(hr), hr);
return hr;
}
-#endif
-
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 wchar_t* aDllName,
+ const char* aDllName,
CComPtr<IMFTransform>& aOutMFT)
{
HMODULE module = ::GetModuleHandle(aDllName);
if (!module) {
- LOG(L"Failed to get %S\n", aDllName);
+ 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(L"Failed to get DllGetClassObject\n");
+ 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(L"Failed to get H264 IClassFactory\n");
+ 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(L"Failed to get create MFT\n");
+ LOG("Failed to get create MFT\n");
return E_FAIL;
}
return S_OK;
-}
\ No newline at end of file
+}
+
+} // namespace
--- a/media/gmp-clearkey/0.1/WMFUtils.h
+++ b/media/gmp-clearkey/0.1/WMFUtils.h
@@ -9,44 +9,109 @@
*
* 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.
*/
-void LOG(wchar_t* format, ...);
+#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 "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
+
+namespace wmf {
+
+// Reimplementation of CComPtr to reduce dependence on system
+// shared libraries.
+template<class T>
+class CComPtr {
+public:
+ CComPtr() : mPtr(nullptr) { }
+ CComPtr(T* const & aPtr) : mPtr(aPtr) { }
+ CComPtr(const nullptr_t& aNullPtr) : mPtr(aNullPtr) { }
+ T** operator&() { return &mPtr; }
+ T* operator->(){ return mPtr; }
+ operator T*() { return mPtr; }
+ T* operator=(T* const & aPtr) { return mPtr = aPtr; }
+ T* operator=(const nullptr_t& aPtr) { return mPtr = aPtr; }
+
+ T* Detach() {
+ T* tmp = mPtr;
+ mPtr = nullptr;
+ return tmp;
+ }
+
+ ~CComPtr() {
+ if (mPtr) {
+ mPtr->Release();
+ }
+ mPtr = nullptr;
+ }
+
+private:
+ 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(L"##condition## FAILED %S:%d\n", __FILE__, __LINE__); return 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) \
+ 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
@@ -169,10 +234,14 @@ public:
private:
GMPMutex* mMutex;
};
void dump(const uint8_t* data, uint32_t len, const char* filename);
HRESULT
CreateMFT(const CLSID& clsid,
- const wchar_t* aDllName,
+ const char* aDllName,
CComPtr<IMFTransform>& aOutMFT);
+
+} // namespace wmf
+
+#endif // __WMFUtils_h__
--- a/media/gmp-clearkey/0.1/clearkey.info
+++ b/media/gmp-clearkey/0.1/clearkey.info
@@ -1,4 +1,5 @@
Name: clearkey
Description: ClearKey decrypt-only GMP plugin
Version: 0.1
-APIs: eme-decrypt-v6[org.w3.clearkey]
+APIs: eme-decrypt-v6[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
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -1,16 +1,20 @@
/* 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 <assert.h>
#include <stdio.h>
#include <string.h>
#include "ClearKeySessionManager.h"
+#if defined(ENABLE_WMF)
+#include "WMFUtils.h"
+#endif
#include "gmp-api/gmp-decryption.h"
#include "gmp-api/gmp-platform.h"
#include "mozilla/Attributes.h"
static GMPPlatformAPI* sPlatform = nullptr;
GMPPlatformAPI*
GetPlatform()
@@ -25,24 +29,38 @@ GMPInit(GMPPlatformAPI* aPlatformAPI)
{
sPlatform = aPlatformAPI;
return GMPNoErr;
}
MOZ_EXPORT GMPErr
GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
{
- if (strcmp(aApiName, GMP_API_DECRYPTOR)) {
- return GMPNotImplementedErr;
+ CK_LOGD("ClearKey GMPGetAPI |%s|", aApiName);
+ assert(!*aPluginAPI);
+
+ if (!strcmp(aApiName, GMP_API_DECRYPTOR)) {
+ *aPluginAPI = new ClearKeySessionManager();
+ }
+#if defined(ENABLE_WMF)
+ else if (wmf::EnsureLibs()) {
+ if (!strcmp(aApiName, GMP_API_AUDIO_DECODER)) {
+ *aPluginAPI = new AudioDecoder(static_cast<GMPAudioHost*>(aHostAPI));
+ } else if (!strcmp(aApiName, GMP_API_VIDEO_DECODER)) {
+ *aPluginAPI = new VideoDecoder(static_cast<GMPVideoHost*>(aHostAPI));
+ }
+ }
+#endif
+ else {
+ CK_LOGE("GMPGetAPI couldn't resolve API name |%s|\n", aApiName);
}
- *aPluginAPI = new ClearKeySessionManager();
-
- return GMPNoErr;
+ return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
}
MOZ_EXPORT GMPErr
GMPShutdown(void)
{
+ CK_LOGD("ClearKey GMPShutdown");
return GMPNoErr;
}
}
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -4,31 +4,45 @@
# 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'
UNIFIED_SOURCES += [
- 'AudioDecoder.cpp',
'ClearKeyDecryptionManager.cpp',
'ClearKeyPersistence.cpp',
'ClearKeySession.cpp',
'ClearKeySessionManager.cpp',
'ClearKeyStorage.cpp',
'ClearKeyUtils.cpp',
'gmp-clearkey.cpp',
+]
+
+SOURCES += [
'openaes/oaes_lib.c',
- 'VideoDecoder.cpp',
- 'WMFAACDecoder.cpp',
- 'WMFH264Decoder.cpp',
- 'WMFUtils.cpp',
]
+if CONFIG['OS_ARCH'] == 'WINNT':
+ UNIFIED_SOURCES += [
+ 'AnnexB.cpp',
+ 'AudioDecoder.cpp',
+ 'VideoDecoder.cpp',
+ 'WMFAACDecoder.cpp',
+ 'WMFH264Decoder.cpp',
+ ]
+
+ SOURCES += [
+ 'WMFUtils.cpp',
+ ]
+
+ DEFINES['ENABLE_WMF'] = True
+
+
LOCAL_INCLUDES += [
'/dom/media/gmp',
]
USE_STATIC_LIBS = True
DISABLE_STL_WRAPPING = True
DEFINES['MOZ_NO_MOZALLOC'] = True
--- a/media/gmp-clearkey/0.1/openaes/standard.h
+++ b/media/gmp-clearkey/0.1/openaes/standard.h
@@ -33,25 +33,25 @@ typedef unsigned char ub1;
#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 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
+// #ifndef abs
+// # define abs(a) (((a)>0) ? (a) : -(a))
+// #endif
#define TRUE 1
#define FALSE 0
#define SUCCESS 0 /* 1 on VAX */
#endif /* STANDARD */