blob: 1f34d67cf9e535140d03591dd38ed3427d99ecb8 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Ensures MFAudioFormat_Xxx symbols are defined in mfapi.h which is included
// by media_foundation_audio_stream.h.
#include <initguid.h> // NOLINT(build/include_order)
#include "media/renderers/win/media_foundation_audio_stream.h"
#include <mferror.h> // NOLINT(build/include_order)
#include <mmreg.h> // NOLINT(build/include_order)
#include <wrl.h> // NOLINT(build/include_order)
#include "base/win/scoped_co_mem.h"
#include "media/base/audio_codecs.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/win/mf_helpers.h"
namespace media {
using Microsoft::WRL::ComPtr;
using Microsoft::WRL::MakeAndInitialize;
/*static*/
HRESULT MediaFoundationAudioStream::Create(
int stream_id,
IMFMediaSource* parent_source,
DemuxerStream* demuxer_stream,
std::unique_ptr<MediaLog> media_log,
MediaFoundationStreamWrapper** stream_out) {
DVLOG(1) << __func__ << ": stream_id=" << stream_id;
ComPtr<IMFMediaStream> audio_stream;
AudioCodec codec = demuxer_stream->audio_decoder_config().codec();
switch (codec) {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case AudioCodec::kAAC:
RETURN_IF_FAILED(MakeAndInitialize<MediaFoundationAACAudioStream>(
&audio_stream, stream_id, parent_source, demuxer_stream,
std::move(media_log)));
break;
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
default:
RETURN_IF_FAILED(MakeAndInitialize<MediaFoundationAudioStream>(
&audio_stream, stream_id, parent_source, demuxer_stream,
std::move(media_log)));
break;
}
*stream_out =
static_cast<MediaFoundationStreamWrapper*>(audio_stream.Detach());
return S_OK;
}
bool MediaFoundationAudioStream::IsEncrypted() const {
AudioDecoderConfig audio_config = demuxer_stream_->audio_decoder_config();
return audio_config.is_encrypted();
}
HRESULT MediaFoundationAudioStream::GetMediaType(
IMFMediaType** media_type_out) {
AudioDecoderConfig decoder_config = demuxer_stream_->audio_decoder_config();
return GetDefaultAudioType(decoder_config, media_type_out);
}
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
HRESULT MediaFoundationAACAudioStream::GetMediaType(
IMFMediaType** media_type_out) {
AudioDecoderConfig decoder_config = demuxer_stream_->audio_decoder_config();
enable_adts_header_removal_ = !decoder_config.should_discard_decoder_delay();
return GetAacAudioType(decoder_config, media_type_out);
}
// This detects whether the given |sample| has an ADTS header in the clear. If
// yes, it removes the ADTS header (and modifies the subsample mappings
// accordingly).
HRESULT MediaFoundationAACAudioStream::TransformSample(
Microsoft::WRL::ComPtr<IMFSample>& sample) {
DVLOG_FUNC(3);
if (!enable_adts_header_removal_)
return S_OK;
ComPtr<IMFMediaBuffer> mf_buffer;
ComPtr<IMFMediaBuffer> new_mf_buffer;
DWORD buffer_count = 0;
RETURN_IF_FAILED(sample->GetBufferCount(&buffer_count));
if (buffer_count != 1) {
DLOG(ERROR) << __func__ << ": buffer_count=" << buffer_count;
return MF_E_UNEXPECTED;
}
RETURN_IF_FAILED(sample->GetBufferByIndex(0, &mf_buffer));
BYTE* mf_buffer_data = nullptr;
DWORD max_length = 0;
DWORD current_length = 0;
const int kADTSHeaderSize = 7;
bool might_contain_adts_header = false;
RETURN_IF_FAILED(
mf_buffer->Lock(&mf_buffer_data, &max_length, &current_length));
if (current_length >= kADTSHeaderSize && mf_buffer_data[0] == 0xff &&
mf_buffer_data[1] == 0xf1 && mf_buffer_data[6] == 0xfc) {
might_contain_adts_header = true;
}
RETURN_IF_FAILED(mf_buffer->Unlock());
if (!might_contain_adts_header)
return S_OK;
bool contains_clear_adts_header = true;
// If the stream is encrypted, ensure that the sub-sample mappings are
// adjusted to account for the ADTS header removal.
base::win::ScopedCoMem<MediaFoundationSubsampleEntry> subsample_mappings;
uint32_t subsample_mappings_size = 0;
// MFSampleExtension_Encryption_SubSample_Mapping is documented in "mfapi.h".
// If the attribute doesn't exist, we should not fail the call.
if (SUCCEEDED(sample->GetAllocatedBlob(
MFSampleExtension_Encryption_SubSample_Mapping,
reinterpret_cast<uint8_t**>(&subsample_mappings),
&subsample_mappings_size)) &&
subsample_mappings_size >= sizeof(MediaFoundationSubsampleEntry)) {
uint32_t subsample_count =
subsample_mappings_size / sizeof(MediaFoundationSubsampleEntry);
if (subsample_count == 1 &&
subsample_mappings.get()[0].clear_bytes == kADTSHeaderSize) {
// When MFSampleExtension_Encryption_SubSample_Mapping attribute is
// removed, the whole sample is considered as encrypted.
RETURN_IF_FAILED(
sample->DeleteItem(MFSampleExtension_Encryption_SubSample_Mapping));
} else if (subsample_mappings.get()[0].clear_bytes >= kADTSHeaderSize) {
subsample_mappings.get()[0].clear_bytes -= kADTSHeaderSize;
RETURN_IF_FAILED(sample->SetBlob(
MFSampleExtension_Encryption_SubSample_Mapping,
reinterpret_cast<const uint8_t*>(subsample_mappings.get()),
subsample_mappings_size));
} else {
// There is no ADTS header inserted by Demuxer, we don't need to remove
// it.
contains_clear_adts_header = false;
}
}
if (contains_clear_adts_header) {
// Remove ADTS header from |sample|.
RETURN_IF_FAILED(MFCreateMediaBufferWrapper(
mf_buffer.Get(), kADTSHeaderSize, current_length - kADTSHeaderSize,
&new_mf_buffer));
RETURN_IF_FAILED(sample->RemoveAllBuffers());
RETURN_IF_FAILED(sample->AddBuffer(new_mf_buffer.Get()));
}
return S_OK;
}
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
} // namespace media