| // 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" |
| #include "media/filters/win/media_foundation_utils.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, ¤t_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 |