blob: f67376c4d9fe19b64b7f7d8d61a1d2067be95549 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/cdm/win/test/media_foundation_clear_key_decryptor.h"
#include <mfapi.h>
#include <mferror.h>
#include "base/functional/callback.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/scoped_propvariant.h"
#include "media/base/decoder_buffer.h"
#include "media/base/subsample_entry.h"
#include "media/base/win/mf_helpers.h"
#include "media/cdm/win/test/media_foundation_clear_key_guids.h"
#include "media/filters/win/media_foundation_utils.h"
namespace media {
namespace {
#define ENUM_TO_STRING(enum) \
case enum: \
return #enum
std::string MessageTypeToString(MFT_MESSAGE_TYPE event) {
switch (event) {
ENUM_TO_STRING(MFT_MESSAGE_COMMAND_DRAIN);
ENUM_TO_STRING(MFT_MESSAGE_COMMAND_MARKER);
ENUM_TO_STRING(MFT_MESSAGE_COMMAND_FLUSH);
ENUM_TO_STRING(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING);
ENUM_TO_STRING(MFT_MESSAGE_NOTIFY_END_STREAMING);
ENUM_TO_STRING(MFT_MESSAGE_NOTIFY_START_OF_STREAM);
ENUM_TO_STRING(MFT_MESSAGE_NOTIFY_END_OF_STREAM);
ENUM_TO_STRING(MFT_MESSAGE_NOTIFY_RELEASE_RESOURCES);
ENUM_TO_STRING(MFT_MESSAGE_NOTIFY_REACQUIRE_RESOURCES);
ENUM_TO_STRING(MFT_MESSAGE_SET_D3D_MANAGER);
ENUM_TO_STRING(MFT_MESSAGE_NOTIFY_EVENT);
ENUM_TO_STRING(MFT_MESSAGE_DROP_SAMPLES);
ENUM_TO_STRING(MFT_MESSAGE_COMMAND_TICK);
ENUM_TO_STRING(MFT_MESSAGE_COMMAND_SET_OUTPUT_STREAM_STATE);
ENUM_TO_STRING(MFT_MESSAGE_COMMAND_FLUSH_OUTPUT_STREAM);
}
}
#undef ENUM_TO_STRING
} // namespace
using Microsoft::WRL::ComPtr;
MediaFoundationClearKeyDecryptor::MediaFoundationClearKeyDecryptor() = default;
MediaFoundationClearKeyDecryptor::~MediaFoundationClearKeyDecryptor() {
DVLOG_FUNC(1);
}
HRESULT MediaFoundationClearKeyDecryptor::RuntimeClassInitialize(
_In_ scoped_refptr<AesDecryptor> aes_decryptor) {
DVLOG_FUNC(1);
aes_decryptor_ = std::move(aes_decryptor);
return S_OK;
}
// IMFTransform
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetStreamLimits(
_Out_ DWORD* input_minimum,
_Out_ DWORD* input_maximum,
_Out_ DWORD* output_minimum,
_Out_ DWORD* output_maximum) {
DVLOG_FUNC(3);
CHECK(input_minimum);
CHECK(input_maximum);
CHECK(output_minimum);
CHECK(output_maximum);
*input_minimum = 1;
*input_maximum = 1;
*output_minimum = 1;
*output_maximum = 1;
return S_OK;
}
STDMETHODIMP
MediaFoundationClearKeyDecryptor::GetStreamCount(_Out_ DWORD* input_streams,
_Out_ DWORD* output_streams) {
DVLOG_FUNC(3);
CHECK(input_streams);
CHECK(output_streams);
*input_streams = 1;
*output_streams = 1;
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetStreamIDs(
_In_ DWORD input_ids_size,
_Out_ DWORD* input_ids,
_In_ DWORD output_ids_size,
_Out_ DWORD* output_ids) {
DVLOG_FUNC(3);
// Optional API. Expected to return E_NOTIMPL since the transform has a fixed
// number of streams and the streams are numbered consecutively from 0 to n-1.
// The same thing applies to GetInputStreamAttributes(),
// GetOutputStreamAttributes(), DeleteInputStream() and AddInputStreams()
// methods. See
// https://learn.microsoft.com/en-us/windows/win32/api/mftransform/nf-mftransform-imftransform-getstreamids.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::
MediaFoundationClearKeyDecryptor::GetInputStreamInfo(
_In_ DWORD input_stream_id,
_Out_ MFT_INPUT_STREAM_INFO* stream_info) {
DVLOG_FUNC(3);
CHECK(stream_info);
if (input_stream_id != 0) {
return MF_E_INVALIDSTREAMNUMBER;
}
*stream_info = {0};
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetOutputStreamInfo(
_In_ DWORD output_stream_id,
_Out_ MFT_OUTPUT_STREAM_INFO* stream_info) {
DVLOG_FUNC(3);
CHECK(stream_info);
if (output_stream_id != 0) {
return MF_E_INVALIDSTREAMNUMBER;
}
stream_info->dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;
stream_info->cbSize = 0;
stream_info->cbAlignment = 0;
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetAttributes(
_COM_Outptr_ IMFAttributes** attributes) {
DVLOG_FUNC(3);
// Even though there is an exception where Hardware-based MFTs must implement
// this method, we don't need it for now. However, we can implement it as
// needed when adding more browser tests or changing to an async
// MFT(`MF_TRANSFORM_ASYNC` to be TRUE). See
// https://learn.microsoft.com/en-us/windows/win32/api/mftransform/nf-mftransform-imftransform-getattributes
// Optional API. Expected to return E_NOTIMPL.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetInputStreamAttributes(
_In_ DWORD input_stream_id,
_COM_Outptr_ IMFAttributes** attributes) {
DVLOG_FUNC(3);
// Optional API. Expected to return E_NOTIMPL.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetOutputStreamAttributes(
_In_ DWORD output_stream_id,
_COM_Outptr_ IMFAttributes** attributes) {
DVLOG_FUNC(3);
// Optional API. Expected to return E_NOTIMPL.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::DeleteInputStream(
_In_ DWORD stream_id) {
DVLOG_FUNC(3);
// Optional API. Expected to return E_NOTIMPL.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::AddInputStreams(
_In_ DWORD streams_count,
_In_count_(streams_count) DWORD* stream_ids) {
DVLOG_FUNC(3);
// Optional API. Expected to return E_NOTIMPL.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetInputAvailableType(
_In_ DWORD input_stream_index,
_In_ DWORD type_index,
_COM_Outptr_ IMFMediaType** type) {
DVLOG_FUNC(3);
base::AutoLock lock(lock_);
if (input_stream_index != 0) {
return MF_E_INVALIDSTREAMNUMBER;
}
if (!input_media_type_) {
return E_NOTIMPL;
}
if (type_index != 0) {
return MF_E_NO_MORE_TYPES;
}
// Since the transform stores a media type internally, the transform should
// return a clone of the media type, not a pointer to the original type.
// Otherwise, the caller might modify the type and alter the internal state of
// the MFT.
ComPtr<IMFMediaType> media_type;
RETURN_IF_FAILED(MFCreateMediaType(&media_type));
RETURN_IF_FAILED(input_media_type_->CopyAllItems(media_type.Get()));
*type = media_type.Detach();
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetOutputAvailableType(
_In_ DWORD output_stream_index,
_In_ DWORD type_index,
_COM_Outptr_ IMFMediaType** type) {
DVLOG_FUNC(1);
base::AutoLock lock(lock_);
if (output_stream_index != 0) {
return MF_E_INVALIDSTREAMNUMBER;
}
if (!available_output_media_type_) {
return MF_E_TRANSFORM_TYPE_NOT_SET;
}
if (type_index != 0) {
return MF_E_NO_MORE_TYPES;
}
// Since the transform stores a media type internally, the transform should
// return a clone of the media type, not a pointer to the original type.
// Otherwise, the caller might modify the type and alter the internal state of
// the MFT.
ComPtr<IMFMediaType> media_type;
RETURN_IF_FAILED(MFCreateMediaType(&media_type));
RETURN_IF_FAILED(
available_output_media_type_->CopyAllItems(media_type.Get()));
*type = media_type.Detach();
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::SetInputType(
_In_ DWORD input_stream_index,
_In_ IMFMediaType* type,
_In_ DWORD flags) {
DVLOG_FUNC(1) << "input_stream_index=" << input_stream_index
<< ", flags=" << flags;
base::AutoLock lock(lock_);
if (input_stream_index != 0) {
return MF_E_INVALIDSTREAMNUMBER;
}
if (flags & MFT_SET_TYPE_TEST_ONLY) {
DVLOG_FUNC(3) << "MFT_SET_TYPE_TEST_ONLY";
return S_OK;
}
if (!type) {
return E_INVALIDARG;
}
input_media_type_ = type;
GUID guid_major_type = GUID_NULL;
ComPtr<IMFMediaType> unwrapped_media_type;
RETURN_IF_FAILED(input_media_type_->GetMajorType(&guid_major_type));
// Handle both clear and protected content and do NOT send an error if we
// get non-wrapped samples.
if (MFMediaType_Protected == guid_major_type) {
DVLOG_FUNC(3) << "MFMediaType_Protected";
RETURN_IF_FAILED(
MFUnwrapMediaType(input_media_type_.Get(), &unwrapped_media_type));
} else {
unwrapped_media_type = input_media_type_;
}
RETURN_IF_FAILED(unwrapped_media_type->GetMajorType(&guid_major_type));
if (guid_major_type == MFMediaType_Audio) {
DVLOG_FUNC(3) << "MFMediaType_Audio";
if (stream_type_ == StreamType::kVideo) {
// If we were already set up as video we can't change to audio.
return MF_E_INVALIDMEDIATYPE;
}
stream_type_ = StreamType::kAudio;
} else if (guid_major_type == MFMediaType_Video) {
DVLOG_FUNC(3) << "MFMediaType_Video";
GUID guid_sub_type = GUID_NULL;
if (stream_type_ == StreamType::kAudio) {
// If we were already set up as audio we can't change to video.
return MF_E_INVALIDMEDIATYPE;
}
stream_type_ = StreamType::kVideo;
RETURN_IF_FAILED(
unwrapped_media_type->GetGUID(MF_MT_SUBTYPE, &guid_sub_type));
}
available_output_media_type_ = unwrapped_media_type;
DVLOG_FUNC(3) << "stream_type_=" << static_cast<int>(stream_type_);
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::SetOutputType(
_In_ DWORD output_stream_index,
_In_ IMFMediaType* type,
_In_ DWORD flags) {
DVLOG_FUNC(1) << "output_stream_index=" << output_stream_index
<< ", flags=" << flags;
base::AutoLock lock(lock_);
if (output_stream_index != 0) {
return MF_E_INVALIDSTREAMNUMBER;
}
if (!available_output_media_type_) {
return MF_E_TRANSFORM_TYPE_NOT_SET;
}
if (!type) {
// Clear the media type if `type` is null.
output_media_type_ = nullptr;
return S_OK;
}
DWORD is_equal_flags = 0;
RETURN_IF_FAILED(
type->IsEqual(available_output_media_type_.Get(), &is_equal_flags));
if (!(is_equal_flags & MF_MEDIATYPE_EQUAL_MAJOR_TYPES)) {
return MF_E_INVALIDMEDIATYPE;
}
if (!(is_equal_flags & MF_MEDIATYPE_EQUAL_FORMAT_TYPES)) {
return MF_E_INVALIDMEDIATYPE;
}
if (!(flags & MFT_SET_TYPE_TEST_ONLY)) {
DVLOG_FUNC(3) << "!MFT_SET_TYPE_TEST_ONLY";
output_media_type_ = type;
} else {
DVLOG_FUNC(3) << "MFT_SET_TYPE_TEST_ONLY";
}
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetInputCurrentType(
_In_ DWORD input_stream_index,
_COM_Outptr_ IMFMediaType** type) {
DVLOG_FUNC(1) << "input_stream_index=" << input_stream_index;
base::AutoLock lock(lock_);
if (input_stream_index != 0) {
return MF_E_INVALIDSTREAMNUMBER;
} else if (input_media_type_ != nullptr) {
*type = input_media_type_.Get();
(*type)->AddRef();
} else {
return MF_E_TRANSFORM_TYPE_NOT_SET;
}
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetOutputCurrentType(
_In_ DWORD output_stream_index,
_COM_Outptr_ IMFMediaType** type) {
DVLOG_FUNC(1) << "output_stream_index=" << output_stream_index;
base::AutoLock lock(lock_);
if (output_stream_index != 0) {
return MF_E_INVALIDSTREAMNUMBER;
} else if (output_media_type_ != nullptr) {
*type = output_media_type_.Get();
(*type)->AddRef();
} else {
return MF_E_TRANSFORM_TYPE_NOT_SET;
}
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetInputStatus(
_In_ DWORD input_stream_index,
_Out_ DWORD* flags) {
DVLOG_FUNC(3) << "input_stream_index=" << input_stream_index;
// Optional API. It will be determined whether the transform can accept more
// data in the ProcessInput().
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetOutputStatus(
_Out_ DWORD* flags) {
DVLOG_FUNC(3);
// Optional API. It will be determined whether the transform has output data
// in the ProcessOutput().
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::SetOutputBounds(
_In_ LONGLONG lower_bound,
_In_ LONGLONG upper_bound) {
DVLOG_FUNC(3);
// Optional API. Expected to return E_NOTIMPL.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::ProcessEvent(
_In_ DWORD input_stream_id,
_In_ IMFMediaEvent* event) {
DVLOG_FUNC(3);
// API not required for synchronous MFTs.
return E_NOTIMPL;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::ProcessMessage(
_In_ MFT_MESSAGE_TYPE message,
_In_ ULONG_PTR param) {
DVLOG_FUNC(3) << "message=" << MessageTypeToString(message);
RETURN_IF_FAILED(GetShutdownStatus());
// TODO(crbug.com/1443014): Remove unused MFT message types here once the
// encrypted video playback and rendering is fully working.
switch (message) {
case MFT_MESSAGE_COMMAND_DRAIN:
// A synchronous MFT can ignore this message and return S_OK since:
// - The MFT never stores more than one input sample at a time.
// - Each input sample produces a single output sample.
break;
case MFT_MESSAGE_COMMAND_MARKER:
// This message applies only to Asynchronous MFTs.
break;
case MFT_MESSAGE_COMMAND_FLUSH:
// Flush all stored data. MFT should discard any media samples it is
// holding.
FlushAllStoredData();
break;
case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
// The streaming is about to start. The MFT can respond by allocating
// buffers or other resources before the first call to ProcessInput().
break;
case MFT_MESSAGE_NOTIFY_END_STREAMING:
// The streaming is about to end. The MFT can respond to this message by
// releasing buffers and other resources. The MFT does not flush input
// data or reset the media types in response to this message.
break;
case MFT_MESSAGE_NOTIFY_START_OF_STREAM:
// The first sample is about to be processed. A synchronous MFT is not
// required to respond to the message.
break;
case MFT_MESSAGE_NOTIFY_END_OF_STREAM:
// An input stream has ended.
break;
case MFT_MESSAGE_NOTIFY_RELEASE_RESOURCES:
// When we are told to release resources we need to flush all output
// samples.
FlushAllStoredData();
break;
case MFT_MESSAGE_NOTIFY_REACQUIRE_RESOURCES:
break;
case MFT_MESSAGE_SET_D3D_MANAGER:
// This message applies to video transforms only if the MF_SA_D3D_AWARE
// attribute is set to TRUE.
break;
case MFT_MESSAGE_NOTIFY_EVENT:
// Need to handle initial or dynamic policy notifications when the media
// event type is `MEPolicySet` when we set `MFT_POLICY_SET_AWARE`
// attribute to TRUE.
break;
case MFT_MESSAGE_DROP_SAMPLES: // fallthrough
case MFT_MESSAGE_COMMAND_TICK: // fallthrough
case MFT_MESSAGE_COMMAND_SET_OUTPUT_STREAM_STATE: // fallthrough
case MFT_MESSAGE_COMMAND_FLUSH_OUTPUT_STREAM: // fallthrough
// An MFT is allowed to ignore message types it doesn't care about.
DVLOG_FUNC(3) << "fallthrough!";
break;
}
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::ProcessInput(
_In_ DWORD input_stream_index,
_In_ IMFSample* sample,
_In_ DWORD flags) {
DVLOG_FUNC(1) << "input_stream_index=" << input_stream_index
<< ", flags=" << flags;
RETURN_IF_FAILED(GetShutdownStatus());
base::AutoLock lock(lock_);
if (input_stream_index != 0) {
return MF_E_INVALIDSTREAMNUMBER;
}
if (!sample) {
return E_INVALIDARG;
}
if (sample_) {
DVLOG_FUNC(3) << "The previous input sample is not processed yet.";
return MF_E_NOTACCEPTING;
}
// We only store the input sample here to process it in the `ProcessOutput()`.
sample_ = sample;
return S_OK;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::ProcessOutput(
_In_ DWORD flags,
_In_ DWORD output_samples_count,
_Inout_count_(output_samples_count) MFT_OUTPUT_DATA_BUFFER* output_samples,
_Out_ DWORD* status) {
DVLOG_FUNC(1) << "flags=" << flags
<< ", output_samples_count=" << output_samples_count;
RETURN_IF_FAILED(GetShutdownStatus());
base::AutoLock lock(lock_);
if (!output_samples || !status || output_samples_count != 1) {
return E_INVALIDARG;
}
if (!input_media_type_ || !output_media_type_) {
return MF_E_TRANSFORM_TYPE_NOT_SET;
}
// It should be 0 unless there is a stream change that we don't support.
*status = 0;
// Validate that the caller didn't provide a sample since we indicate the
// callee is the allocator by setting MFT_OUTPUT_STREAM_PROVIDES_SAMPLES flag
// in the GetOutputStreamInfo().
if (output_samples[0].pSample != nullptr) {
return E_INVALIDARG;
}
// Ensure synchronous MFTs never need any event.
output_samples[0].pEvents = nullptr;
DVLOG_FUNC(3) << "output_samples[0].dwStreamID="
<< output_samples[0].dwStreamID
<< ", dwStatus=" << output_samples[0].dwStatus;
if (!sample_) {
DVLOG_FUNC(3) << "MF_E_TRANSFORM_NEED_MORE_INPUT";
// The MFT cannot produce output data until it receives more input data.
// Note that ProcessOutput() may be called to see if there is still data
// to process before ProcessInput().
return MF_E_TRANSFORM_NEED_MORE_INPUT;
}
// If we were not able to get the content key id, this must be a clear
// sample.
GUID key_id_guid;
if (FAILED(sample_->GetGUID(MFSampleExtension_Content_KeyID, &key_id_guid))) {
DVLOG_FUNC(3) << "Clear sample detected!";
output_samples[0].pSample = sample_.Detach();
} else {
// Convert the Media Foundation sample to a DecoderBuffer.
scoped_refptr<DecoderBuffer> encrypted_buffer;
RETURN_IF_FAILED(GenerateDecoderBufferFromSample(
sample_.Detach(), &key_id_guid, &encrypted_buffer));
DVLOG_FUNC(3) << "encrypted_buffer=" +
encrypted_buffer->AsHumanReadableString(true);
// Decrypt the protected content.
Decryptor::Status decryptor_status = Decryptor::kError;
// TODO(crbug.com/1442373): We may remove the tracking code of stream type
// if two decryptors get created for audio and video respectively.
CHECK(stream_type_ != StreamType::kUnknown);
Decryptor::StreamType stream_type = stream_type_ == StreamType::kVideo
? Decryptor::kVideo
: Decryptor::kAudio;
scoped_refptr<DecoderBuffer> decrypted_buffer;
bool is_decrypt_completed = false;
aes_decryptor_->Decrypt(
stream_type, encrypted_buffer,
base::BindOnce(
[](Decryptor::Status* status_copy,
scoped_refptr<DecoderBuffer>* buffer_copy,
bool* is_decrypt_completed, Decryptor::Status status,
scoped_refptr<DecoderBuffer> buffer) {
*status_copy = status;
*buffer_copy = std::move(buffer);
*is_decrypt_completed = true;
},
&decryptor_status, &decrypted_buffer, &is_decrypt_completed));
// Ensure the decryption is done synchronously.
CHECK(is_decrypt_completed);
// Convert the DecoderBuffer back to a Media Foundation sample.
ComPtr<IMFSample> sample_decrypted = nullptr;
GUID last_key_id = GUID_NULL;
RETURN_IF_FAILED(GenerateSampleFromDecoderBuffer(
decrypted_buffer.get(), &sample_decrypted, &last_key_id,
base::NullCallback()));
DVLOG_FUNC(3) << "decrypted_buffer=" +
decrypted_buffer->AsHumanReadableString(true);
output_samples[0].pSample = sample_decrypted.Detach();
}
// TODO(crbug.com/1421444): Playback never ends with a sync Media Foundation
// MFT decryptor. Nothing will be rendered but it is sufficient for initial
// testing. Remove this block after fixing the bug.
{
output_samples[0].pSample->Release();
output_samples[0].pSample = nullptr;
}
return S_OK;
}
// IMFShutdown
STDMETHODIMP MediaFoundationClearKeyDecryptor::GetShutdownStatus(
_Out_ MFSHUTDOWN_STATUS* status) {
DVLOG_FUNC(1);
base::AutoLock lock(lock_);
if (is_shutdown_) {
*status = MFSHUTDOWN_COMPLETED;
return S_OK;
}
return MF_E_INVALIDREQUEST;
}
STDMETHODIMP MediaFoundationClearKeyDecryptor::Shutdown() {
DVLOG_FUNC(1);
base::AutoLock lock(lock_);
if (is_shutdown_) {
return MF_E_SHUTDOWN;
}
is_shutdown_ = true;
return S_OK;
}
HRESULT MediaFoundationClearKeyDecryptor::GenerateDecoderBufferFromSample(
IMFSample* mf_sample,
GUID* key_id,
scoped_refptr<DecoderBuffer>* buffer_out) {
DVLOG_FUNC(3);
CHECK(mf_sample);
CHECK(key_id);
ComPtr<IMFMediaBuffer> mf_buffer;
ComPtr<IMFMediaBuffer> new_mf_buffer;
DWORD buffer_count = 0;
RETURN_IF_FAILED(mf_sample->GetBufferCount(&buffer_count));
if (buffer_count != 1) {
DLOG(ERROR) << __func__ << ": buffer_count=" << buffer_count;
return MF_E_UNEXPECTED;
}
RETURN_IF_FAILED(mf_sample->GetBufferByIndex(0, &mf_buffer));
BYTE* mf_buffer_data = nullptr;
DWORD current_length = 0;
RETURN_IF_FAILED(mf_buffer->Lock(&mf_buffer_data, nullptr, &current_length));
auto decoder_buffer = DecoderBuffer::CopyFrom(mf_buffer_data, current_length);
RETURN_IF_FAILED(mf_buffer->Unlock());
UINT32 clean_point = 0;
HRESULT hr = mf_sample->GetUINT32(MFSampleExtension_CleanPoint, &clean_point);
if (hr == MF_E_ATTRIBUTENOTFOUND) {
hr = S_OK;
}
RETURN_IF_FAILED(hr);
decoder_buffer->set_is_key_frame(clean_point);
// Only set the sample duration if the source sample has one.
MFTIME sample_duration = 0;
hr = mf_sample->GetSampleDuration(&sample_duration);
if (hr == MF_E_NO_SAMPLE_DURATION) {
hr = S_OK;
} else {
RETURN_IF_FAILED(hr);
auto timedelta_sample_duration = MfTimeToTimeDelta(sample_duration);
DVLOG_FUNC(3) << "timedelta_sample_duration=" << timedelta_sample_duration;
decoder_buffer->set_duration(timedelta_sample_duration);
}
// Only set the sample time if the source sample has one.
MFTIME sample_time = 0;
hr = mf_sample->GetSampleTime(&sample_time);
if (hr == MF_E_NO_SAMPLE_DURATION) {
hr = S_OK;
} else {
RETURN_IF_FAILED(hr);
auto timedelta_sample_time = MfTimeToTimeDelta(sample_time);
DVLOG_FUNC(3) << "timedelta_sample_time=" << timedelta_sample_time;
decoder_buffer->set_timestamp(timedelta_sample_time);
}
std::unique_ptr<DecryptConfig> decrypt_config;
RETURN_IF_FAILED(
CreateDecryptConfigFromSample(mf_sample, *key_id, &decrypt_config));
decoder_buffer->set_decrypt_config(std::move(decrypt_config));
*buffer_out = std::move(decoder_buffer);
return S_OK;
}
void MediaFoundationClearKeyDecryptor::FlushAllStoredData() {
base::AutoLock lock(lock_);
DVLOG_FUNC(1) << ", sample_=" << sample_;
if (sample_) {
sample_.Reset();
}
}
} // namespace media