Bug 1075199 - WMF decoding in ClearKey CDM - r=cpearce a=lmandel
authorEdwin Flores <edwin@mozilla.com>
Fri, 16 Jan 2015 10:37:54 +1300
changeset 250191 6cb6bddb9b9d
parent 250190 9910b5a6a99f
child 250192 8fb0193c1399
push id4521
push usercpearce@mozilla.com
push date2015-03-04 01:22 +0000
treeherdermozilla-beta@8abdbdecd2d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, lmandel
bugs1075199
milestone37.0
Bug 1075199 - WMF decoding in ClearKey CDM - r=cpearce a=lmandel
media/gmp-clearkey/0.1/AnnexB.cpp
media/gmp-clearkey/0.1/AnnexB.h
media/gmp-clearkey/0.1/AudioDecoder.cpp
media/gmp-clearkey/0.1/AudioDecoder.h
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
media/gmp-clearkey/0.1/ClearKeySession.cpp
media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
media/gmp-clearkey/0.1/ClearKeySessionManager.h
media/gmp-clearkey/0.1/ClearKeyUtils.cpp
media/gmp-clearkey/0.1/VideoDecoder.cpp
media/gmp-clearkey/0.1/VideoDecoder.h
media/gmp-clearkey/0.1/WMFAACDecoder.cpp
media/gmp-clearkey/0.1/WMFAACDecoder.h
media/gmp-clearkey/0.1/WMFH264Decoder.cpp
media/gmp-clearkey/0.1/WMFH264Decoder.h
media/gmp-clearkey/0.1/WMFSymbols.h
media/gmp-clearkey/0.1/WMFUtils.cpp
media/gmp-clearkey/0.1/WMFUtils.h
media/gmp-clearkey/0.1/clearkey.info
media/gmp-clearkey/0.1/gmp-clearkey.cpp
media/gmp-clearkey/0.1/moz.build
media/gmp-clearkey/0.1/openaes/standard.h
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 */