summaryrefslogtreecommitdiffstats
path: root/media/gmp-clearkey
diff options
context:
space:
mode:
Diffstat (limited to 'media/gmp-clearkey')
-rw-r--r--media/gmp-clearkey/0.1/ClearKeyCDM.cpp191
-rw-r--r--media/gmp-clearkey/0.1/ClearKeyCDM.h103
-rw-r--r--media/gmp-clearkey/0.1/VideoDecoder.cpp325
-rw-r--r--media/gmp-clearkey/0.1/VideoDecoder.h72
-rw-r--r--media/gmp-clearkey/0.1/WMFH264Decoder.cpp331
-rw-r--r--media/gmp-clearkey/0.1/WMFH264Decoder.h70
-rw-r--r--media/gmp-clearkey/0.1/WMFSymbols.h20
-rw-r--r--media/gmp-clearkey/0.1/WMFUtils.cpp223
-rw-r--r--media/gmp-clearkey/0.1/WMFUtils.h248
-rw-r--r--media/gmp-clearkey/0.1/gmp-clearkey.cpp173
-rw-r--r--media/gmp-clearkey/0.1/manifest.json.in13
-rw-r--r--media/gmp-clearkey/0.1/moz.build62
12 files changed, 1831 insertions, 0 deletions
diff --git a/media/gmp-clearkey/0.1/ClearKeyCDM.cpp b/media/gmp-clearkey/0.1/ClearKeyCDM.cpp
new file mode 100644
index 0000000000..10ec6d3fe2
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyCDM.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClearKeyCDM.h"
+
+#include "ClearKeyUtils.h"
+
+using namespace cdm;
+
+ClearKeyCDM::ClearKeyCDM(Host_10* aHost) {
+ mHost = aHost;
+ mSessionManager = new ClearKeySessionManager(mHost);
+}
+
+void ClearKeyCDM::Initialize(bool aAllowDistinctiveIdentifier,
+ bool aAllowPersistentState,
+ bool aUseHardwareSecureCodecs) {
+ mSessionManager->Init(aAllowDistinctiveIdentifier, aAllowPersistentState);
+ // We call mHost->OnInitialized() in the session manager once it has
+ // initialized.
+}
+
+void ClearKeyCDM::GetStatusForPolicy(uint32_t aPromiseId,
+ const Policy& aPolicy) {
+ // Pretend the device is HDCP 2.1 compliant.
+ const cdm::HdcpVersion kDeviceHdcpVersion = cdm::kHdcpVersion2_1;
+ if (aPolicy.min_hdcp_version <= kDeviceHdcpVersion) {
+ mHost->OnResolveKeyStatusPromise(aPromiseId, KeyStatus::kUsable);
+ } else {
+ mHost->OnResolveKeyStatusPromise(aPromiseId, KeyStatus::kOutputRestricted);
+ }
+}
+void ClearKeyCDM::SetServerCertificate(uint32_t aPromiseId,
+ const uint8_t* aServerCertificateData,
+ uint32_t aServerCertificateDataSize) {
+ mSessionManager->SetServerCertificate(aPromiseId, aServerCertificateData,
+ aServerCertificateDataSize);
+}
+
+void ClearKeyCDM::CreateSessionAndGenerateRequest(uint32_t aPromiseId,
+ SessionType aSessionType,
+ InitDataType aInitDataType,
+ const uint8_t* aInitData,
+ uint32_t aInitDataSize) {
+ mSessionManager->CreateSession(aPromiseId, aInitDataType, aInitData,
+ aInitDataSize, aSessionType);
+}
+
+void ClearKeyCDM::LoadSession(uint32_t aPromiseId, SessionType aSessionType,
+ const char* aSessionId, uint32_t aSessionIdSize) {
+ mSessionManager->LoadSession(aPromiseId, aSessionId, aSessionIdSize);
+}
+
+void ClearKeyCDM::UpdateSession(uint32_t aPromiseId, const char* aSessionId,
+ uint32_t aSessionIdSize,
+ const uint8_t* aResponse,
+ uint32_t aResponseSize) {
+ mSessionManager->UpdateSession(aPromiseId, aSessionId, aSessionIdSize,
+ aResponse, aResponseSize);
+}
+
+void ClearKeyCDM::CloseSession(uint32_t aPromiseId, const char* aSessionId,
+ uint32_t aSessionIdSize) {
+ mSessionManager->CloseSession(aPromiseId, aSessionId, aSessionIdSize);
+}
+
+void ClearKeyCDM::RemoveSession(uint32_t aPromiseId, const char* aSessionId,
+ uint32_t aSessionIdSize) {
+ mSessionManager->RemoveSession(aPromiseId, aSessionId, aSessionIdSize);
+}
+
+void ClearKeyCDM::TimerExpired(void* aContext) {
+ // Clearkey is not interested in timers, so this method has not been
+ // implemented.
+ assert(false);
+}
+
+Status ClearKeyCDM::Decrypt(const InputBuffer_2& aEncryptedBuffer,
+ DecryptedBlock* aDecryptedBuffer) {
+ if (mIsProtectionQueryEnabled) {
+ // Piggyback this check onto Decrypt calls. If Clearkey implements a timer
+ // based approach for firing events, we could instead trigger the check
+ // using that mechanism.
+ mSessionManager->QueryOutputProtectionStatusIfNeeded();
+ }
+ return mSessionManager->Decrypt(aEncryptedBuffer, aDecryptedBuffer);
+}
+
+Status ClearKeyCDM::InitializeAudioDecoder(
+ const AudioDecoderConfig_2& aAudioDecoderConfig) {
+ // Audio decoding is not supported by Clearkey because Widevine doesn't
+ // support it and Clearkey's raison d'etre is to provide test coverage
+ // for paths that Widevine will exercise in the wild.
+ return Status::kDecodeError;
+}
+
+Status ClearKeyCDM::InitializeVideoDecoder(
+ const VideoDecoderConfig_2& aVideoDecoderConfig) {
+#ifdef ENABLE_WMF
+ mVideoDecoder = new VideoDecoder(mHost);
+ return mVideoDecoder->InitDecode(aVideoDecoderConfig);
+#else
+ return Status::kDecodeError;
+#endif
+}
+
+void ClearKeyCDM::DeinitializeDecoder(StreamType aDecoderType) {
+#ifdef ENABLE_WMF
+ if (aDecoderType == StreamType::kStreamTypeVideo) {
+ mVideoDecoder->DecodingComplete();
+ mVideoDecoder = nullptr;
+ }
+#endif
+}
+
+void ClearKeyCDM::ResetDecoder(StreamType aDecoderType) {
+#ifdef ENABLE_WMF
+ if (aDecoderType == StreamType::kStreamTypeVideo) {
+ mVideoDecoder->Reset();
+ }
+#endif
+}
+
+Status ClearKeyCDM::DecryptAndDecodeFrame(const InputBuffer_2& aEncryptedBuffer,
+ VideoFrame* aVideoFrame) {
+#ifdef ENABLE_WMF
+ if (mIsProtectionQueryEnabled) {
+ // Piggyback this check onto Decrypt + Decode. If Clearkey implements a
+ // timer based approach for firing events, we could instead trigger the
+ // check using that mechanism.
+ mSessionManager->QueryOutputProtectionStatusIfNeeded();
+ }
+ return mVideoDecoder->Decode(aEncryptedBuffer, aVideoFrame);
+#else
+ return Status::kDecodeError;
+#endif
+}
+
+Status ClearKeyCDM::DecryptAndDecodeSamples(
+ const InputBuffer_2& aEncryptedBuffer, AudioFrames* aAudioFrame) {
+ // Audio decoding is not supported by Clearkey because Widevine doesn't
+ // support it and Clearkey's raison d'etre is to provide test coverage
+ // for paths that Widevine will exercise in the wild.
+ return Status::kDecodeError;
+}
+
+void ClearKeyCDM::OnPlatformChallengeResponse(
+ const PlatformChallengeResponse& aResponse) {
+ // This function should never be called and is not supported.
+ assert(false);
+}
+
+void ClearKeyCDM::OnQueryOutputProtectionStatus(
+ QueryResult aResult, uint32_t aLinkMask, uint32_t aOutputProtectionMask) {
+ // The higher level GMP machinery should not forward us query information
+ // unless we've requested it (even if mutiple CDMs exist at once and some
+ // others are reqeusting this info). If this assert fires we're violating
+ // that.
+ MOZ_ASSERT(mIsProtectionQueryEnabled,
+ "Should only receive a protection status "
+ "mIsProtectionQueryEnabled is true");
+ // The session manager handles the guts of this for ClearKey.
+ mSessionManager->OnQueryOutputProtectionStatus(aResult, aLinkMask,
+ aOutputProtectionMask);
+}
+
+void ClearKeyCDM::OnStorageId(uint32_t aVersion, const uint8_t* aStorageId,
+ uint32_t aStorageIdSize) {
+ // This function should never be called and is not supported.
+ assert(false);
+}
+
+void ClearKeyCDM::Destroy() {
+ mSessionManager->DecryptingComplete();
+#ifdef ENABLE_WMF
+ // If we have called 'DeinitializeDecoder' mVideoDecoder will be null.
+ if (mVideoDecoder) {
+ mVideoDecoder->DecodingComplete();
+ }
+#endif
+ delete this;
+}
+
+void ClearKeyCDM::EnableProtectionQuery() {
+ MOZ_ASSERT(!mIsProtectionQueryEnabled,
+ "Should not be called more than once per CDM");
+ mIsProtectionQueryEnabled = true;
+}
diff --git a/media/gmp-clearkey/0.1/ClearKeyCDM.h b/media/gmp-clearkey/0.1/ClearKeyCDM.h
new file mode 100644
index 0000000000..6977b0e787
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyCDM.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ClearKeyCDM_h_
+#define ClearKeyCDM_h_
+
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include <stddef.h>
+
+#include "content_decryption_module.h"
+
+#include "ClearKeySessionManager.h"
+
+#ifdef ENABLE_WMF
+# include "VideoDecoder.h"
+# include "WMFUtils.h"
+#endif
+
+class ClearKeyCDM : public cdm::ContentDecryptionModule_10 {
+ private:
+ RefPtr<ClearKeySessionManager> mSessionManager;
+#ifdef ENABLE_WMF
+ RefPtr<VideoDecoder> mVideoDecoder;
+#endif
+ bool mIsProtectionQueryEnabled = false;
+
+ protected:
+ cdm::Host_10* mHost;
+
+ public:
+ explicit ClearKeyCDM(cdm::Host_10* aHost);
+
+ void Initialize(bool aAllowDistinctiveIdentifier, bool aAllowPersistentState,
+ bool aUseHardwareSecureCodecs) override;
+
+ void GetStatusForPolicy(uint32_t aPromiseId,
+ const cdm::Policy& aPolicy) override;
+
+ void SetServerCertificate(uint32_t aPromiseId,
+ const uint8_t* aServerCertificateData,
+ uint32_t aServerCertificateDataSize) override;
+
+ void CreateSessionAndGenerateRequest(uint32_t aPromiseId,
+ cdm::SessionType aSessionType,
+ cdm::InitDataType aInitDataType,
+ const uint8_t* aInitData,
+ uint32_t aInitDataSize) override;
+
+ void LoadSession(uint32_t aPromiseId, cdm::SessionType aSessionType,
+ const char* aSessionId, uint32_t aSessionIdSize) override;
+
+ void UpdateSession(uint32_t aPromiseId, const char* aSessionId,
+ uint32_t aSessionIdSize, const uint8_t* aResponse,
+ uint32_t aResponseSize) override;
+
+ void CloseSession(uint32_t aPromiseId, const char* aSessionId,
+ uint32_t aSessionIdSize) override;
+
+ void RemoveSession(uint32_t aPromiseId, const char* aSessionId,
+ uint32_t aSessionIdSize) override;
+
+ void TimerExpired(void* aContext) override;
+
+ cdm::Status Decrypt(const cdm::InputBuffer_2& aEncryptedBuffer,
+ cdm::DecryptedBlock* aDecryptedBuffer) override;
+
+ cdm::Status InitializeAudioDecoder(
+ const cdm::AudioDecoderConfig_2& aAudioDecoderConfig) override;
+
+ cdm::Status InitializeVideoDecoder(
+ const cdm::VideoDecoderConfig_2& aVideoDecoderConfig) override;
+
+ void DeinitializeDecoder(cdm::StreamType aDecoderType) override;
+
+ void ResetDecoder(cdm::StreamType aDecoderType) override;
+
+ cdm::Status DecryptAndDecodeFrame(const cdm::InputBuffer_2& aEncryptedBuffer,
+ cdm::VideoFrame* aVideoFrame) override;
+
+ cdm::Status DecryptAndDecodeSamples(
+ const cdm::InputBuffer_2& aEncryptedBuffer,
+ cdm::AudioFrames* aAudioFrame) override;
+
+ void OnPlatformChallengeResponse(
+ const cdm::PlatformChallengeResponse& aResponse) override;
+
+ void OnQueryOutputProtectionStatus(cdm::QueryResult aResult,
+ uint32_t aLinkMask,
+ uint32_t aOutputProtectionMask) override;
+
+ void OnStorageId(uint32_t aVersion, const uint8_t* aStorageId,
+ uint32_t aStorageIdSize) override;
+
+ void Destroy() override;
+
+ void EnableProtectionQuery();
+};
+
+#endif
diff --git a/media/gmp-clearkey/0.1/VideoDecoder.cpp b/media/gmp-clearkey/0.1/VideoDecoder.cpp
new file mode 100644
index 0000000000..6417defdaf
--- /dev/null
+++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2013, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+
+#include "BigEndian.h"
+#include "ClearKeyUtils.h"
+#include "ClearKeyDecryptionManager.h"
+#include "VideoDecoder.h"
+#include "content_decryption_module.h"
+#include "mozilla/CheckedInt.h"
+
+using namespace wmf;
+
+VideoDecoder::VideoDecoder(cdm::Host_10* aHost)
+ : mHost(aHost), mHasShutdown(false) {
+ CK_LOGD("VideoDecoder created");
+
+ // We drop the ref in DecodingComplete().
+ AddRef();
+
+ mDecoder = new WMFH264Decoder();
+
+ uint32_t cores = std::max(1u, std::thread::hardware_concurrency());
+
+ HRESULT hr = mDecoder->Init(cores);
+ if (FAILED(hr)) {
+ CK_LOGE("Failed to initialize mDecoder!");
+ }
+}
+
+VideoDecoder::~VideoDecoder() { CK_LOGD("VideoDecoder destroyed"); }
+
+cdm::Status VideoDecoder::InitDecode(const cdm::VideoDecoderConfig_2& aConfig) {
+ CK_LOGD("VideoDecoder::InitDecode");
+
+ if (!mDecoder) {
+ CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
+
+ return cdm::Status::kDecodeError;
+ }
+
+ return cdm::Status::kSuccess;
+}
+
+cdm::Status VideoDecoder::Decode(const cdm::InputBuffer_2& aInputBuffer,
+ cdm::VideoFrame* aVideoFrame) {
+ CK_LOGD("VideoDecoder::Decode");
+ // If the input buffer we have been passed has a null buffer, it means we
+ // should drain.
+ if (!aInputBuffer.data) {
+ // This will drain the decoder until there are no frames left to drain,
+ // whereupon it will return 'NeedsMoreData'.
+ CK_LOGD("VideoDecoder::Decode Input buffer null: Draining");
+ return Drain(aVideoFrame);
+ }
+
+ DecodeData* data = new DecodeData();
+ Assign(data->mBuffer, aInputBuffer.data, aInputBuffer.data_size);
+ data->mTimestamp = aInputBuffer.timestamp;
+ data->mCrypto = CryptoMetaData(&aInputBuffer);
+
+ AutoPtr<DecodeData> d(data);
+ HRESULT hr;
+
+ if (!data || !mDecoder) {
+ CK_LOGE("Decode job not set up correctly!");
+ return cdm::Status::kDecodeError;
+ }
+
+ std::vector<uint8_t>& buffer = data->mBuffer;
+
+ if (data->mCrypto.IsValid()) {
+ cdm::Status rv =
+ ClearKeyDecryptionManager::Get()->Decrypt(buffer, data->mCrypto);
+
+ if (STATUS_FAILED(rv)) {
+ CK_LOGARRAY("Failed to decrypt video using key ", aInputBuffer.key_id,
+ aInputBuffer.key_id_size);
+ return rv;
+ }
+ }
+
+ hr = mDecoder->Input(buffer.data(), buffer.size(), data->mTimestamp);
+
+ CK_LOGD("VideoDecoder::Decode() Input ret hr=0x%x", hr);
+
+ if (FAILED(hr)) {
+ assert(hr != MF_E_TRANSFORM_NEED_MORE_INPUT);
+
+ CK_LOGE("VideoDecoder::Decode() decode failed ret=0x%x%s", hr,
+ ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
+ CK_LOGD("Decode failed. The decoder is not accepting input");
+ return cdm::Status::kDecodeError;
+ }
+
+ return OutputFrame(aVideoFrame);
+}
+
+cdm::Status VideoDecoder::OutputFrame(cdm::VideoFrame* aVideoFrame) {
+ CK_LOGD("VideoDecoder::OutputFrame");
+
+ HRESULT hr = S_OK;
+
+ // Read all the output from the decoder. Ideally, this would be a while loop
+ // where we read the output and check the result as the condition. However,
+ // this produces a memory leak connected to assigning a new CComPtr to the
+ // address of the old one, which avoids the CComPtr cleaning up.
+ while (true) {
+ CComPtr<IMFSample> output;
+ hr = mDecoder->Output(&output);
+
+ if (hr != S_OK) {
+ break;
+ }
+
+ CK_LOGD("VideoDecoder::OutputFrame Decoder output ret=0x%x", hr);
+
+ mOutputQueue.push(output);
+ CK_LOGD("VideoDecoder::OutputFrame: Queue size: %u", mOutputQueue.size());
+ }
+
+ // If we don't have any inputs, we need more data.
+ if (mOutputQueue.empty()) {
+ CK_LOGD("Decode failed. Not enought data; Requesting more input");
+ return cdm::Status::kNeedMoreData;
+ }
+
+ // We will get a MF_E_TRANSFORM_NEED_MORE_INPUT every time, as we always
+ // consume everything in the buffer.
+ if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT && FAILED(hr)) {
+ CK_LOGD("Decode failed output ret=0x%x", hr);
+ return cdm::Status::kDecodeError;
+ }
+
+ CComPtr<IMFSample> result = mOutputQueue.front();
+ mOutputQueue.pop();
+
+ // The Chromium CDM API doesn't have support for negative strides, though
+ // they are theoretically possible in real world data.
+ if (mDecoder->GetStride() <= 0) {
+ CK_LOGD("VideoDecoder::OutputFrame Failed! (negative stride)");
+ return cdm::Status::kDecodeError;
+ }
+
+ const IntRect& picture = mDecoder->GetPictureRegion();
+ hr = SampleToVideoFrame(result, picture.width, picture.height,
+ mDecoder->GetStride(), mDecoder->GetFrameHeight(),
+ aVideoFrame);
+ if (FAILED(hr)) {
+ CK_LOGD("VideoDecoder::OutputFrame Failed!");
+ return cdm::Status::kDecodeError;
+ }
+
+ CK_LOGD("VideoDecoder::OutputFrame Succeeded.");
+ return cdm::Status::kSuccess;
+}
+
+HRESULT
+VideoDecoder::SampleToVideoFrame(IMFSample* aSample, int32_t aPictureWidth,
+ int32_t aPictureHeight, int32_t aStride,
+ int32_t aFrameHeight,
+ cdm::VideoFrame* aVideoFrame) {
+ CK_LOGD("[%p] VideoDecoder::SampleToVideoFrame()", this);
+
+ ENSURE(aSample != nullptr, E_POINTER);
+ ENSURE(aVideoFrame != nullptr, E_POINTER);
+
+ HRESULT hr;
+ CComPtr<IMFMediaBuffer> mediaBuffer;
+
+ aVideoFrame->SetFormat(cdm::kI420);
+
+ // Must convert to contiguous mediaBuffer to use IMD2DBuffer interface.
+ hr = aSample->ConvertToContiguousBuffer(&mediaBuffer);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ // Try and use the IMF2DBuffer interface if available, otherwise fallback
+ // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
+ // but only some systems (Windows 8?) support it.
+ BYTE* data = nullptr;
+ LONG stride = 0;
+ CComPtr<IMF2DBuffer> twoDBuffer;
+ hr = mediaBuffer->QueryInterface(static_cast<IMF2DBuffer**>(&twoDBuffer));
+ if (SUCCEEDED(hr)) {
+ hr = twoDBuffer->Lock2D(&data, &stride);
+ ENSURE(SUCCEEDED(hr), hr);
+ } else {
+ hr = mediaBuffer->Lock(&data, nullptr, nullptr);
+ ENSURE(SUCCEEDED(hr), hr);
+ stride = aStride;
+ }
+
+ // WMF stores the U and V planes 16-row-aligned, so we need to add padding
+ // to the row heights to ensure the source offsets of the Y'CbCr planes are
+ // referenced properly.
+ // YV12, planar format: [YYYY....][UUUU....][VVVV....]
+ // i.e., Y, then U, then V.
+ uint32_t padding = 0;
+ if (aFrameHeight % 16 != 0) {
+ padding = 16 - (aFrameHeight % 16);
+ }
+ uint32_t srcYSize = stride * (aFrameHeight + padding);
+ uint32_t srcUVSize = stride * (aFrameHeight + padding) / 4;
+ uint32_t halfStride = (stride + 1) / 2;
+
+ aVideoFrame->SetStride(cdm::VideoPlane::kYPlane, stride);
+ aVideoFrame->SetStride(cdm::VideoPlane::kUPlane, halfStride);
+ aVideoFrame->SetStride(cdm::VideoPlane::kVPlane, halfStride);
+
+ aVideoFrame->SetSize(cdm::Size{aPictureWidth, aPictureHeight});
+
+ // Note: We allocate the minimal sized buffer required to send the
+ // frame back over to the parent process. This is so that we request the
+ // same sized frame as the buffer allocator expects.
+ using mozilla::CheckedUint32;
+ CheckedUint32 bufferSize = CheckedUint32(stride) * aPictureHeight +
+ ((CheckedUint32(stride) * aPictureHeight) / 4) * 2;
+
+ // If the buffer is bigger than the max for a 32 bit, fail to avoid buffer
+ // overflows.
+ if (!bufferSize.isValid()) {
+ CK_LOGD("VideoDecoder::SampleToFrame Buffersize bigger than UINT32_MAX");
+ return E_FAIL;
+ }
+
+ // Get the buffer from the host.
+ cdm::Buffer* buffer = mHost->Allocate(bufferSize.value());
+ aVideoFrame->SetFrameBuffer(buffer);
+
+ // Make sure the buffer is non-null (allocate guarantees it will be of
+ // sufficient size).
+ if (!buffer) {
+ CK_LOGD("VideoDecoder::SampleToFrame Out of memory");
+ return E_OUTOFMEMORY;
+ }
+
+ uint8_t* outBuffer = buffer->Data();
+
+ aVideoFrame->SetPlaneOffset(cdm::VideoPlane::kYPlane, 0);
+
+ // Offset of U plane is the size of the Y plane, excluding the padding that
+ // WMF adds.
+ uint32_t dstUOffset = stride * aPictureHeight;
+ aVideoFrame->SetPlaneOffset(cdm::VideoPlane::kUPlane, dstUOffset);
+
+ // Offset of the V plane is the size of the Y plane + the size of the U plane,
+ // excluding any padding WMF adds.
+ uint32_t dstVOffset = stride * aPictureHeight + (stride * aPictureHeight) / 4;
+ aVideoFrame->SetPlaneOffset(cdm::VideoPlane::kVPlane, dstVOffset);
+
+ // Copy the pixel data, excluding WMF's padding.
+ memcpy(outBuffer, data, stride * aPictureHeight);
+ memcpy(outBuffer + dstUOffset, data + srcYSize,
+ (stride * aPictureHeight) / 4);
+ memcpy(outBuffer + dstVOffset, data + srcYSize + srcUVSize,
+ (stride * aPictureHeight) / 4);
+
+ if (twoDBuffer) {
+ twoDBuffer->Unlock2D();
+ } else {
+ mediaBuffer->Unlock();
+ }
+
+ LONGLONG hns = 0;
+ hr = aSample->GetSampleTime(&hns);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ aVideoFrame->SetTimestamp(HNsToUsecs(hns));
+
+ return S_OK;
+}
+
+void VideoDecoder::Reset() {
+ CK_LOGD("VideoDecoder::Reset");
+
+ if (mDecoder) {
+ mDecoder->Reset();
+ }
+
+ // Remove all the frames from the output queue.
+ while (!mOutputQueue.empty()) {
+ mOutputQueue.pop();
+ }
+}
+
+cdm::Status VideoDecoder::Drain(cdm::VideoFrame* aVideoFrame) {
+ CK_LOGD("VideoDecoder::Drain()");
+
+ if (!mDecoder) {
+ CK_LOGD("Drain failed! Decoder was not initialized");
+ return cdm::Status::kDecodeError;
+ }
+
+ mDecoder->Drain();
+
+ // Return any pending output.
+ return OutputFrame(aVideoFrame);
+}
+
+void VideoDecoder::DecodingComplete() {
+ CK_LOGD("VideoDecoder::DecodingComplete()");
+
+ mHasShutdown = true;
+
+ // Release the reference we added in the constructor. There may be
+ // WrapRefCounted tasks that also hold references to us, and keep
+ // us alive a little longer.
+ Release();
+}
diff --git a/media/gmp-clearkey/0.1/VideoDecoder.h b/media/gmp-clearkey/0.1/VideoDecoder.h
new file mode 100644
index 0000000000..6a1544ce65
--- /dev/null
+++ b/media/gmp-clearkey/0.1/VideoDecoder.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2013, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VideoDecoder_h__
+#define __VideoDecoder_h__
+
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include <stddef.h>
+
+#include <atomic>
+#include <queue>
+#include <thread>
+
+#include "content_decryption_module.h"
+#include "WMFH264Decoder.h"
+
+class VideoDecoder : public RefCounted {
+ public:
+ explicit VideoDecoder(cdm::Host_10* aHost);
+
+ cdm::Status InitDecode(const cdm::VideoDecoderConfig_2& aConfig);
+
+ cdm::Status Decode(const cdm::InputBuffer_2& aEncryptedBuffer,
+ cdm::VideoFrame* aVideoFrame);
+
+ void Reset();
+
+ void DecodingComplete();
+
+ bool HasShutdown() { return mHasShutdown; }
+
+ private:
+ virtual ~VideoDecoder();
+
+ cdm::Status Drain(cdm::VideoFrame* aVideoFrame);
+
+ struct DecodeData {
+ std::vector<uint8_t> mBuffer;
+ uint64_t mTimestamp = 0;
+ CryptoMetaData mCrypto;
+ };
+
+ cdm::Status OutputFrame(cdm::VideoFrame* aVideoFrame);
+
+ HRESULT SampleToVideoFrame(IMFSample* aSample, int32_t aPictureWidth,
+ int32_t aPictureHeight, int32_t aStride,
+ int32_t aFrameHeight,
+ cdm::VideoFrame* aVideoFrame);
+
+ cdm::Host_10* mHost;
+ wmf::AutoPtr<wmf::WMFH264Decoder> mDecoder;
+
+ std::queue<wmf::CComPtr<IMFSample>> mOutputQueue;
+
+ bool mHasShutdown;
+};
+
+#endif // __VideoDecoder_h__
diff --git a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
new file mode 100644
index 0000000000..8054b38c64
--- /dev/null
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2013, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WMFH264Decoder.h"
+
+#include <codecapi.h>
+
+#include <algorithm>
+
+namespace wmf {
+
+WMFH264Decoder::WMFH264Decoder() : mDecoder(nullptr) {
+ memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO));
+ memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO));
+}
+
+WMFH264Decoder::~WMFH264Decoder() {}
+
+HRESULT
+WMFH264Decoder::Init(int32_t aCoreCount) {
+ HRESULT hr;
+
+ hr = CreateMFT(__uuidof(CMSH264DecoderMFT), WMFDecoderDllName(), mDecoder);
+ if (FAILED(hr)) {
+ // Windows 7 Enterprise Server N (which is what Mozilla's mochitests run
+ // on) need a different CLSID to instantiate the H.264 decoder.
+ hr = CreateMFT(CLSID_CMSH264DecMFT, WMFDecoderDllName(), mDecoder);
+ }
+ ENSURE(SUCCEEDED(hr), hr);
+
+ CComPtr<IMFAttributes> attr;
+ hr = mDecoder->GetAttributes(&attr);
+ ENSURE(SUCCEEDED(hr), hr);
+ hr = attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
+ GetNumThreads(aCoreCount));
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = SetDecoderInputType();
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = SetDecoderOutputType();
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType) {
+ ENSURE(aMediaType != nullptr, E_POINTER);
+ HRESULT hr;
+
+ IntRect pictureRegion;
+ hr = wmf::GetPictureRegion(aMediaType, pictureRegion);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ UINT32 width = 0, height = 0;
+ hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
+ ENSURE(SUCCEEDED(hr), hr);
+ ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
+ ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
+
+ UINT32 stride = 0;
+ hr = GetDefaultStride(aMediaType, &stride);
+ ENSURE(SUCCEEDED(hr), hr);
+ ENSURE(stride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
+
+ // Success! Save state.
+ mStride = stride;
+ mVideoWidth = width;
+ mVideoHeight = height;
+ mPictureRegion = pictureRegion;
+
+ LOG("WMFH264Decoder frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, "
+ "%d, %d)\n",
+ width, height, mStride, mPictureRegion.x, mPictureRegion.y,
+ mPictureRegion.width, mPictureRegion.height);
+
+ return S_OK;
+}
+
+int32_t WMFH264Decoder::GetFrameHeight() const { return mVideoHeight; }
+
+const IntRect& WMFH264Decoder::GetPictureRegion() const {
+ return mPictureRegion;
+}
+
+int32_t WMFH264Decoder::GetStride() const { return mStride; }
+
+HRESULT
+WMFH264Decoder::SetDecoderInputType() {
+ HRESULT hr;
+
+ CComPtr<IMFMediaType> type;
+ hr = MFCreateMediaType(&type);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = type->SetUINT32(MF_MT_INTERLACE_MODE,
+ MFVideoInterlace_MixedInterlaceOrProgressive);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = mDecoder->SetInputType(0, type, 0);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::SetDecoderOutputType() {
+ HRESULT hr;
+
+ CComPtr<IMFMediaType> type;
+
+ UINT32 typeIndex = 0;
+ while (static_cast<void>(type = nullptr),
+ SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, &type))) {
+ GUID subtype;
+ hr = type->GetGUID(MF_MT_SUBTYPE, &subtype);
+ if (FAILED(hr)) {
+ continue;
+ }
+ if (subtype == MFVideoFormat_I420 || subtype == MFVideoFormat_IYUV) {
+ // On Windows 7 Enterprise N the MFT reports it reports IYUV instead
+ // of I420. Other Windows' report I420. The formats are the same, so
+ // support both.
+ hr = mDecoder->SetOutputType(0, type, 0);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = ConfigureVideoFrameGeometry(type);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ return S_OK;
+ }
+ }
+
+ return E_FAIL;
+}
+
+HRESULT
+WMFH264Decoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData) {
+ ENSURE(mDecoder != nullptr, E_POINTER);
+ HRESULT hr = mDecoder->ProcessMessage(aMsg, aData);
+ ENSURE(SUCCEEDED(hr), hr);
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::CreateInputSample(const uint8_t* aData, uint32_t aDataSize,
+ Microseconds aTimestamp,
+ IMFSample** aOutSample) {
+ HRESULT hr;
+ CComPtr<IMFSample> sample;
+ hr = MFCreateSample(&sample);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ CComPtr<IMFMediaBuffer> buffer;
+ int32_t bufferSize =
+ std::max<uint32_t>(uint32_t(mInputStreamInfo.cbSize), aDataSize);
+ UINT32 alignment =
+ (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0;
+ hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ DWORD maxLength = 0;
+ DWORD currentLength = 0;
+ BYTE* dst = nullptr;
+ hr = buffer->Lock(&dst, &maxLength, &currentLength);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ // Copy data into sample's buffer.
+ memcpy(dst, aData, aDataSize);
+
+ hr = buffer->Unlock();
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = buffer->SetCurrentLength(aDataSize);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = sample->AddBuffer(buffer);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = sample->SetSampleTime(UsecsToHNs(aTimestamp));
+ ENSURE(SUCCEEDED(hr), hr);
+
+ *aOutSample = sample.Detach();
+
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::CreateOutputSample(IMFSample** aOutSample) {
+ HRESULT hr;
+ CComPtr<IMFSample> sample;
+ hr = MFCreateSample(&sample);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ CComPtr<IMFMediaBuffer> buffer;
+ int32_t bufferSize = mOutputStreamInfo.cbSize;
+ UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1)
+ ? mOutputStreamInfo.cbAlignment - 1
+ : 0;
+ hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = sample->AddBuffer(buffer);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ *aOutSample = sample.Detach();
+
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::GetOutputSample(IMFSample** aOutSample) {
+ HRESULT hr;
+ // We allocate samples for MFT output.
+ MFT_OUTPUT_DATA_BUFFER output = {0};
+
+ CComPtr<IMFSample> sample;
+ hr = CreateOutputSample(&sample);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ output.pSample = sample;
+
+ DWORD status = 0;
+ hr = mDecoder->ProcessOutput(0, 1, &output, &status);
+ // LOG(L"WMFH264Decoder::GetOutputSample() ProcessOutput returned 0x%x\n",
+ // hr);
+ CComPtr<IMFCollection> events = output.pEvents; // Ensure this is released.
+
+ if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
+ // Type change. Probably geometric apperature change.
+ hr = SetDecoderOutputType();
+ ENSURE(SUCCEEDED(hr), hr);
+
+ return GetOutputSample(aOutSample);
+ } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
+ return MF_E_TRANSFORM_NEED_MORE_INPUT;
+ }
+ // Treat other errors as fatal.
+ ENSURE(SUCCEEDED(hr), hr);
+
+ assert(sample);
+
+ // output.pSample
+ *aOutSample = sample.Detach(); // AddRefs
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::Input(const uint8_t* aData, uint32_t aDataSize,
+ Microseconds aTimestamp) {
+ HRESULT hr;
+ CComPtr<IMFSample> input = nullptr;
+ hr = CreateInputSample(aData, aDataSize, aTimestamp, &input);
+ ENSURE(SUCCEEDED(hr) && input != nullptr, hr);
+
+ hr = mDecoder->ProcessInput(0, input, 0);
+ if (hr == MF_E_NOTACCEPTING) {
+ // MFT *already* has enough data to produce a sample. Retrieve it.
+ LOG("ProcessInput returned MF_E_NOTACCEPTING\n");
+ return MF_E_NOTACCEPTING;
+ }
+ ENSURE(SUCCEEDED(hr), hr);
+
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::Output(IMFSample** aOutput) {
+ HRESULT hr;
+ CComPtr<IMFSample> outputSample;
+ hr = GetOutputSample(&outputSample);
+ if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
+ return MF_E_TRANSFORM_NEED_MORE_INPUT;
+ }
+ // Treat other errors as fatal.
+ ENSURE(SUCCEEDED(hr) && outputSample, hr);
+
+ *aOutput = outputSample.Detach();
+
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::Reset() {
+ HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ return S_OK;
+}
+
+HRESULT
+WMFH264Decoder::Drain() {
+ HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ return S_OK;
+}
+
+} // namespace wmf
diff --git a/media/gmp-clearkey/0.1/WMFH264Decoder.h b/media/gmp-clearkey/0.1/WMFH264Decoder.h
new file mode 100644
index 0000000000..3f74735e23
--- /dev/null
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(WMFH264Decoder_h_)
+# define WMFH264Decoder_h_
+
+# include "WMFUtils.h"
+
+namespace wmf {
+
+class WMFH264Decoder {
+ public:
+ WMFH264Decoder();
+ ~WMFH264Decoder();
+
+ HRESULT Init(int32_t aCoreCount);
+
+ HRESULT Input(const uint8_t* aData, uint32_t aDataSize,
+ Microseconds aTimestamp);
+
+ HRESULT Output(IMFSample** aOutput);
+
+ HRESULT Reset();
+
+ int32_t GetFrameHeight() const;
+ const IntRect& GetPictureRegion() const;
+ int32_t GetStride() const;
+
+ HRESULT Drain();
+
+ private:
+ HRESULT SetDecoderInputType();
+ HRESULT SetDecoderOutputType();
+ HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData);
+
+ HRESULT CreateInputSample(const uint8_t* aData, uint32_t aDataSize,
+ Microseconds aTimestamp, IMFSample** aOutSample);
+
+ HRESULT CreateOutputSample(IMFSample** aOutSample);
+
+ HRESULT GetOutputSample(IMFSample** aOutSample);
+ HRESULT ConfigureVideoFrameGeometry(IMFMediaType* aMediaType);
+
+ MFT_INPUT_STREAM_INFO mInputStreamInfo;
+ MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
+
+ CComPtr<IMFTransform> mDecoder;
+
+ int32_t mVideoWidth;
+ int32_t mVideoHeight;
+ IntRect mPictureRegion;
+ int32_t mStride;
+};
+
+} // namespace wmf
+
+#endif
diff --git a/media/gmp-clearkey/0.1/WMFSymbols.h b/media/gmp-clearkey/0.1/WMFSymbols.h
new file mode 100644
index 0000000000..60a0a6854b
--- /dev/null
+++ b/media/gmp-clearkey/0.1/WMFSymbols.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+MFPLAT_FUNC(MFCreateSample, "mfplat.dll");
+MFPLAT_FUNC(MFCreateAlignedMemoryBuffer, "mfplat.dll");
+MFPLAT_FUNC(MFGetStrideForBitmapInfoHeader, "evr.dll");
+MFPLAT_FUNC(MFCreateMediaType, "mfplat.dll");
diff --git a/media/gmp-clearkey/0.1/WMFUtils.cpp b/media/gmp-clearkey/0.1/WMFUtils.cpp
new file mode 100644
index 0000000000..5aa628f619
--- /dev/null
+++ b/media/gmp-clearkey/0.1/WMFUtils.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2013, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WMFUtils.h"
+
+#define INITGUID
+#include <guiddef.h>
+#include <stdio.h>
+#include <versionhelpers.h>
+
+#include <algorithm>
+
+#include "ClearKeyUtils.h"
+
+#ifndef __MINGW32__
+# pragma comment(lib, "mfuuid.lib")
+# pragma comment(lib, "wmcodecdspuuid")
+#endif
+
+void LOG(const char* format, ...) {
+#ifdef WMF_DECODER_LOG
+ va_list args;
+ va_start(args, format);
+ vprintf(format, args);
+#endif
+}
+
+DEFINE_GUID(CLSID_CMSH264DecMFT, 0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45,
+ 0x28, 0x31, 0xA8, 0x7D, 0x9D);
+
+namespace wmf {
+
+#define MFPLAT_FUNC(_func, _dllname) decltype(::_func)* _func;
+#include "WMFSymbols.h"
+#undef MFPLAT_FUNC
+
+static bool LinkMfplat() {
+ static bool sInitDone = false;
+ static bool sInitOk = false;
+ if (!sInitDone) {
+ sInitDone = true;
+ HMODULE handle;
+
+#define MFPLAT_FUNC(_func, _dllname) \
+ handle = GetModuleHandleA(_dllname); \
+ if (!(_func = (decltype(_func))(GetProcAddress(handle, #_func)))) { \
+ return false; \
+ }
+
+#include "WMFSymbols.h"
+#undef MFPLAT_FUNC
+ sInitOk = true;
+ }
+ return sInitOk;
+}
+
+bool EnsureLibs() {
+ static bool sInitDone = false;
+ static bool sInitOk = false;
+ if (!sInitDone) {
+ sInitOk = LinkMfplat() && !!GetModuleHandleA(WMFDecoderDllName());
+ sInitDone = true;
+ }
+ return sInitOk;
+}
+
+int32_t MFOffsetToInt32(const MFOffset& aOffset) {
+ return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
+}
+
+// Gets the sub-region of the video frame that should be displayed.
+// See:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
+HRESULT
+GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion) {
+ // Determine if "pan and scan" is enabled for this media. If it is, we
+ // only display a region of the video frame, not the entire frame.
+ BOOL panScan =
+ MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
+
+ // If pan and scan mode is enabled. Try to get the display region.
+ HRESULT hr = E_FAIL;
+ MFVideoArea videoArea;
+ memset(&videoArea, 0, sizeof(MFVideoArea));
+ if (panScan) {
+ hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)&videoArea,
+ sizeof(MFVideoArea), NULL);
+ }
+
+ // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
+ // check for a minimimum display aperture.
+ if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
+ hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&videoArea,
+ sizeof(MFVideoArea), NULL);
+ }
+
+ if (hr == MF_E_ATTRIBUTENOTFOUND) {
+ // Minimum display aperture is not set, for "backward compatibility with
+ // some components", check for a geometric aperture.
+ hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)&videoArea,
+ sizeof(MFVideoArea), NULL);
+ }
+
+ if (SUCCEEDED(hr)) {
+ // The media specified a picture region, return it.
+ IntRect picture = IntRect(MFOffsetToInt32(videoArea.OffsetX),
+ MFOffsetToInt32(videoArea.OffsetY),
+ videoArea.Area.cx, videoArea.Area.cy);
+ ENSURE(picture.width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
+ ENSURE(picture.height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
+ aOutPictureRegion = picture;
+ return S_OK;
+ }
+
+ // No picture region defined, fall back to using the entire video area.
+ UINT32 width = 0, height = 0;
+ hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
+ ENSURE(SUCCEEDED(hr), hr);
+ ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
+ ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
+ aOutPictureRegion = IntRect(0, 0, width, height);
+ return S_OK;
+}
+
+HRESULT
+GetDefaultStride(IMFMediaType* aType, uint32_t* aOutStride) {
+ // Try to get the default stride from the media type.
+ UINT32 stride = 0;
+ HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride);
+ if (SUCCEEDED(hr)) {
+ ENSURE(stride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
+ *aOutStride = stride;
+ return S_OK;
+ }
+
+ // Stride attribute not set, calculate it.
+ GUID subtype = GUID_NULL;
+ uint32_t width = 0;
+ uint32_t height = 0;
+
+ hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
+ ENSURE(SUCCEEDED(hr), hr);
+
+ hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
+ ENSURE(SUCCEEDED(hr), hr);
+ ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
+ ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
+
+ LONG lstride = 0;
+ hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lstride);
+ ENSURE(SUCCEEDED(hr), hr);
+ ENSURE(lstride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
+ ENSURE(lstride >= 0, E_FAIL);
+ *aOutStride = lstride;
+
+ return hr;
+}
+
+void dump(const uint8_t* data, uint32_t len, const char* filename) {
+ FILE* f = 0;
+ fopen_s(&f, filename, "wb");
+ fwrite(data, len, 1, f);
+ fclose(f);
+}
+
+HRESULT
+CreateMFT(const CLSID& clsid, const char* aDllName,
+ CComPtr<IMFTransform>& aOutMFT) {
+ HMODULE module = ::GetModuleHandleA(aDllName);
+ if (!module) {
+ LOG("Failed to get %S\n", aDllName);
+ return E_FAIL;
+ }
+
+ typedef HRESULT(WINAPI * DllGetClassObjectFnPtr)(
+ const CLSID& clsid, const IID& iid, void** object);
+
+ DllGetClassObjectFnPtr GetClassObjPtr =
+ reinterpret_cast<DllGetClassObjectFnPtr>(
+ GetProcAddress(module, "DllGetClassObject"));
+ if (!GetClassObjPtr) {
+ LOG("Failed to get DllGetClassObject\n");
+ return E_FAIL;
+ }
+
+ CComPtr<IClassFactory> classFactory;
+ HRESULT hr = GetClassObjPtr(
+ clsid, __uuidof(IClassFactory),
+ reinterpret_cast<void**>(static_cast<IClassFactory**>(&classFactory)));
+ if (FAILED(hr)) {
+ LOG("Failed to get H264 IClassFactory\n");
+ return E_FAIL;
+ }
+
+ hr = classFactory->CreateInstance(
+ NULL, __uuidof(IMFTransform),
+ reinterpret_cast<void**>(static_cast<IMFTransform**>(&aOutMFT)));
+ if (FAILED(hr)) {
+ LOG("Failed to get create MFT\n");
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+int32_t GetNumThreads(int32_t aCoreCount) {
+ return aCoreCount > 4 ? -1 : (std::max)(aCoreCount - 1, 1);
+}
+
+} // namespace wmf
diff --git a/media/gmp-clearkey/0.1/WMFUtils.h b/media/gmp-clearkey/0.1/WMFUtils.h
new file mode 100644
index 0000000000..0c2819e25c
--- /dev/null
+++ b/media/gmp-clearkey/0.1/WMFUtils.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2013, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __WMFUtils_h__
+#define __WMFUtils_h__
+
+#include <assert.h>
+#include <mfapi.h>
+#include <mferror.h>
+#include <mfobjects.h>
+#include <mftransform.h>
+#include <wmcodecdsp.h>
+
+#include <cstdint>
+#include <string>
+
+#include "VideoLimits.h"
+#include "content_decryption_module.h"
+#include "gmp-platform.h"
+#include "mozilla/Attributes.h"
+
+void LOG(const char* format, ...);
+
+#ifdef LOG_SAMPLE_DECODE
+# define SAMPLE_LOG LOG
+#else
+# define SAMPLE_LOG(...)
+#endif
+
+#ifndef CLSID_CMSAACDecMFT
+# define WMF_MUST_DEFINE_AAC_MFT_CLSID
+extern "C" const CLSID CLSID_CMSAACDecMFT;
+#endif
+
+extern "C" const CLSID CLSID_CMSH264DecMFT;
+
+namespace wmf {
+
+// Reimplementation of CComPtr to reduce dependence on system
+// shared libraries.
+template <class T>
+class CComPtr {
+ public:
+ CComPtr(CComPtr&& aOther) : mPtr(aOther.Detach()) {}
+ CComPtr& operator=(CComPtr&& other) { mPtr = other.Detach(); }
+
+ CComPtr(const CComPtr& aOther) : mPtr(nullptr) { Set(aOther.Get()); }
+ CComPtr() : mPtr(nullptr) {}
+ MOZ_IMPLICIT CComPtr(T* const& aPtr) : mPtr(nullptr) { Set(aPtr); }
+ MOZ_IMPLICIT CComPtr(const std::nullptr_t& aNullPtr) : mPtr(aNullPtr) {}
+ T** operator&() { return &mPtr; }
+ T* operator->() { return mPtr; }
+ operator T*() { return mPtr; }
+ T* operator=(T* const& aPtr) { return Set(aPtr); }
+ T* operator=(const std::nullptr_t& aPtr) { return mPtr = aPtr; }
+
+ T* Get() const { return mPtr; }
+
+ T* Detach() {
+ T* tmp = mPtr;
+ mPtr = nullptr;
+ return tmp;
+ }
+
+ ~CComPtr() {
+ if (mPtr) {
+ mPtr->Release();
+ }
+ mPtr = nullptr;
+ }
+
+ private:
+ T* Set(T* aPtr) {
+ if (mPtr == aPtr) {
+ return aPtr;
+ }
+ if (mPtr) {
+ mPtr->Release();
+ }
+ mPtr = aPtr;
+ if (mPtr) {
+ mPtr->AddRef();
+ }
+ return mPtr;
+ }
+
+ T* mPtr;
+};
+
+class IntRect {
+ public:
+ IntRect(int32_t _x, int32_t _y, int32_t _w, int32_t _h)
+ : x(_x), y(_y), width(_w), height(_h) {}
+ IntRect() : x(0), y(0), width(0), height(0) {}
+ int32_t x;
+ int32_t y;
+ int32_t width;
+ int32_t height;
+};
+
+typedef int64_t Microseconds;
+
+#ifdef ENSURE
+# undef ENSURE
+#endif
+
+#define ENSURE(condition, ret) \
+ { \
+ if (!(condition)) { \
+ LOG("##condition## FAILED %S:%d\n", __FILE__, __LINE__); \
+ return ret; \
+ } \
+ }
+
+inline bool STATUS_SUCCEEDED(cdm::Status status) {
+ return status == cdm::Status::kSuccess;
+}
+inline bool STATUS_FAILED(cdm::Status status) {
+ return !STATUS_SUCCEEDED(status);
+}
+
+#define MFPLAT_FUNC(_func, _dllname) extern decltype(::_func)* _func;
+#include "WMFSymbols.h"
+#undef MFPLAT_FUNC
+
+bool EnsureLibs();
+
+HRESULT
+GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion);
+
+HRESULT
+GetDefaultStride(IMFMediaType* aType, uint32_t* aOutStride);
+
+// Converts from microseconds to hundreds of nanoseconds.
+// We use microseconds for our timestamps, whereas WMF uses
+// hundreds of nanoseconds.
+inline int64_t UsecsToHNs(int64_t aUsecs) { return aUsecs * 10; }
+
+// Converts from hundreds of nanoseconds to microseconds.
+// We use microseconds for our timestamps, whereas WMF uses
+// hundreds of nanoseconds.
+inline int64_t HNsToUsecs(int64_t hNanoSecs) { return hNanoSecs / 10; }
+
+inline std::string narrow(std::wstring& wide) {
+ std::string ns(wide.begin(), wide.end());
+ return ns;
+}
+
+inline std::wstring widen(std::string& narrow) {
+ std::wstring ws(narrow.begin(), narrow.end());
+ return ws;
+}
+
+#define ARRAY_LENGTH(array_) (sizeof(array_) / sizeof(array_[0]))
+
+template <class Type>
+class AutoPtr {
+ public:
+ AutoPtr() : mPtr(nullptr) {}
+
+ AutoPtr(AutoPtr<Type>& aPtr) : mPtr(aPtr.Forget()) {}
+
+ explicit AutoPtr(Type* aPtr) : mPtr(aPtr) {}
+
+ ~AutoPtr() {
+ if (mPtr) {
+ delete mPtr;
+ }
+ }
+
+ Type* Forget() {
+ Type* rv = mPtr;
+ mPtr = nullptr;
+ return rv;
+ }
+
+ AutoPtr<Type>& operator=(Type* aOther) {
+ Assign(aOther);
+ return *this;
+ }
+
+ AutoPtr<Type>& operator=(AutoPtr<Type>& aOther) {
+ Assign(aOther.Forget());
+ return *this;
+ }
+
+ Type* Get() const { return mPtr; }
+
+ Type* operator->() const {
+ assert(mPtr);
+ return Get();
+ }
+
+ operator Type*() const { return Get(); }
+
+ Type** Receive() { return &mPtr; }
+
+ private:
+ void Assign(Type* aPtr) {
+ if (mPtr) {
+ delete mPtr;
+ }
+ mPtr = aPtr;
+ }
+
+ Type* mPtr;
+};
+
+// Video frame microseconds are (currently) in 90kHz units, as used by RTP.
+// Use this to convert to microseconds...
+inline Microseconds RTPTimeToMicroseconds(int64_t rtptime) {
+ return (rtptime * 1000000) / 90000;
+}
+
+inline uint32_t MicrosecondsToRTPTime(Microseconds us) {
+ return uint32_t(0xffffffff & (us * 90000) / 1000000);
+}
+
+void dump(const uint8_t* data, uint32_t len, const char* filename);
+
+HRESULT
+CreateMFT(const CLSID& clsid, const char* aDllName,
+ CComPtr<IMFTransform>& aOutMFT);
+
+// Returns the name of the DLL that is needed to decode H.264 on
+// the given windows version we're running on.
+inline const char* WMFDecoderDllName() { return "msmpeg2vdec.dll"; }
+
+// Returns the maximum number of threads we want WMF to use for decoding
+// given the number of logical processors available.
+int32_t GetNumThreads(int32_t aCoreCount);
+
+} // namespace wmf
+
+#endif // __WMFUtils_h__
diff --git a/media/gmp-clearkey/0.1/gmp-clearkey.cpp b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
new file mode 100644
index 0000000000..70c27602f7
--- /dev/null
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2015, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+// This include is required in order for content_decryption_module to work
+// on Unix systems.
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "content_decryption_module.h"
+#include "content_decryption_module_ext.h"
+#include "nss.h"
+
+#include "ClearKeyCDM.h"
+#include "ClearKeySessionManager.h"
+#include "mozilla/dom/KeySystemNames.h"
+
+#ifndef XP_WIN
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+#endif
+
+#ifdef ENABLE_WMF
+# include "WMFUtils.h"
+#endif // ENABLE_WMF
+
+extern "C" {
+
+CDM_API
+void INITIALIZE_CDM_MODULE() {}
+
+static bool sCanReadHostVerificationFiles = false;
+
+CDM_API
+void* CreateCdmInstance(int cdm_interface_version, const char* key_system,
+ uint32_t key_system_size,
+ GetCdmHostFunc get_cdm_host_func, void* user_data) {
+ CK_LOGE("ClearKey CreateCDMInstance");
+
+ if (cdm_interface_version != cdm::ContentDecryptionModule_10::kVersion) {
+ CK_LOGE(
+ "ClearKey CreateCDMInstance failed due to requesting unsupported "
+ "version %d.",
+ cdm_interface_version);
+ return nullptr;
+ }
+#ifdef ENABLE_WMF
+ if (!wmf::EnsureLibs()) {
+ CK_LOGE("Required libraries were not found");
+ return nullptr;
+ }
+#endif
+
+ if (NSS_NoDB_Init(nullptr) == SECFailure) {
+ CK_LOGE("Unable to initialize NSS");
+ return nullptr;
+ }
+
+#ifdef MOZILLA_OFFICIAL
+ // Test that we're able to read the host files.
+ if (!sCanReadHostVerificationFiles) {
+ return nullptr;
+ }
+#endif
+
+ cdm::Host_10* host = static_cast<cdm::Host_10*>(
+ get_cdm_host_func(cdm_interface_version, user_data));
+ ClearKeyCDM* clearKey = new ClearKeyCDM(host);
+
+ CK_LOGE("Created ClearKeyCDM instance!");
+
+ if (strncmp(key_system, mozilla::kClearKeyWithProtectionQueryKeySystemName,
+ key_system_size) == 0) {
+ CK_LOGE("Enabling protection query on ClearKeyCDM instance!");
+ clearKey->EnableProtectionQuery();
+ }
+
+ return clearKey;
+}
+
+const size_t TEST_READ_SIZE = 16 * 1024;
+
+bool CanReadSome(cdm::PlatformFile aFile) {
+ std::vector<uint8_t> data;
+ data.resize(TEST_READ_SIZE);
+#ifdef XP_WIN
+ DWORD bytesRead = 0;
+ return ReadFile(aFile, &data.front(), TEST_READ_SIZE, &bytesRead, nullptr) &&
+ bytesRead > 0;
+#else
+ return read(aFile, &data.front(), TEST_READ_SIZE) > 0;
+#endif
+}
+
+void ClosePlatformFile(cdm::PlatformFile aFile) {
+#ifdef XP_WIN
+ CloseHandle(aFile);
+#else
+ close(aFile);
+#endif
+}
+
+static uint32_t NumExpectedHostFiles(const cdm::HostFile* aHostFiles,
+ uint32_t aNumFiles) {
+#if !defined(XP_WIN)
+ // We expect 4 binaries: clearkey, libxul, plugin-container, and Firefox.
+ return 4;
+#else
+ // Windows running x64 or x86 natively should also have 4 as above.
+ // For Windows on ARM64, we run an x86 plugin-contianer process under
+ // emulation, and so we expect one additional binary; the x86
+ // xul.dll used by plugin-container.exe.
+ bool i686underAArch64 = false;
+ // Assume that we're running under x86 emulation on an aarch64 host if
+ // one of the paths ends with the x86 plugin-container path we'd expect.
+ const std::wstring plugincontainer = L"i686\\plugin-container.exe";
+ for (uint32_t i = 0; i < aNumFiles; i++) {
+ const cdm::HostFile& hostFile = aHostFiles[i];
+ if (hostFile.file != cdm::kInvalidPlatformFile) {
+ std::wstring path = hostFile.file_path;
+ auto offset = path.find(plugincontainer);
+ if (offset != std::string::npos &&
+ offset == path.size() - plugincontainer.size()) {
+ i686underAArch64 = true;
+ break;
+ }
+ }
+ }
+ return i686underAArch64 ? 5 : 4;
+#endif
+}
+
+CDM_API
+bool VerifyCdmHost_0(const cdm::HostFile* aHostFiles, uint32_t aNumFiles) {
+ // Check that we've received the expected number of host files.
+ bool rv = (aNumFiles == NumExpectedHostFiles(aHostFiles, aNumFiles));
+ // Verify that each binary is readable inside the sandbox,
+ // and close the handle.
+ for (uint32_t i = 0; i < aNumFiles; i++) {
+ const cdm::HostFile& hostFile = aHostFiles[i];
+ if (hostFile.file != cdm::kInvalidPlatformFile) {
+ if (!CanReadSome(hostFile.file)) {
+ rv = false;
+ }
+ ClosePlatformFile(hostFile.file);
+ }
+ if (hostFile.sig_file != cdm::kInvalidPlatformFile) {
+ ClosePlatformFile(hostFile.sig_file);
+ }
+ }
+ sCanReadHostVerificationFiles = rv;
+ return rv;
+}
+
+} // extern "C".
diff --git a/media/gmp-clearkey/0.1/manifest.json.in b/media/gmp-clearkey/0.1/manifest.json.in
new file mode 100644
index 0000000000..dd36af8556
--- /dev/null
+++ b/media/gmp-clearkey/0.1/manifest.json.in
@@ -0,0 +1,13 @@
+{
+ "name": "clearkey",
+ "description": "ClearKey Gecko Media Plugin",
+ "version": "1",
+ "x-cdm-module-versions": "4",
+ "x-cdm-interface-versions": "10",
+ "x-cdm-host-versions": "10",
+#ifdef ENABLE_WMF
+ "x-cdm-codecs": "avc1"
+#else
+ "x-cdm-codecs": ""
+#endif
+} \ No newline at end of file
diff --git a/media/gmp-clearkey/0.1/moz.build b/media/gmp-clearkey/0.1/moz.build
new file mode 100644
index 0000000000..ad393c1c25
--- /dev/null
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -0,0 +1,62 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Audio/Video: GMP")
+
+GeckoSharedLibrary("clearkey", linkage=None)
+
+FINAL_TARGET = "dist/bin/gmp-clearkey/0.1"
+
+FINAL_TARGET_PP_FILES += ["manifest.json.in"]
+
+USE_LIBS += ["gecko-clearkey"]
+
+UNIFIED_SOURCES += [
+ "ClearKeyCDM.cpp",
+ "gmp-clearkey.cpp",
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ UNIFIED_SOURCES += [
+ "VideoDecoder.cpp",
+ "WMFH264Decoder.cpp",
+ ]
+
+ SOURCES += [
+ "WMFUtils.cpp",
+ ]
+
+ OS_LIBS += [
+ "mfuuid",
+ "uuid",
+ ]
+
+ DEFINES["ENABLE_WMF"] = True
+
+DEFINES["CDM_IMPLEMENTATION"] = True
+
+DisableStlWrapping()
+DEFINES["MOZ_NO_MOZALLOC"] = True
+
+# Suppress warnings in third-party code.
+CFLAGS += [
+ "-Wno-pointer-to-int-cast",
+ "-Wno-sign-compare",
+]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CFLAGS += [
+ "-include",
+ "stdio.h", # for sprintf() prototype
+ "-include",
+ "unistd.h", # for getpid() prototype
+ ]
+elif CONFIG["CC_TYPE"] == "clang-cl":
+ CFLAGS += [
+ "-FI",
+ "stdio.h", # for sprintf() prototype
+ ]