Automatically generated with:
cp dom/.clang-format media/ && mach format media/**.{cpp,h}
No manual changes required.
Differential Revision: https://phabricator.services.mozilla.com/D311686
412 lines
13 KiB
C++
412 lines
13 KiB
C++
/* 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 "WMFClearKeyDecryptor.h"
|
|
|
|
#include <mfapi.h>
|
|
#include <mferror.h>
|
|
|
|
#include <vector>
|
|
|
|
#include "WMFClearKeyCDM.h"
|
|
#include "WMFDecryptedBlock.h"
|
|
#include "content_decryption_module.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
WMFClearKeyDecryptor::~WMFClearKeyDecryptor() { ENTRY_LOG(); }
|
|
|
|
HRESULT WMFClearKeyDecryptor::RuntimeClassInitialize(
|
|
SessionManagerWrapper* aSessionManager) {
|
|
ENTRY_LOG();
|
|
MOZ_ASSERT(aSessionManager);
|
|
mSessionManager = aSessionManager;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetStreamLimits(DWORD* aInputMin,
|
|
DWORD* aInputMax,
|
|
DWORD* aOutputMin,
|
|
DWORD* aOutputMax) {
|
|
*aInputMin = *aInputMax = *aOutputMin = *aOutputMax = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetStreamCount(DWORD* aInputStreams,
|
|
DWORD* aOutputStreams) {
|
|
*aInputStreams = *aOutputStreams = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetStreamIDs(DWORD aInputSize,
|
|
DWORD* aInputIDs,
|
|
DWORD aOutputSize,
|
|
DWORD* aOutputIDs) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetInputStreamInfo(
|
|
DWORD aInputStreamID, MFT_INPUT_STREAM_INFO* aStreamInfo) {
|
|
if (aInputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
aStreamInfo->hnsMaxLatency = 0;
|
|
aStreamInfo->dwFlags = 0;
|
|
aStreamInfo->cbSize = 0;
|
|
aStreamInfo->cbMaxLookahead = 0;
|
|
aStreamInfo->cbAlignment = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetOutputStreamInfo(
|
|
DWORD aOutputStreamID, MFT_OUTPUT_STREAM_INFO* aStreamInfo) {
|
|
if (aOutputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
aStreamInfo->dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;
|
|
aStreamInfo->cbSize = 0;
|
|
aStreamInfo->cbAlignment = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetAttributes(IMFAttributes** aAttributes) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetInputStreamAttributes(
|
|
DWORD aInputStreamID, IMFAttributes** aAttributes) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetOutputStreamAttributes(
|
|
DWORD aOutputStreamID, IMFAttributes** aAttributes) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::DeleteInputStream(DWORD aStreamID) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::AddInputStreams(DWORD aStreams,
|
|
DWORD* aStreamIDs) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetInputAvailableType(DWORD aInputStreamID,
|
|
DWORD aTypeIndex,
|
|
IMFMediaType** aType) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (aInputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
if (aTypeIndex != 0 || !mInputType) {
|
|
return MF_E_NO_MORE_TYPES;
|
|
}
|
|
return mInputType.CopyTo(aType);
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetOutputAvailableType(
|
|
DWORD aOutputStreamID, DWORD aTypeIndex, IMFMediaType** aType) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (aOutputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
if (aTypeIndex != 0 || !mOutputType) {
|
|
return MF_E_NO_MORE_TYPES;
|
|
}
|
|
return mOutputType.CopyTo(aType);
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::SetInputType(DWORD aInputStreamID,
|
|
IMFMediaType* aType,
|
|
DWORD aFlags) {
|
|
if (aInputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (aFlags & MFT_SET_TYPE_TEST_ONLY) {
|
|
return S_OK;
|
|
}
|
|
// Unwrap the protected media type to get the real underlying media type.
|
|
ComPtr<IMFMediaType> unwrapped;
|
|
if (aType && SUCCEEDED(MFUnwrapMediaType(aType, &unwrapped))) {
|
|
mInputType = unwrapped;
|
|
} else {
|
|
mInputType = aType;
|
|
}
|
|
if (!mOutputType && mInputType) {
|
|
// Mirror input type as output type.
|
|
RETURN_IF_FAILED(MFCreateMediaType(&mOutputType));
|
|
RETURN_IF_FAILED(mInputType->CopyAllItems(mOutputType.Get()));
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::SetOutputType(DWORD aOutputStreamID,
|
|
IMFMediaType* aType,
|
|
DWORD aFlags) {
|
|
if (aOutputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (aFlags & MFT_SET_TYPE_TEST_ONLY) {
|
|
return S_OK;
|
|
}
|
|
mOutputType = aType;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetInputCurrentType(DWORD aInputStreamID,
|
|
IMFMediaType** aType) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (aInputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
if (!mInputType) {
|
|
return MF_E_TRANSFORM_TYPE_NOT_SET;
|
|
}
|
|
return mInputType.CopyTo(aType);
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetOutputCurrentType(DWORD aOutputStreamID,
|
|
IMFMediaType** aType) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (aOutputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
if (!mOutputType) {
|
|
return MF_E_TRANSFORM_TYPE_NOT_SET;
|
|
}
|
|
return mOutputType.CopyTo(aType);
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetInputStatus(DWORD aInputStreamID,
|
|
DWORD* aFlags) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (aInputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
*aFlags = mInputSample ? 0 : MFT_INPUT_STATUS_ACCEPT_DATA;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::GetOutputStatus(DWORD* aFlags) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
*aFlags = mInputSample ? MFT_OUTPUT_STATUS_SAMPLE_READY : 0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::SetOutputBounds(LONGLONG aLowerBound,
|
|
LONGLONG aUpperBound) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::ProcessEvent(DWORD aInputStreamID,
|
|
IMFMediaEvent* aEvent) {
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::ProcessMessage(MFT_MESSAGE_TYPE aMessage,
|
|
ULONG_PTR aParam) {
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::ProcessInput(DWORD aInputStreamID,
|
|
IMFSample* aSample,
|
|
DWORD aFlags) {
|
|
ENTRY_LOG();
|
|
if (aInputStreamID != 0) {
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
if (!aSample) {
|
|
return E_INVALIDARG;
|
|
}
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (mInputSample) {
|
|
return MF_E_NOTACCEPTING;
|
|
}
|
|
mInputSample = aSample;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WMFClearKeyDecryptor::DecryptSample(IMFSample* aEncryptedSample,
|
|
IMFSample** aDecryptedSample) {
|
|
ENTRY_LOG();
|
|
// Extract IV (stored as the SampleID blob, 16 bytes).
|
|
BYTE* ivData = nullptr;
|
|
UINT32 ivSize = 0;
|
|
HRESULT hr = aEncryptedSample->GetAllocatedBlob(
|
|
MFSampleExtension_Encryption_SampleID, &ivData, &ivSize);
|
|
const bool isEncrypted = SUCCEEDED(hr) && ivSize > 0;
|
|
LOG("isEncrypted=%d, ivSize=%u", isEncrypted, ivSize);
|
|
|
|
// Copy all buffer data from the sample.
|
|
DWORD totalLen = 0;
|
|
RETURN_IF_FAILED(aEncryptedSample->GetTotalLength(&totalLen));
|
|
|
|
std::vector<uint8_t> sampleData(totalLen);
|
|
{
|
|
ComPtr<IMFMediaBuffer> contiguousBuffer;
|
|
RETURN_IF_FAILED(
|
|
aEncryptedSample->ConvertToContiguousBuffer(&contiguousBuffer));
|
|
BYTE* bufferData = nullptr;
|
|
DWORD currentLen = 0;
|
|
RETURN_IF_FAILED(contiguousBuffer->Lock(&bufferData, nullptr, ¤tLen));
|
|
if (currentLen > 0) {
|
|
memcpy(sampleData.data(), bufferData, currentLen);
|
|
}
|
|
contiguousBuffer->Unlock();
|
|
}
|
|
|
|
WMFDecryptedBlock decryptedBlock;
|
|
|
|
if (isEncrypted) {
|
|
// Extract key ID (stored as a GUID).
|
|
GUID keyIdGuid = GUID_NULL;
|
|
RETURN_IF_FAILED(
|
|
aEncryptedSample->GetGUID(MFSampleExtension_Content_KeyID, &keyIdGuid));
|
|
uint8_t keyId[16];
|
|
GuidToKeyId(keyIdGuid, keyId);
|
|
|
|
// Extract subsample mapping if present.
|
|
BYTE* subsampleData = nullptr;
|
|
UINT32 subsampleDataSize = 0;
|
|
std::vector<cdm::SubsampleEntry> subsamples;
|
|
if (SUCCEEDED(aEncryptedSample->GetAllocatedBlob(
|
|
MFSampleExtension_Encryption_SubSample_Mapping, &subsampleData,
|
|
&subsampleDataSize)) &&
|
|
subsampleDataSize >= 8) {
|
|
// Each entry is {DWORD clearBytes, DWORD cipherBytes} = 8 bytes.
|
|
const DWORD numEntries = subsampleDataSize / 8;
|
|
subsamples.resize(numEntries);
|
|
for (DWORD i = 0; i < numEntries; i++) {
|
|
DWORD clearBytes = 0, cipherBytes = 0;
|
|
memcpy(&clearBytes, subsampleData + i * 8, sizeof(DWORD));
|
|
memcpy(&cipherBytes, subsampleData + i * 8 + sizeof(DWORD),
|
|
sizeof(DWORD));
|
|
subsamples[i].clear_bytes = clearBytes;
|
|
subsamples[i].cipher_bytes = cipherBytes;
|
|
}
|
|
CoTaskMemFree(subsampleData);
|
|
} else {
|
|
if (subsampleData) {
|
|
CoTaskMemFree(subsampleData);
|
|
}
|
|
// No subsample info: treat the whole buffer as cipher bytes.
|
|
cdm::SubsampleEntry entry;
|
|
entry.clear_bytes = 0;
|
|
entry.cipher_bytes = totalLen;
|
|
subsamples.push_back(entry);
|
|
}
|
|
|
|
LONGLONG sampleTime = 0;
|
|
aEncryptedSample->GetSampleTime(&sampleTime);
|
|
|
|
cdm::InputBuffer_2 inputBuffer = {};
|
|
inputBuffer.data = sampleData.data();
|
|
inputBuffer.data_size = static_cast<uint32_t>(sampleData.size());
|
|
inputBuffer.encryption_scheme = cdm::EncryptionScheme::kCenc;
|
|
inputBuffer.key_id = keyId;
|
|
inputBuffer.key_id_size = 16;
|
|
inputBuffer.iv = ivData;
|
|
inputBuffer.iv_size = ivSize;
|
|
inputBuffer.subsamples = subsamples.data();
|
|
inputBuffer.num_subsamples = static_cast<uint32_t>(subsamples.size());
|
|
inputBuffer.timestamp = sampleTime;
|
|
|
|
HRESULT decryptHr = mSessionManager->Decrypt(inputBuffer, &decryptedBlock);
|
|
LOG("Decrypt hr=%lx", static_cast<long>(decryptHr));
|
|
CoTaskMemFree(ivData);
|
|
RETURN_IF_FAILED(decryptHr);
|
|
} else {
|
|
if (ivData) {
|
|
CoTaskMemFree(ivData);
|
|
}
|
|
LOG("Clear sample passthrough, size=%lu", totalLen);
|
|
WMFDecryptedBuffer* buffer =
|
|
new WMFDecryptedBuffer(static_cast<uint32_t>(sampleData.size()));
|
|
memcpy(buffer->Data(), sampleData.data(), sampleData.size());
|
|
decryptedBlock.SetDecryptedBuffer(buffer);
|
|
}
|
|
|
|
cdm::Buffer* decryptedBuffer = decryptedBlock.DecryptedBuffer();
|
|
if (!decryptedBuffer || !decryptedBuffer->Data()) {
|
|
LOG("Decrypt produced null or empty buffer");
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Wrap decrypted data in a new IMFSample.
|
|
ComPtr<IMFSample> outputSample;
|
|
RETURN_IF_FAILED(MFCreateSample(&outputSample));
|
|
|
|
ComPtr<IMFMediaBuffer> outputBuffer;
|
|
RETURN_IF_FAILED(MFCreateMemoryBuffer(
|
|
static_cast<DWORD>(decryptedBuffer->Size()), &outputBuffer));
|
|
|
|
BYTE* outputData = nullptr;
|
|
RETURN_IF_FAILED(outputBuffer->Lock(&outputData, nullptr, nullptr));
|
|
memcpy(outputData, decryptedBuffer->Data(), decryptedBuffer->Size());
|
|
outputBuffer->Unlock();
|
|
RETURN_IF_FAILED(outputBuffer->SetCurrentLength(
|
|
static_cast<DWORD>(decryptedBuffer->Size())));
|
|
|
|
RETURN_IF_FAILED(outputSample->AddBuffer(outputBuffer.Get()));
|
|
|
|
LONGLONG sampleTime = 0;
|
|
LONGLONG sampleDuration = 0;
|
|
if (SUCCEEDED(aEncryptedSample->GetSampleTime(&sampleTime))) {
|
|
outputSample->SetSampleTime(sampleTime);
|
|
}
|
|
if (SUCCEEDED(aEncryptedSample->GetSampleDuration(&sampleDuration))) {
|
|
outputSample->SetSampleDuration(sampleDuration);
|
|
}
|
|
|
|
// Propagate the keyframe flag so the downstream H264 decoder knows which
|
|
// samples are IDR frames.
|
|
UINT32 isCleanPoint = 0;
|
|
if (SUCCEEDED(aEncryptedSample->GetUINT32(MFSampleExtension_CleanPoint,
|
|
&isCleanPoint)) &&
|
|
isCleanPoint) {
|
|
outputSample->SetUINT32(MFSampleExtension_CleanPoint, 1);
|
|
}
|
|
|
|
*aDecryptedSample = outputSample.Detach();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP WMFClearKeyDecryptor::ProcessOutput(
|
|
DWORD aFlags, DWORD aOutputBufferCount,
|
|
MFT_OUTPUT_DATA_BUFFER* aOutputSamples, DWORD* aStatus) {
|
|
ENTRY_LOG();
|
|
*aStatus = 0;
|
|
|
|
if (aOutputBufferCount != 1) {
|
|
LOG("Invalid output buffer count: %lu", aOutputBufferCount);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ComPtr<IMFSample> encryptedSample;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (!mInputSample) {
|
|
LOG("No input sample available");
|
|
return MF_E_TRANSFORM_NEED_MORE_INPUT;
|
|
}
|
|
encryptedSample = std::move(mInputSample);
|
|
}
|
|
|
|
// The MFT allocates output samples (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES).
|
|
MOZ_ASSERT(!aOutputSamples[0].pSample);
|
|
|
|
ComPtr<IMFSample> decryptedSample;
|
|
RETURN_IF_FAILED(DecryptSample(encryptedSample.Get(), &decryptedSample));
|
|
|
|
aOutputSamples[0].pSample = decryptedSample.Detach();
|
|
return S_OK;
|
|
}
|
|
|
|
} // namespace mozilla
|