mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	audio_core: Replace AAC decoders with single FAAD2-based decoder. (#7098)
This commit is contained in:
		
							parent
							
								
									1570aeffcb
								
							
						
					
					
						commit
						27bad3a699
					
				
					 28 changed files with 304 additions and 2403 deletions
				
			
		|  | @ -4,15 +4,11 @@ add_library(audio_core STATIC | |||
|     codec.h | ||||
|     dsp_interface.cpp | ||||
|     dsp_interface.h | ||||
|     hle/adts.h | ||||
|     hle/adts_reader.cpp | ||||
|     hle/common.h | ||||
|     hle/decoder.cpp | ||||
|     hle/decoder.h | ||||
|     hle/fdk_decoder.cpp | ||||
|     hle/fdk_decoder.h | ||||
|     hle/ffmpeg_decoder.cpp | ||||
|     hle/ffmpeg_decoder.h | ||||
|     hle/faad2_decoder.cpp | ||||
|     hle/faad2_decoder.h | ||||
|     hle/filter.cpp | ||||
|     hle/filter.h | ||||
|     hle/hle.cpp | ||||
|  | @ -48,36 +44,7 @@ add_library(audio_core STATIC | |||
| create_target_directory_groups(audio_core) | ||||
| 
 | ||||
| target_link_libraries(audio_core PUBLIC citra_common citra_core) | ||||
| target_link_libraries(audio_core PRIVATE SoundTouch teakra) | ||||
| 
 | ||||
| if(ENABLE_MF) | ||||
|     target_sources(audio_core PRIVATE | ||||
|         hle/wmf_decoder.cpp | ||||
|         hle/wmf_decoder.h | ||||
|         hle/wmf_decoder_utils.cpp | ||||
|         hle/wmf_decoder_utils.h | ||||
|     ) | ||||
|     # We dynamically load the required symbols from mf.dll and mfplat.dll but mfuuid is not a dll | ||||
|     # just a static library of GUIDS so include that one directly. | ||||
|     target_link_libraries(audio_core PRIVATE mfuuid.lib) | ||||
|     target_compile_definitions(audio_core PUBLIC HAVE_MF) | ||||
| elseif(ENABLE_AUDIOTOOLBOX) | ||||
|     target_sources(audio_core PRIVATE | ||||
|         hle/audiotoolbox_decoder.cpp | ||||
|         hle/audiotoolbox_decoder.h | ||||
|     ) | ||||
|     find_library(AUDIOTOOLBOX AudioToolbox) | ||||
|     target_link_libraries(audio_core PRIVATE ${AUDIOTOOLBOX}) | ||||
|     target_compile_definitions(audio_core PUBLIC HAVE_AUDIOTOOLBOX) | ||||
| endif() | ||||
| 
 | ||||
| if(ANDROID) | ||||
|     target_sources(audio_core PRIVATE | ||||
|         hle/mediandk_decoder.cpp | ||||
|         hle/mediandk_decoder.h | ||||
|     ) | ||||
|     target_link_libraries(audio_core PRIVATE mediandk) | ||||
| endif() | ||||
| target_link_libraries(audio_core PRIVATE faad2 SoundTouch teakra) | ||||
| 
 | ||||
| if(ENABLE_SDL2) | ||||
|     target_link_libraries(audio_core PRIVATE SDL2::SDL2) | ||||
|  |  | |||
|  | @ -1,28 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| 
 | ||||
| struct ADTSData { | ||||
|     u8 header_length = 0; | ||||
|     bool mpeg2 = false; | ||||
|     u8 profile = 0; | ||||
|     u8 channels = 0; | ||||
|     u8 channel_idx = 0; | ||||
|     u8 framecount = 0; | ||||
|     u8 samplerate_idx = 0; | ||||
|     u32 length = 0; | ||||
|     u32 samplerate = 0; | ||||
| }; | ||||
| 
 | ||||
| ADTSData ParseADTS(const u8* buffer); | ||||
| 
 | ||||
| // last two bytes of MF AAC decoder user data
 | ||||
| // see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types
 | ||||
| u16 MFGetAACTag(const ADTSData& input); | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  | @ -1,79 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| #include <array> | ||||
| #include "adts.h" | ||||
| #include "common/bit_field.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, | ||||
|                                             16000, 12000, 11025, 8000,  7350,  0,     0,     0}; | ||||
| constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; | ||||
| 
 | ||||
| struct ADTSHeader { | ||||
|     union { | ||||
|         std::array<u8, 7> raw{}; | ||||
|         BitFieldBE<52, 12, u64> sync_word; | ||||
|         BitFieldBE<51, 1, u64> mpeg2; | ||||
|         BitFieldBE<49, 2, u64> layer; | ||||
|         BitFieldBE<48, 1, u64> protection_absent; | ||||
|         BitFieldBE<46, 2, u64> profile; | ||||
|         BitFieldBE<42, 4, u64> samplerate_idx; | ||||
|         BitFieldBE<41, 1, u64> private_bit; | ||||
|         BitFieldBE<38, 3, u64> channel_idx; | ||||
|         BitFieldBE<37, 1, u64> originality; | ||||
|         BitFieldBE<36, 1, u64> home; | ||||
|         BitFieldBE<35, 1, u64> copyright_id; | ||||
|         BitFieldBE<34, 1, u64> copyright_id_start; | ||||
|         BitFieldBE<21, 13, u64> frame_length; | ||||
|         BitFieldBE<10, 11, u64> buffer_fullness; | ||||
|         BitFieldBE<8, 2, u64> frame_count; | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| ADTSData ParseADTS(const u8* buffer) { | ||||
|     ADTSHeader header; | ||||
|     memcpy(header.raw.data(), buffer, sizeof(header.raw)); | ||||
| 
 | ||||
|     // sync word 0xfff
 | ||||
|     if (header.sync_word != 0xfff) { | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     ADTSData out{}; | ||||
|     // bit 16 = no CRC
 | ||||
|     out.header_length = header.protection_absent ? 7 : 9; | ||||
|     out.mpeg2 = static_cast<bool>(header.mpeg2); | ||||
|     // bit 17 to 18
 | ||||
|     out.profile = static_cast<u8>(header.profile) + 1; | ||||
|     // bit 19 to 22
 | ||||
|     out.samplerate_idx = static_cast<u8>(header.samplerate_idx); | ||||
|     out.samplerate = header.samplerate_idx > 15 ? 0 : freq_table[header.samplerate_idx]; | ||||
|     // bit 24 to 26
 | ||||
|     out.channel_idx = static_cast<u8>(header.channel_idx); | ||||
|     out.channels = (header.channel_idx > 7) ? 0 : channel_table[header.channel_idx]; | ||||
|     // bit 55 to 56
 | ||||
|     out.framecount = static_cast<u8>(header.frame_count + 1); | ||||
|     // bit 31 to 43
 | ||||
|     out.length = static_cast<u32>(header.frame_length); | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| // last two bytes of MF AAC decoder user data
 | ||||
| // Audio object type (5 bits)
 | ||||
| // Sample rate profile (4 bits)
 | ||||
| // Channel configuration profile (4 bits)
 | ||||
| // Frame length flag (1 bit)
 | ||||
| // Depends on core coder (1 bit)
 | ||||
| // Extension flag (1 bit)
 | ||||
| u16 MFGetAACTag(const ADTSData& input) { | ||||
|     u16 tag = 0; | ||||
| 
 | ||||
|     tag |= input.profile << 11; | ||||
|     tag |= input.samplerate_idx << 7; | ||||
|     tag |= input.channel_idx << 3; | ||||
| 
 | ||||
|     return tag; | ||||
| } | ||||
| } // namespace AudioCore
 | ||||
|  | @ -1,264 +0,0 @@ | |||
| // Copyright 2023 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <AudioToolbox/AudioToolbox.h> | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/hle/adts.h" | ||||
| #include "audio_core/hle/audiotoolbox_decoder.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| static constexpr auto bytes_per_sample = sizeof(s16); | ||||
| static constexpr auto aac_frames_per_packet = 1024; | ||||
| static constexpr auto error_out_of_data = -1932; | ||||
| 
 | ||||
| class AudioToolboxDecoder::Impl { | ||||
| public: | ||||
|     explicit Impl(Memory::MemorySystem& memory); | ||||
|     ~Impl(); | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); | ||||
| 
 | ||||
| private: | ||||
|     std::optional<BinaryMessage> Initalize(const BinaryMessage& request); | ||||
|     std::optional<BinaryMessage> Decode(const BinaryMessage& request); | ||||
| 
 | ||||
|     void Clear(); | ||||
|     bool InitializeDecoder(AudioCore::ADTSData& adts_header); | ||||
| 
 | ||||
|     static OSStatus DataFunc(AudioConverterRef in_audio_converter, u32* io_number_data_packets, | ||||
|                              AudioBufferList* io_data, | ||||
|                              AudioStreamPacketDescription** out_data_packet_description, | ||||
|                              void* in_user_data); | ||||
| 
 | ||||
|     Memory::MemorySystem& memory; | ||||
| 
 | ||||
|     AudioCore::ADTSData adts_config; | ||||
|     AudioStreamBasicDescription output_format = {}; | ||||
|     AudioConverterRef converter = nullptr; | ||||
| 
 | ||||
|     u8* curr_data = nullptr; | ||||
|     u32 curr_data_len = 0; | ||||
| 
 | ||||
|     AudioStreamPacketDescription packet_description; | ||||
| }; | ||||
| 
 | ||||
| AudioToolboxDecoder::Impl::Impl(Memory::MemorySystem& memory_) : memory(memory_) {} | ||||
| 
 | ||||
| std::optional<BinaryMessage> AudioToolboxDecoder::Impl::Initalize(const BinaryMessage& request) { | ||||
|     BinaryMessage response = request; | ||||
|     response.header.result = ResultStatus::Success; | ||||
| 
 | ||||
|     Clear(); | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| AudioToolboxDecoder::Impl::~Impl() { | ||||
|     Clear(); | ||||
| } | ||||
| 
 | ||||
| void AudioToolboxDecoder::Impl::Clear() { | ||||
|     curr_data = nullptr; | ||||
|     curr_data_len = 0; | ||||
| 
 | ||||
|     adts_config = {}; | ||||
|     output_format = {}; | ||||
| 
 | ||||
|     if (converter) { | ||||
|         AudioConverterDispose(converter); | ||||
|         converter = nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> AudioToolboxDecoder::Impl::ProcessRequest( | ||||
|     const BinaryMessage& request) { | ||||
|     if (request.header.codec != DecoderCodec::DecodeAAC) { | ||||
|         LOG_ERROR(Audio_DSP, "AudioToolbox AAC Decoder cannot handle such codec: {}", | ||||
|                   static_cast<u16>(request.header.codec)); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     switch (request.header.cmd) { | ||||
|     case DecoderCommand::Init: { | ||||
|         return Initalize(request); | ||||
|     } | ||||
|     case DecoderCommand::EncodeDecode: { | ||||
|         return Decode(request); | ||||
|     } | ||||
|     case DecoderCommand::Shutdown: | ||||
|     case DecoderCommand::SaveState: | ||||
|     case DecoderCommand::LoadState: { | ||||
|         LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", | ||||
|                     static_cast<u16>(request.header.cmd)); | ||||
|         BinaryMessage response = request; | ||||
|         response.header.result = ResultStatus::Success; | ||||
|         return response; | ||||
|     } | ||||
|     default: | ||||
|         LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", | ||||
|                   static_cast<u16>(request.header.cmd)); | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool AudioToolboxDecoder::Impl::InitializeDecoder(AudioCore::ADTSData& adts_header) { | ||||
|     if (converter) { | ||||
|         if (adts_config.channels == adts_header.channels && | ||||
|             adts_config.samplerate == adts_header.samplerate) { | ||||
|             return true; | ||||
|         } else { | ||||
|             Clear(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     AudioStreamBasicDescription input_format = { | ||||
|         .mSampleRate = static_cast<Float64>(adts_header.samplerate), | ||||
|         .mFormatID = kAudioFormatMPEG4AAC, | ||||
|         .mFramesPerPacket = aac_frames_per_packet, | ||||
|         .mChannelsPerFrame = adts_header.channels, | ||||
|     }; | ||||
| 
 | ||||
|     u32 bytes_per_frame = input_format.mChannelsPerFrame * bytes_per_sample; | ||||
|     output_format = { | ||||
|         .mSampleRate = input_format.mSampleRate, | ||||
|         .mFormatID = kAudioFormatLinearPCM, | ||||
|         .mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked, | ||||
|         .mBytesPerPacket = bytes_per_frame, | ||||
|         .mFramesPerPacket = 1, | ||||
|         .mBytesPerFrame = bytes_per_frame, | ||||
|         .mChannelsPerFrame = input_format.mChannelsPerFrame, | ||||
|         .mBitsPerChannel = bytes_per_sample * 8, | ||||
|     }; | ||||
| 
 | ||||
|     auto status = AudioConverterNew(&input_format, &output_format, &converter); | ||||
|     if (status != noErr) { | ||||
|         LOG_ERROR(Audio_DSP, "Could not create AAC audio converter: {}", status); | ||||
|         Clear(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     adts_config = adts_header; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| OSStatus AudioToolboxDecoder::Impl::DataFunc( | ||||
|     AudioConverterRef in_audio_converter, u32* io_number_data_packets, AudioBufferList* io_data, | ||||
|     AudioStreamPacketDescription** out_data_packet_description, void* in_user_data) { | ||||
|     auto impl = reinterpret_cast<Impl*>(in_user_data); | ||||
|     if (!impl || !impl->curr_data || impl->curr_data_len == 0) { | ||||
|         *io_number_data_packets = 0; | ||||
|         return error_out_of_data; | ||||
|     } | ||||
| 
 | ||||
|     io_data->mNumberBuffers = 1; | ||||
|     io_data->mBuffers[0].mNumberChannels = 0; | ||||
|     io_data->mBuffers[0].mDataByteSize = impl->curr_data_len; | ||||
|     io_data->mBuffers[0].mData = impl->curr_data; | ||||
|     *io_number_data_packets = 1; | ||||
| 
 | ||||
|     if (out_data_packet_description != nullptr) { | ||||
|         impl->packet_description.mStartOffset = 0; | ||||
|         impl->packet_description.mVariableFramesInPacket = 0; | ||||
|         impl->packet_description.mDataByteSize = impl->curr_data_len; | ||||
|         *out_data_packet_description = &impl->packet_description; | ||||
|     } | ||||
| 
 | ||||
|     impl->curr_data = nullptr; | ||||
|     impl->curr_data_len = 0; | ||||
| 
 | ||||
|     return noErr; | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> AudioToolboxDecoder::Impl::Decode(const BinaryMessage& request) { | ||||
|     BinaryMessage response{}; | ||||
|     response.header.codec = request.header.codec; | ||||
|     response.header.cmd = request.header.cmd; | ||||
|     response.decode_aac_response.size = request.decode_aac_request.size; | ||||
| 
 | ||||
|     if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || | ||||
|         request.decode_aac_request.src_addr + request.decode_aac_request.size > | ||||
|             Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|         LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", | ||||
|                   request.decode_aac_request.src_addr); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     const auto data = | ||||
|         memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); | ||||
|     auto adts_header = AudioCore::ParseADTS(data); | ||||
|     curr_data = data + adts_header.header_length; | ||||
|     curr_data_len = request.decode_aac_request.size - adts_header.header_length; | ||||
| 
 | ||||
|     if (!InitializeDecoder(adts_header)) { | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     // Up to 2048 samples, up to 2 channels each
 | ||||
|     s16 decoder_output[4096]; | ||||
|     AudioBufferList out_buffer{1, | ||||
|                                {{ | ||||
|                                    output_format.mChannelsPerFrame, | ||||
|                                    sizeof(decoder_output), | ||||
|                                    decoder_output, | ||||
|                                }}}; | ||||
| 
 | ||||
|     u32 num_packets = sizeof(decoder_output) / output_format.mBytesPerPacket; | ||||
|     auto status = AudioConverterFillComplexBuffer(converter, DataFunc, this, &num_packets, | ||||
|                                                   &out_buffer, nullptr); | ||||
|     if (status != noErr && status != error_out_of_data) { | ||||
|         LOG_ERROR(Audio_DSP, "Could not decode AAC data: {}", status); | ||||
|         Clear(); | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     // De-interleave samples.
 | ||||
|     std::array<std::vector<s16>, 2> out_streams; | ||||
|     auto num_frames = num_packets * output_format.mFramesPerPacket; | ||||
|     for (u32 frame = 0; frame < num_frames; frame++) { | ||||
|         for (u32 ch = 0; ch < output_format.mChannelsPerFrame; ch++) { | ||||
|             out_streams[ch].push_back( | ||||
|                 decoder_output[(frame * output_format.mChannelsPerFrame) + ch]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     curr_data = nullptr; | ||||
|     curr_data_len = 0; | ||||
| 
 | ||||
|     response.decode_aac_response.sample_rate = | ||||
|         GetSampleRateEnum(static_cast<u32>(output_format.mSampleRate)); | ||||
|     response.decode_aac_response.num_channels = output_format.mChannelsPerFrame; | ||||
|     response.decode_aac_response.num_samples = num_frames; | ||||
| 
 | ||||
|     // transfer the decoded buffer from vector to the FCRAM
 | ||||
|     for (std::size_t ch = 0; ch < out_streams.size(); ch++) { | ||||
|         if (!out_streams[ch].empty()) { | ||||
|             auto byte_size = out_streams[ch].size() * bytes_per_sample; | ||||
|             auto dst = ch == 0 ? request.decode_aac_request.dst_addr_ch0 | ||||
|                                : request.decode_aac_request.dst_addr_ch1; | ||||
|             if (dst < Memory::FCRAM_PADDR || | ||||
|                 dst + byte_size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|                 LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch{} {:08x}", ch, dst); | ||||
|                 return {}; | ||||
|             } | ||||
|             std::memcpy(memory.GetFCRAMPointer(dst - Memory::FCRAM_PADDR), out_streams[ch].data(), | ||||
|                         byte_size); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| AudioToolboxDecoder::AudioToolboxDecoder(Memory::MemorySystem& memory) | ||||
|     : impl(std::make_unique<Impl>(memory)) {} | ||||
| 
 | ||||
| AudioToolboxDecoder::~AudioToolboxDecoder() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> AudioToolboxDecoder::ProcessRequest(const BinaryMessage& request) { | ||||
|     return impl->ProcessRequest(request); | ||||
| } | ||||
| 
 | ||||
| bool AudioToolboxDecoder::IsValid() const { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
							
								
								
									
										186
									
								
								src/audio_core/hle/faad2_decoder.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/audio_core/hle/faad2_decoder.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,186 @@ | |||
| // Copyright 2023 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <neaacdec.h> | ||||
| #include "audio_core/hle/faad2_decoder.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class FAAD2Decoder::Impl { | ||||
| public: | ||||
|     explicit Impl(Memory::MemorySystem& memory); | ||||
|     ~Impl(); | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); | ||||
|     bool IsValid() const { | ||||
|         return decoder != nullptr; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::optional<BinaryMessage> Initalize(const BinaryMessage& request); | ||||
| 
 | ||||
|     std::optional<BinaryMessage> Decode(const BinaryMessage& request); | ||||
| 
 | ||||
|     Memory::MemorySystem& memory; | ||||
| 
 | ||||
|     NeAACDecHandle decoder = nullptr; | ||||
| }; | ||||
| 
 | ||||
| FAAD2Decoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { | ||||
|     decoder = NeAACDecOpen(); | ||||
|     if (decoder == nullptr) { | ||||
|         LOG_CRITICAL(Audio_DSP, "Could not open FAAD2 decoder."); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto config = NeAACDecGetCurrentConfiguration(decoder); | ||||
|     config->defObjectType = LC; | ||||
|     config->outputFormat = FAAD_FMT_16BIT; | ||||
|     if (!NeAACDecSetConfiguration(decoder, config)) { | ||||
|         LOG_CRITICAL(Audio_DSP, "Could not configure FAAD2 decoder."); | ||||
|         NeAACDecClose(decoder); | ||||
|         decoder = nullptr; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     LOG_INFO(Audio_DSP, "Created FAAD2 AAC decoder."); | ||||
| } | ||||
| 
 | ||||
| FAAD2Decoder::Impl::~Impl() { | ||||
|     if (decoder) { | ||||
|         NeAACDecClose(decoder); | ||||
|         decoder = nullptr; | ||||
| 
 | ||||
|         LOG_INFO(Audio_DSP, "Destroyed FAAD2 AAC decoder."); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FAAD2Decoder::Impl::ProcessRequest(const BinaryMessage& request) { | ||||
|     if (request.header.codec != DecoderCodec::DecodeAAC) { | ||||
|         LOG_ERROR(Audio_DSP, "FAAD2 AAC Decoder cannot handle such codec: {}", | ||||
|                   static_cast<u16>(request.header.codec)); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     switch (request.header.cmd) { | ||||
|     case DecoderCommand::Init: { | ||||
|         return Initalize(request); | ||||
|     } | ||||
|     case DecoderCommand::EncodeDecode: { | ||||
|         return Decode(request); | ||||
|     } | ||||
|     case DecoderCommand::Shutdown: | ||||
|     case DecoderCommand::SaveState: | ||||
|     case DecoderCommand::LoadState: { | ||||
|         LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", | ||||
|                     static_cast<u16>(request.header.cmd)); | ||||
|         BinaryMessage response = request; | ||||
|         response.header.result = ResultStatus::Success; | ||||
|         return response; | ||||
|     } | ||||
|     default: | ||||
|         LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", | ||||
|                   static_cast<u16>(request.header.cmd)); | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FAAD2Decoder::Impl::Initalize(const BinaryMessage& request) { | ||||
|     BinaryMessage response = request; | ||||
|     response.header.result = ResultStatus::Success; | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FAAD2Decoder::Impl::Decode(const BinaryMessage& request) { | ||||
|     BinaryMessage response{}; | ||||
|     response.header.codec = request.header.codec; | ||||
|     response.header.cmd = request.header.cmd; | ||||
|     response.decode_aac_response.size = request.decode_aac_request.size; | ||||
|     // This is a hack to continue games when a failure occurs.
 | ||||
|     response.decode_aac_response.sample_rate = DecoderSampleRate::Rate48000; | ||||
|     response.decode_aac_response.num_channels = 2; | ||||
|     response.decode_aac_response.num_samples = 1024; | ||||
| 
 | ||||
|     if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || | ||||
|         request.decode_aac_request.src_addr + request.decode_aac_request.size > | ||||
|             Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|         LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", | ||||
|                   request.decode_aac_request.src_addr); | ||||
|         return response; | ||||
|     } | ||||
|     u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); | ||||
|     u32 data_len = request.decode_aac_request.size; | ||||
| 
 | ||||
|     unsigned long sample_rate; | ||||
|     u8 num_channels; | ||||
|     auto init_result = NeAACDecInit(decoder, data, data_len, &sample_rate, &num_channels); | ||||
|     if (init_result < 0) { | ||||
|         LOG_ERROR(Audio_DSP, "Could not initialize FAAD2 AAC decoder for request: {}", init_result); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     // Advance past the frame header if needed.
 | ||||
|     data += init_result; | ||||
|     data_len -= init_result; | ||||
| 
 | ||||
|     std::array<std::vector<s16>, 2> out_streams; | ||||
| 
 | ||||
|     while (data_len > 0) { | ||||
|         NeAACDecFrameInfo frame_info; | ||||
|         auto curr_sample_buffer = | ||||
|             static_cast<s16*>(NeAACDecDecode(decoder, &frame_info, data, data_len)); | ||||
|         if (curr_sample_buffer == nullptr || frame_info.error != 0) { | ||||
|             LOG_ERROR(Audio_DSP, "Failed to decode AAC buffer using FAAD2: {}", frame_info.error); | ||||
|             return response; | ||||
|         } | ||||
| 
 | ||||
|         // Split the decode result into channels.
 | ||||
|         u32 num_samples = frame_info.samples / frame_info.channels; | ||||
|         for (u32 sample = 0; sample < num_samples; sample++) { | ||||
|             for (u32 ch = 0; ch < frame_info.channels; ch++) { | ||||
|                 out_streams[ch].push_back(curr_sample_buffer[(sample * frame_info.channels) + ch]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         data += frame_info.bytesconsumed; | ||||
|         data_len -= frame_info.bytesconsumed; | ||||
|     } | ||||
| 
 | ||||
|     // Transfer the decoded buffer from vector to the FCRAM.
 | ||||
|     for (std::size_t ch = 0; ch < out_streams.size(); ch++) { | ||||
|         if (out_streams[ch].empty()) { | ||||
|             continue; | ||||
|         } | ||||
|         auto byte_size = out_streams[ch].size() * sizeof(s16); | ||||
|         auto dst = ch == 0 ? request.decode_aac_request.dst_addr_ch0 | ||||
|                            : request.decode_aac_request.dst_addr_ch1; | ||||
|         if (dst < Memory::FCRAM_PADDR || | ||||
|             dst + byte_size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|             LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch{} {:08x}", ch, dst); | ||||
|             return response; | ||||
|         } | ||||
|         std::memcpy(memory.GetFCRAMPointer(dst - Memory::FCRAM_PADDR), out_streams[ch].data(), | ||||
|                     byte_size); | ||||
|     } | ||||
| 
 | ||||
|     // Set the output frame info.
 | ||||
|     response.decode_aac_response.sample_rate = GetSampleRateEnum(sample_rate); | ||||
|     response.decode_aac_response.num_channels = num_channels; | ||||
|     response.decode_aac_response.num_samples = static_cast<u32_le>(out_streams[0].size()); | ||||
| 
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| FAAD2Decoder::FAAD2Decoder(Memory::MemorySystem& memory) : impl(std::make_unique<Impl>(memory)) {} | ||||
| 
 | ||||
| FAAD2Decoder::~FAAD2Decoder() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> FAAD2Decoder::ProcessRequest(const BinaryMessage& request) { | ||||
|     return impl->ProcessRequest(request); | ||||
| } | ||||
| 
 | ||||
| bool FAAD2Decoder::IsValid() const { | ||||
|     return impl->IsValid(); | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -8,10 +8,10 @@ | |||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class AudioToolboxDecoder final : public DecoderBase { | ||||
| class FAAD2Decoder final : public DecoderBase { | ||||
| public: | ||||
|     explicit AudioToolboxDecoder(Memory::MemorySystem& memory); | ||||
|     ~AudioToolboxDecoder() override; | ||||
|     explicit FAAD2Decoder(Memory::MemorySystem& memory); | ||||
|     ~FAAD2Decoder() override; | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) override; | ||||
|     bool IsValid() const override; | ||||
| 
 | ||||
|  | @ -1,236 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "audio_core/hle/fdk_decoder.h" | ||||
| #include "common/dynamic_library/fdk-aac.h" | ||||
| 
 | ||||
| using namespace DynamicLibrary; | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class FDKDecoder::Impl { | ||||
| public: | ||||
|     explicit Impl(Memory::MemorySystem& memory); | ||||
|     ~Impl(); | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); | ||||
|     bool IsValid() const { | ||||
|         return decoder != nullptr; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::optional<BinaryMessage> Initalize(const BinaryMessage& request); | ||||
| 
 | ||||
|     std::optional<BinaryMessage> Decode(const BinaryMessage& request); | ||||
| 
 | ||||
|     void Clear(); | ||||
| 
 | ||||
|     Memory::MemorySystem& memory; | ||||
| 
 | ||||
|     HANDLE_AACDECODER decoder = nullptr; | ||||
| }; | ||||
| 
 | ||||
| FDKDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { | ||||
|     if (!FdkAac::LoadFdkAac()) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // allocate an array of LIB_INFO structures
 | ||||
|     // if we don't pre-fill the whole segment with zeros, when we call `aacDecoder_GetLibInfo`
 | ||||
|     // it will segfault, upon investigation, there is some code in fdk_aac depends on your initial
 | ||||
|     // values in this array
 | ||||
|     LIB_INFO decoder_info[FDK_MODULE_LAST] = {}; | ||||
|     // get library information and fill the struct
 | ||||
|     if (FdkAac::aacDecoder_GetLibInfo(decoder_info) != 0) { | ||||
|         LOG_ERROR(Audio_DSP, "Failed to retrieve fdk_aac library information!"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     LOG_INFO(Audio_DSP, "Using fdk_aac version {} (build date: {})", decoder_info[0].versionStr, | ||||
|              decoder_info[0].build_date); | ||||
| 
 | ||||
|     // choose the input format when initializing: 1 layer of ADTS
 | ||||
|     decoder = FdkAac::aacDecoder_Open(TRANSPORT_TYPE::TT_MP4_ADTS, 1); | ||||
|     // set maximum output channel to two (stereo)
 | ||||
|     // if the input samples have more channels, fdk_aac will perform a downmix
 | ||||
|     AAC_DECODER_ERROR ret = FdkAac::aacDecoder_SetParam(decoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2); | ||||
|     if (ret != AAC_DEC_OK) { | ||||
|         // unable to set this parameter reflects the decoder implementation might be broken
 | ||||
|         // we'd better shuts down everything
 | ||||
|         FdkAac::aacDecoder_Close(decoder); | ||||
|         decoder = nullptr; | ||||
|         LOG_ERROR(Audio_DSP, "Unable to set downmix parameter: {}", ret); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FDKDecoder::Impl::Initalize(const BinaryMessage& request) { | ||||
|     BinaryMessage response = request; | ||||
|     response.header.result = ResultStatus::Success; | ||||
| 
 | ||||
|     if (decoder) { | ||||
|         LOG_INFO(Audio_DSP, "FDK Decoder initialized"); | ||||
|         Clear(); | ||||
|     } else { | ||||
|         LOG_ERROR(Audio_DSP, "Decoder not initialized"); | ||||
|     } | ||||
| 
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| FDKDecoder::Impl::~Impl() { | ||||
|     if (decoder) { | ||||
|         FdkAac::aacDecoder_Close(decoder); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void FDKDecoder::Impl::Clear() { | ||||
|     s16 decoder_output[8192]; | ||||
|     // flush and re-sync the decoder, discarding the internal buffer
 | ||||
|     // we actually don't care if this succeeds or not
 | ||||
|     // FLUSH - flush internal buffer
 | ||||
|     // INTR - treat the current internal buffer as discontinuous
 | ||||
|     // CONCEAL - try to interpolate and smooth out the samples
 | ||||
|     if (decoder) { | ||||
|         FdkAac::aacDecoder_DecodeFrame(decoder, decoder_output, 8192, | ||||
|                                        AACDEC_FLUSH & AACDEC_INTR & AACDEC_CONCEAL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FDKDecoder::Impl::ProcessRequest(const BinaryMessage& request) { | ||||
|     if (request.header.codec != DecoderCodec::DecodeAAC) { | ||||
|         LOG_ERROR(Audio_DSP, "FDK AAC Decoder cannot handle such codec: {}", | ||||
|                   static_cast<u16>(request.header.codec)); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     switch (request.header.cmd) { | ||||
|     case DecoderCommand::Init: { | ||||
|         return Initalize(request); | ||||
|     } | ||||
|     case DecoderCommand::EncodeDecode: { | ||||
|         return Decode(request); | ||||
|     } | ||||
|     case DecoderCommand::Shutdown: | ||||
|     case DecoderCommand::SaveState: | ||||
|     case DecoderCommand::LoadState: { | ||||
|         LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", | ||||
|                     static_cast<u16>(request.header.cmd)); | ||||
|         BinaryMessage response = request; | ||||
|         response.header.result = ResultStatus::Success; | ||||
|         return response; | ||||
|     } | ||||
|     default: | ||||
|         LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", | ||||
|                   static_cast<u16>(request.header.cmd)); | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FDKDecoder::Impl::Decode(const BinaryMessage& request) { | ||||
|     BinaryMessage response{}; | ||||
|     response.header.codec = request.header.codec; | ||||
|     response.header.cmd = request.header.cmd; | ||||
|     response.decode_aac_response.size = request.decode_aac_request.size; | ||||
| 
 | ||||
|     if (!decoder) { | ||||
|         LOG_DEBUG(Audio_DSP, "Decoder not initalized"); | ||||
|         // This is a hack to continue games that are not compiled with the aac codec
 | ||||
|         response.decode_aac_response.num_channels = 2; | ||||
|         response.decode_aac_response.num_samples = 1024; | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || | ||||
|         request.decode_aac_request.src_addr + request.decode_aac_request.size > | ||||
|             Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|         LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", | ||||
|                   request.decode_aac_request.src_addr); | ||||
|         return {}; | ||||
|     } | ||||
|     u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); | ||||
| 
 | ||||
|     std::array<std::vector<s16>, 2> out_streams; | ||||
| 
 | ||||
|     u32 data_size = request.decode_aac_request.size; | ||||
| 
 | ||||
|     // decoding loops
 | ||||
|     AAC_DECODER_ERROR result = AAC_DEC_OK; | ||||
|     // Up to 2048 samples, up to 2 channels each
 | ||||
|     s16 decoder_output[4096]; | ||||
|     // note that we don't free this pointer as it is automatically freed by fdk_aac
 | ||||
|     CStreamInfo* stream_info; | ||||
|     // how many bytes to be queued into the decoder, decrementing from the buffer size
 | ||||
|     u32 buffer_remaining = data_size; | ||||
|     // alias the data_size as an u32
 | ||||
|     u32 input_size = data_size; | ||||
| 
 | ||||
|     while (buffer_remaining) { | ||||
|         // queue the input buffer, fdk_aac will automatically slice out the buffer it needs
 | ||||
|         // from the input buffer
 | ||||
|         result = FdkAac::aacDecoder_Fill(decoder, &data, &input_size, &buffer_remaining); | ||||
|         if (result != AAC_DEC_OK) { | ||||
|             // there are some issues when queuing the input buffer
 | ||||
|             LOG_ERROR(Audio_DSP, "Failed to enqueue the input samples"); | ||||
|             return std::nullopt; | ||||
|         } | ||||
|         // get output from decoder
 | ||||
|         result = FdkAac::aacDecoder_DecodeFrame(decoder, decoder_output, | ||||
|                                                 sizeof(decoder_output) / sizeof(s16), 0); | ||||
|         if (result == AAC_DEC_OK) { | ||||
|             // get the stream information
 | ||||
|             stream_info = FdkAac::aacDecoder_GetStreamInfo(decoder); | ||||
|             // fill the stream information for binary response
 | ||||
|             response.decode_aac_response.sample_rate = GetSampleRateEnum(stream_info->sampleRate); | ||||
|             response.decode_aac_response.num_channels = stream_info->numChannels; | ||||
|             response.decode_aac_response.num_samples = stream_info->frameSize; | ||||
|             // fill the output
 | ||||
|             // the sample size = frame_size * channel_counts
 | ||||
|             for (int sample = 0; sample < stream_info->frameSize; sample++) { | ||||
|                 for (int ch = 0; ch < stream_info->numChannels; ch++) { | ||||
|                     out_streams[ch].push_back( | ||||
|                         decoder_output[(sample * stream_info->numChannels) + ch]); | ||||
|                 } | ||||
|             } | ||||
|         } else if (result == AAC_DEC_TRANSPORT_SYNC_ERROR) { | ||||
|             // decoder has some synchronization problems, try again with new samples,
 | ||||
|             // using old samples might trigger this error again
 | ||||
|             continue; | ||||
|         } else { | ||||
|             LOG_ERROR(Audio_DSP, "Error decoding the sample: {}", result); | ||||
|             return std::nullopt; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // transfer the decoded buffer from vector to the FCRAM
 | ||||
|     for (std::size_t ch = 0; ch < out_streams.size(); ch++) { | ||||
|         if (!out_streams[ch].empty()) { | ||||
|             auto byte_size = out_streams[ch].size() * sizeof(s16); | ||||
|             auto dst = ch == 0 ? request.decode_aac_request.dst_addr_ch0 | ||||
|                                : request.decode_aac_request.dst_addr_ch1; | ||||
|             if (dst < Memory::FCRAM_PADDR || | ||||
|                 dst + byte_size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|                 LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch{} {:08x}", ch, dst); | ||||
|                 return {}; | ||||
|             } | ||||
|             std::memcpy(memory.GetFCRAMPointer(dst - Memory::FCRAM_PADDR), out_streams[ch].data(), | ||||
|                         byte_size); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| FDKDecoder::FDKDecoder(Memory::MemorySystem& memory) : impl(std::make_unique<Impl>(memory)) {} | ||||
| 
 | ||||
| FDKDecoder::~FDKDecoder() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> FDKDecoder::ProcessRequest(const BinaryMessage& request) { | ||||
|     return impl->ProcessRequest(request); | ||||
| } | ||||
| 
 | ||||
| bool FDKDecoder::IsValid() const { | ||||
|     return impl->IsValid(); | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -1,23 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "audio_core/hle/decoder.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class FDKDecoder final : public DecoderBase { | ||||
| public: | ||||
|     explicit FDKDecoder(Memory::MemorySystem& memory); | ||||
|     ~FDKDecoder() override; | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) override; | ||||
|     bool IsValid() const override; | ||||
| 
 | ||||
| private: | ||||
|     class Impl; | ||||
|     std::unique_ptr<Impl> impl; | ||||
| }; | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -1,290 +0,0 @@ | |||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "audio_core/hle/ffmpeg_decoder.h" | ||||
| #include "common/dynamic_library/ffmpeg.h" | ||||
| 
 | ||||
| using namespace DynamicLibrary; | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class FFMPEGDecoder::Impl { | ||||
| public: | ||||
|     explicit Impl(Memory::MemorySystem& memory); | ||||
|     ~Impl(); | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); | ||||
|     bool IsValid() const { | ||||
|         return have_ffmpeg_dl; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::optional<BinaryMessage> Initalize(const BinaryMessage& request); | ||||
| 
 | ||||
|     void Clear(); | ||||
| 
 | ||||
|     std::optional<BinaryMessage> Decode(const BinaryMessage& request); | ||||
| 
 | ||||
|     struct AVPacketDeleter { | ||||
|         void operator()(AVPacket* packet) const { | ||||
|             FFmpeg::av_packet_free(&packet); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct AVCodecContextDeleter { | ||||
|         void operator()(AVCodecContext* context) const { | ||||
|             FFmpeg::avcodec_free_context(&context); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct AVCodecParserContextDeleter { | ||||
|         void operator()(AVCodecParserContext* parser) const { | ||||
|             FFmpeg::av_parser_close(parser); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct AVFrameDeleter { | ||||
|         void operator()(AVFrame* frame) const { | ||||
|             FFmpeg::av_frame_free(&frame); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     bool initalized = false; | ||||
|     bool have_ffmpeg_dl; | ||||
| 
 | ||||
|     Memory::MemorySystem& memory; | ||||
| 
 | ||||
|     const AVCodec* codec; | ||||
|     std::unique_ptr<AVCodecContext, AVCodecContextDeleter> av_context; | ||||
|     std::unique_ptr<AVCodecParserContext, AVCodecParserContextDeleter> parser; | ||||
|     std::unique_ptr<AVPacket, AVPacketDeleter> av_packet; | ||||
|     std::unique_ptr<AVFrame, AVFrameDeleter> decoded_frame; | ||||
| }; | ||||
| 
 | ||||
| FFMPEGDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { | ||||
|     have_ffmpeg_dl = FFmpeg::LoadFFmpeg(); | ||||
| } | ||||
| 
 | ||||
| FFMPEGDecoder::Impl::~Impl() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> FFMPEGDecoder::Impl::ProcessRequest(const BinaryMessage& request) { | ||||
|     if (request.header.codec != DecoderCodec::DecodeAAC) { | ||||
|         LOG_ERROR(Audio_DSP, "Got wrong codec {}", static_cast<u16>(request.header.codec)); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     switch (request.header.cmd) { | ||||
|     case DecoderCommand::Init: { | ||||
|         return Initalize(request); | ||||
|     } | ||||
|     case DecoderCommand::EncodeDecode: { | ||||
|         return Decode(request); | ||||
|     } | ||||
|     case DecoderCommand::Shutdown: | ||||
|     case DecoderCommand::SaveState: | ||||
|     case DecoderCommand::LoadState: { | ||||
|         LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", | ||||
|                     static_cast<u16>(request.header.cmd)); | ||||
|         BinaryMessage response = request; | ||||
|         response.header.result = ResultStatus::Success; | ||||
|         return response; | ||||
|     } | ||||
|     default: | ||||
|         LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", | ||||
|                   static_cast<u16>(request.header.cmd)); | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FFMPEGDecoder::Impl::Initalize(const BinaryMessage& request) { | ||||
|     if (initalized) { | ||||
|         Clear(); | ||||
|     } | ||||
| 
 | ||||
|     BinaryMessage response = request; | ||||
|     response.header.result = ResultStatus::Success; | ||||
| 
 | ||||
|     if (!have_ffmpeg_dl) { | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     av_packet.reset(FFmpeg::av_packet_alloc()); | ||||
| 
 | ||||
|     codec = FFmpeg::avcodec_find_decoder(AV_CODEC_ID_AAC); | ||||
|     if (!codec) { | ||||
|         LOG_ERROR(Audio_DSP, "Codec not found\n"); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     parser.reset(FFmpeg::av_parser_init(codec->id)); | ||||
|     if (!parser) { | ||||
|         LOG_ERROR(Audio_DSP, "Parser not found\n"); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     av_context.reset(FFmpeg::avcodec_alloc_context3(codec)); | ||||
|     if (!av_context) { | ||||
|         LOG_ERROR(Audio_DSP, "Could not allocate audio codec context\n"); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     if (FFmpeg::avcodec_open2(av_context.get(), codec, nullptr) < 0) { | ||||
|         LOG_ERROR(Audio_DSP, "Could not open codec\n"); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     initalized = true; | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| void FFMPEGDecoder::Impl::Clear() { | ||||
|     if (!have_ffmpeg_dl) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     av_context.reset(); | ||||
|     parser.reset(); | ||||
|     decoded_frame.reset(); | ||||
|     av_packet.reset(); | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> FFMPEGDecoder::Impl::Decode(const BinaryMessage& request) { | ||||
|     BinaryMessage response{}; | ||||
|     response.header.codec = request.header.codec; | ||||
|     response.header.cmd = request.header.cmd; | ||||
|     response.decode_aac_response.size = request.decode_aac_request.size; | ||||
| 
 | ||||
|     if (!initalized) { | ||||
|         LOG_DEBUG(Audio_DSP, "Decoder not initalized"); | ||||
|         // This is a hack to continue games that are not compiled with the aac codec
 | ||||
|         response.decode_aac_response.num_channels = 2; | ||||
|         response.decode_aac_response.num_samples = 1024; | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || | ||||
|         request.decode_aac_request.src_addr + request.decode_aac_request.size > | ||||
|             Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|         LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", | ||||
|                   request.decode_aac_request.src_addr); | ||||
|         return {}; | ||||
|     } | ||||
|     u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); | ||||
| 
 | ||||
|     std::array<std::vector<u8>, 2> out_streams; | ||||
| 
 | ||||
|     std::size_t data_size = request.decode_aac_request.size; | ||||
|     while (data_size > 0) { | ||||
|         if (!decoded_frame) { | ||||
|             decoded_frame.reset(FFmpeg::av_frame_alloc()); | ||||
|             if (!decoded_frame) { | ||||
|                 LOG_ERROR(Audio_DSP, "Could not allocate audio frame"); | ||||
|                 return {}; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         int ret = FFmpeg::av_parser_parse2(parser.get(), av_context.get(), &av_packet->data, | ||||
|                                            &av_packet->size, data, static_cast<int>(data_size), | ||||
|                                            AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); | ||||
|         if (ret < 0) { | ||||
|             LOG_ERROR(Audio_DSP, "Error while parsing"); | ||||
|             return {}; | ||||
|         } | ||||
|         data += ret; | ||||
|         data_size -= ret; | ||||
| 
 | ||||
|         ret = FFmpeg::avcodec_send_packet(av_context.get(), av_packet.get()); | ||||
|         if (ret < 0) { | ||||
|             LOG_ERROR(Audio_DSP, "Error submitting the packet to the decoder"); | ||||
|             return {}; | ||||
|         } | ||||
| 
 | ||||
|         if (av_packet->size) { | ||||
|             while (ret >= 0) { | ||||
|                 ret = FFmpeg::avcodec_receive_frame(av_context.get(), decoded_frame.get()); | ||||
|                 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||||
|                     break; | ||||
|                 else if (ret < 0) { | ||||
|                     LOG_ERROR(Audio_DSP, "Error during decoding"); | ||||
|                     return {}; | ||||
|                 } | ||||
|                 int bytes_per_sample = FFmpeg::av_get_bytes_per_sample(av_context->sample_fmt); | ||||
|                 if (bytes_per_sample < 0) { | ||||
|                     LOG_ERROR(Audio_DSP, "Failed to calculate data size"); | ||||
|                     return {}; | ||||
|                 } | ||||
| 
 | ||||
| #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) | ||||
|                 auto num_channels = static_cast<u32>(decoded_frame->ch_layout.nb_channels); | ||||
| #else | ||||
|                 auto num_channels = static_cast<u32>(decoded_frame->channels); | ||||
| #endif | ||||
| 
 | ||||
|                 ASSERT(num_channels <= out_streams.size()); | ||||
| 
 | ||||
|                 std::size_t size = bytes_per_sample * (decoded_frame->nb_samples); | ||||
| 
 | ||||
|                 response.decode_aac_response.sample_rate = | ||||
|                     GetSampleRateEnum(decoded_frame->sample_rate); | ||||
|                 response.decode_aac_response.num_channels = num_channels; | ||||
|                 response.decode_aac_response.num_samples += decoded_frame->nb_samples; | ||||
| 
 | ||||
|                 // FFmpeg converts to 32 signed floating point PCM, we need s16 PCM so we need to
 | ||||
|                 // convert it
 | ||||
|                 f32 val_float; | ||||
|                 for (std::size_t current_pos(0); current_pos < size;) { | ||||
|                     for (std::size_t channel(0); channel < num_channels; channel++) { | ||||
|                         std::memcpy(&val_float, decoded_frame->data[channel] + current_pos, | ||||
|                                     sizeof(val_float)); | ||||
|                         val_float = std::clamp(val_float, -1.0f, 1.0f); | ||||
|                         s16 val = static_cast<s16>(0x7FFF * val_float); | ||||
|                         out_streams[channel].push_back(val & 0xFF); | ||||
|                         out_streams[channel].push_back(val >> 8); | ||||
|                     } | ||||
|                     current_pos += sizeof(val_float); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (out_streams[0].size() != 0) { | ||||
|         if (request.decode_aac_request.dst_addr_ch0 < Memory::FCRAM_PADDR || | ||||
|             request.decode_aac_request.dst_addr_ch0 + out_streams[0].size() > | ||||
|                 Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|             LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", | ||||
|                       request.decode_aac_request.dst_addr_ch0); | ||||
|             return {}; | ||||
|         } | ||||
|         std::memcpy( | ||||
|             memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch0 - Memory::FCRAM_PADDR), | ||||
|             out_streams[0].data(), out_streams[0].size()); | ||||
|     } | ||||
| 
 | ||||
|     if (out_streams[1].size() != 0) { | ||||
|         if (request.decode_aac_request.dst_addr_ch1 < Memory::FCRAM_PADDR || | ||||
|             request.decode_aac_request.dst_addr_ch1 + out_streams[1].size() > | ||||
|                 Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|             LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", | ||||
|                       request.decode_aac_request.dst_addr_ch1); | ||||
|             return {}; | ||||
|         } | ||||
|         std::memcpy( | ||||
|             memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch1 - Memory::FCRAM_PADDR), | ||||
|             out_streams[1].data(), out_streams[1].size()); | ||||
|     } | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| FFMPEGDecoder::FFMPEGDecoder(Memory::MemorySystem& memory) : impl(std::make_unique<Impl>(memory)) {} | ||||
| 
 | ||||
| FFMPEGDecoder::~FFMPEGDecoder() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> FFMPEGDecoder::ProcessRequest(const BinaryMessage& request) { | ||||
|     return impl->ProcessRequest(request); | ||||
| } | ||||
| 
 | ||||
| bool FFMPEGDecoder::IsValid() const { | ||||
|     return impl->IsValid(); | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -1,23 +0,0 @@ | |||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "audio_core/hle/decoder.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class FFMPEGDecoder final : public DecoderBase { | ||||
| public: | ||||
|     explicit FFMPEGDecoder(Memory::MemorySystem& memory); | ||||
|     ~FFMPEGDecoder() override; | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) override; | ||||
|     bool IsValid() const override; | ||||
| 
 | ||||
| private: | ||||
|     class Impl; | ||||
|     std::unique_ptr<Impl> impl; | ||||
| }; | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -8,23 +8,15 @@ | |||
| #include <boost/serialization/vector.hpp> | ||||
| #include <boost/serialization/weak_ptr.hpp> | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "common/archives.h" | ||||
| #ifdef HAVE_MF | ||||
| #include "audio_core/hle/wmf_decoder.h" | ||||
| #elif HAVE_AUDIOTOOLBOX | ||||
| #include "audio_core/hle/audiotoolbox_decoder.h" | ||||
| #elif ANDROID | ||||
| #include "audio_core/hle/mediandk_decoder.h" | ||||
| #endif | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/decoder.h" | ||||
| #include "audio_core/hle/fdk_decoder.h" | ||||
| #include "audio_core/hle/ffmpeg_decoder.h" | ||||
| #include "audio_core/hle/faad2_decoder.h" | ||||
| #include "audio_core/hle/hle.h" | ||||
| #include "audio_core/hle/mixers.h" | ||||
| #include "audio_core/hle/shared_memory.h" | ||||
| #include "audio_core/hle/source.h" | ||||
| #include "audio_core/sink.h" | ||||
| #include "common/archives.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/hash.h" | ||||
|  | @ -121,26 +113,8 @@ private: | |||
| 
 | ||||
| static std::vector<std::function<std::unique_ptr<HLE::DecoderBase>(Memory::MemorySystem&)>> | ||||
|     decoder_backends = { | ||||
| #if defined(HAVE_MF) | ||||
|         [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> { | ||||
|             return std::make_unique<HLE::WMFDecoder>(memory); | ||||
|         }, | ||||
| #endif | ||||
| #if defined(HAVE_AUDIOTOOLBOX) | ||||
|         [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> { | ||||
|             return std::make_unique<HLE::AudioToolboxDecoder>(memory); | ||||
|         }, | ||||
| #endif | ||||
| #if ANDROID | ||||
|         [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> { | ||||
|             return std::make_unique<HLE::MediaNDKDecoder>(memory); | ||||
|         }, | ||||
| #endif | ||||
|         [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> { | ||||
|             return std::make_unique<HLE::FDKDecoder>(memory); | ||||
|         }, | ||||
|         [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> { | ||||
|             return std::make_unique<HLE::FFMPEGDecoder>(memory); | ||||
|             return std::make_unique<HLE::FAAD2Decoder>(memory); | ||||
|         }, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,253 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <media/NdkMediaCodec.h> | ||||
| #include <media/NdkMediaError.h> | ||||
| #include <media/NdkMediaFormat.h> | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "audio_core/hle/adts.h" | ||||
| #include "audio_core/hle/mediandk_decoder.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| struct AMediaCodecRelease { | ||||
|     void operator()(AMediaCodec* codec) const { | ||||
|         AMediaCodec_stop(codec); | ||||
|         AMediaCodec_delete(codec); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| class MediaNDKDecoder::Impl { | ||||
| public: | ||||
|     explicit Impl(Memory::MemorySystem& memory); | ||||
|     ~Impl(); | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); | ||||
| 
 | ||||
|     bool SetMediaType(const AudioCore::ADTSData& adts_data); | ||||
| 
 | ||||
| private: | ||||
|     std::optional<BinaryMessage> Initalize(const BinaryMessage& request); | ||||
|     std::optional<BinaryMessage> Decode(const BinaryMessage& request); | ||||
| 
 | ||||
|     Memory::MemorySystem& memory; | ||||
|     std::unique_ptr<AMediaCodec, AMediaCodecRelease> decoder; | ||||
|     // default: 2 channles, 48000 samplerate
 | ||||
|     AudioCore::ADTSData mADTSData{ | ||||
|         /*header_length*/ 7,  /*mpeg2*/ false,   /*profile*/ 2, | ||||
|         /*channels*/ 2,       /*channel_idx*/ 2, /*framecount*/ 0, | ||||
|         /*samplerate_idx*/ 3, /*length*/ 0,      /*samplerate*/ 48000}; | ||||
| }; | ||||
| 
 | ||||
| MediaNDKDecoder::Impl::Impl(Memory::MemorySystem& memory_) : memory(memory_) { | ||||
|     SetMediaType(mADTSData); | ||||
| } | ||||
| 
 | ||||
| MediaNDKDecoder::Impl::~Impl() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> MediaNDKDecoder::Impl::Initalize(const BinaryMessage& request) { | ||||
|     BinaryMessage response = request; | ||||
|     response.header.result = ResultStatus::Success; | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| bool MediaNDKDecoder::Impl::SetMediaType(const AudioCore::ADTSData& adts_data) { | ||||
|     const char* mime = "audio/mp4a-latm"; | ||||
|     if (decoder && mADTSData.profile == adts_data.profile && | ||||
|         mADTSData.channel_idx == adts_data.channel_idx && | ||||
|         mADTSData.samplerate_idx == adts_data.samplerate_idx) { | ||||
|         return true; | ||||
|     } | ||||
|     decoder.reset(AMediaCodec_createDecoderByType(mime)); | ||||
|     if (decoder == nullptr) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     u8 csd_0[2]; | ||||
|     csd_0[0] = static_cast<u8>((adts_data.profile << 3) | (adts_data.samplerate_idx >> 1)); | ||||
|     csd_0[1] = | ||||
|         static_cast<u8>(((adts_data.samplerate_idx << 7) & 0x80) | (adts_data.channel_idx << 3)); | ||||
|     AMediaFormat* format = AMediaFormat_new(); | ||||
|     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime); | ||||
|     AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, adts_data.samplerate); | ||||
|     AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, adts_data.channels); | ||||
|     AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_IS_ADTS, 1); | ||||
|     AMediaFormat_setBuffer(format, "csd-0", csd_0, sizeof(csd_0)); | ||||
| 
 | ||||
|     media_status_t status = AMediaCodec_configure(decoder.get(), format, NULL, NULL, 0); | ||||
|     if (status != AMEDIA_OK) { | ||||
|         AMediaFormat_delete(format); | ||||
|         decoder.reset(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     status = AMediaCodec_start(decoder.get()); | ||||
|     if (status != AMEDIA_OK) { | ||||
|         AMediaFormat_delete(format); | ||||
|         decoder.reset(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     AMediaFormat_delete(format); | ||||
|     mADTSData = adts_data; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> MediaNDKDecoder::Impl::ProcessRequest(const BinaryMessage& request) { | ||||
|     if (request.header.codec != DecoderCodec::DecodeAAC) { | ||||
|         LOG_ERROR(Audio_DSP, "AAC Decoder cannot handle such codec: {}", | ||||
|                   static_cast<u16>(request.header.codec)); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     switch (request.header.cmd) { | ||||
|     case DecoderCommand::Init: { | ||||
|         return Initalize(request); | ||||
|     } | ||||
|     case DecoderCommand::EncodeDecode: { | ||||
|         return Decode(request); | ||||
|     } | ||||
|     case DecoderCommand::Shutdown: | ||||
|     case DecoderCommand::SaveState: | ||||
|     case DecoderCommand::LoadState: { | ||||
|         LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", | ||||
|                     static_cast<u16>(request.header.cmd)); | ||||
|         BinaryMessage response = request; | ||||
|         response.header.result = ResultStatus::Success; | ||||
|         return response; | ||||
|     } | ||||
|     default: | ||||
|         LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", | ||||
|                   static_cast<u16>(request.header.cmd)); | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> MediaNDKDecoder::Impl::Decode(const BinaryMessage& request) { | ||||
|     BinaryMessage response{}; | ||||
|     response.header.codec = request.header.codec; | ||||
|     response.header.cmd = request.header.cmd; | ||||
|     response.decode_aac_response.size = request.decode_aac_request.size; | ||||
|     response.decode_aac_response.num_samples = 1024; | ||||
| 
 | ||||
|     if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || | ||||
|         request.decode_aac_request.src_addr + request.decode_aac_request.size > | ||||
|             Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|         LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", | ||||
|                   request.decode_aac_request.src_addr); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     const u8* data = | ||||
|         memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); | ||||
|     ADTSData adts_data = AudioCore::ParseADTS(data); | ||||
|     SetMediaType(adts_data); | ||||
|     response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate); | ||||
|     response.decode_aac_response.num_channels = adts_data.channels; | ||||
|     if (!decoder) { | ||||
|         LOG_ERROR(Audio_DSP, "Missing decoder for profile: {}, channels: {}, samplerate: {}", | ||||
|                   adts_data.profile, adts_data.channels, adts_data.samplerate); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     // input
 | ||||
|     constexpr int timeout = 160; | ||||
|     std::size_t buffer_size = 0; | ||||
|     u8* buffer = nullptr; | ||||
|     ssize_t buffer_index = AMediaCodec_dequeueInputBuffer(decoder.get(), timeout); | ||||
|     if (buffer_index < 0) { | ||||
|         LOG_ERROR(Audio_DSP, "Failed to enqueue the input samples: {}", buffer_index); | ||||
|         return response; | ||||
|     } | ||||
|     buffer = AMediaCodec_getInputBuffer(decoder.get(), buffer_index, &buffer_size); | ||||
|     if (buffer_size < request.decode_aac_request.size) { | ||||
|         return response; | ||||
|     } | ||||
|     std::memcpy(buffer, data, request.decode_aac_request.size); | ||||
|     media_status_t status = AMediaCodec_queueInputBuffer(decoder.get(), buffer_index, 0, | ||||
|                                                          request.decode_aac_request.size, 0, 0); | ||||
|     if (status != AMEDIA_OK) { | ||||
|         LOG_WARNING(Audio_DSP, "Try queue input buffer again later!"); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     // output
 | ||||
|     AMediaCodecBufferInfo info; | ||||
|     std::array<std::vector<u16>, 2> out_streams; | ||||
|     buffer_index = AMediaCodec_dequeueOutputBuffer(decoder.get(), &info, timeout); | ||||
|     switch (buffer_index) { | ||||
|     case AMEDIACODEC_INFO_TRY_AGAIN_LATER: | ||||
|         LOG_WARNING(Audio_DSP, "Failed to dequeue output buffer: timeout!"); | ||||
|         break; | ||||
|     case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED: | ||||
|         LOG_WARNING(Audio_DSP, "Failed to dequeue output buffer: buffers changed!"); | ||||
|         break; | ||||
|     case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED: { | ||||
|         AMediaFormat* format = AMediaCodec_getOutputFormat(decoder.get()); | ||||
|         LOG_WARNING(Audio_DSP, "output format: {}", AMediaFormat_toString(format)); | ||||
|         AMediaFormat_delete(format); | ||||
|         buffer_index = AMediaCodec_dequeueOutputBuffer(decoder.get(), &info, timeout); | ||||
|     } | ||||
|     default: { | ||||
|         int offset = info.offset; | ||||
|         buffer = AMediaCodec_getOutputBuffer(decoder.get(), buffer_index, &buffer_size); | ||||
|         while (offset < info.size) { | ||||
|             for (int channel = 0; channel < response.decode_aac_response.num_channels; channel++) { | ||||
|                 u16 pcm_data; | ||||
|                 std::memcpy(&pcm_data, buffer + offset, sizeof(pcm_data)); | ||||
|                 out_streams[channel].push_back(pcm_data); | ||||
|                 offset += sizeof(pcm_data); | ||||
|             } | ||||
|         } | ||||
|         AMediaCodec_releaseOutputBuffer(decoder.get(), buffer_index, info.size != 0); | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     // transfer the decoded buffer from vector to the FCRAM
 | ||||
|     size_t stream0_size = out_streams[0].size() * sizeof(u16); | ||||
|     if (stream0_size != 0) { | ||||
|         if (request.decode_aac_request.dst_addr_ch0 < Memory::FCRAM_PADDR || | ||||
|             request.decode_aac_request.dst_addr_ch0 + stream0_size > | ||||
|                 Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|             LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", | ||||
|                       request.decode_aac_request.dst_addr_ch0); | ||||
|             return response; | ||||
|         } | ||||
|         std::memcpy( | ||||
|             memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch0 - Memory::FCRAM_PADDR), | ||||
|             out_streams[0].data(), stream0_size); | ||||
|     } | ||||
| 
 | ||||
|     size_t stream1_size = out_streams[1].size() * sizeof(u16); | ||||
|     if (stream1_size != 0) { | ||||
|         if (request.decode_aac_request.dst_addr_ch1 < Memory::FCRAM_PADDR || | ||||
|             request.decode_aac_request.dst_addr_ch1 + stream1_size > | ||||
|                 Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|             LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", | ||||
|                       request.decode_aac_request.dst_addr_ch1); | ||||
|             return response; | ||||
|         } | ||||
|         std::memcpy( | ||||
|             memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch1 - Memory::FCRAM_PADDR), | ||||
|             out_streams[1].data(), stream1_size); | ||||
|     } | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| MediaNDKDecoder::MediaNDKDecoder(Memory::MemorySystem& memory) | ||||
|     : impl(std::make_unique<Impl>(memory)) {} | ||||
| 
 | ||||
| MediaNDKDecoder::~MediaNDKDecoder() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> MediaNDKDecoder::ProcessRequest(const BinaryMessage& request) { | ||||
|     return impl->ProcessRequest(request); | ||||
| } | ||||
| 
 | ||||
| bool MediaNDKDecoder::IsValid() const { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -1,22 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "audio_core/hle/decoder.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class MediaNDKDecoder final : public DecoderBase { | ||||
| public: | ||||
|     explicit MediaNDKDecoder(Memory::MemorySystem& memory); | ||||
|     ~MediaNDKDecoder() override; | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) override; | ||||
|     bool IsValid() const override; | ||||
| 
 | ||||
| private: | ||||
|     class Impl; | ||||
|     std::unique_ptr<Impl> impl; | ||||
| }; | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -1,313 +0,0 @@ | |||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "audio_core/hle/wmf_decoder.h" | ||||
| #include "audio_core/hle/wmf_decoder_utils.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| using namespace MFDecoder; | ||||
| 
 | ||||
| class WMFDecoder::Impl { | ||||
| public: | ||||
|     explicit Impl(Memory::MemorySystem& memory); | ||||
|     ~Impl(); | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); | ||||
|     bool IsValid() const { | ||||
|         return is_valid; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::optional<BinaryMessage> Initalize(const BinaryMessage& request); | ||||
| 
 | ||||
|     std::optional<BinaryMessage> Decode(const BinaryMessage& request); | ||||
| 
 | ||||
|     MFOutputState DecodingLoop(AudioCore::ADTSData adts_header, | ||||
|                                std::array<std::vector<u8>, 2>& out_streams); | ||||
| 
 | ||||
|     bool transform_initialized = false; | ||||
|     bool format_selected = false; | ||||
| 
 | ||||
|     Memory::MemorySystem& memory; | ||||
| 
 | ||||
|     unique_mfptr<IMFTransform> transform; | ||||
|     DWORD in_stream_id = 0; | ||||
|     DWORD out_stream_id = 0; | ||||
|     bool is_valid = false; | ||||
|     bool mf_started = false; | ||||
|     bool coinited = false; | ||||
| }; | ||||
| 
 | ||||
| WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { | ||||
|     // Attempt to load the symbols for mf.dll
 | ||||
|     if (!InitMFDLL()) { | ||||
|         LOG_CRITICAL(Audio_DSP, | ||||
|                      "Unable to load mf.dll. AAC audio through media foundation unavailable"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     HRESULT hr = S_OK; | ||||
|     hr = CoInitialize(NULL); | ||||
|     // S_FALSE will be returned when COM has already been initialized
 | ||||
|     if (hr != S_OK && hr != S_FALSE) { | ||||
|         ReportError("Failed to start COM components", hr); | ||||
|     } else { | ||||
|         coinited = true; | ||||
|     } | ||||
| 
 | ||||
|     // lite startup is faster and all what we need is included
 | ||||
|     hr = MFDecoder::MFStartup(MF_VERSION, MFSTARTUP_LITE); | ||||
|     if (hr != S_OK) { | ||||
|         // Do you know you can't initialize MF in test mode or safe mode?
 | ||||
|         ReportError("Failed to initialize Media Foundation", hr); | ||||
|     } else { | ||||
|         mf_started = true; | ||||
|     } | ||||
| 
 | ||||
|     LOG_INFO(Audio_DSP, "Media Foundation activated"); | ||||
| 
 | ||||
|     // initialize transform
 | ||||
|     transform = MFDecoderInit(); | ||||
|     if (transform == nullptr) { | ||||
|         LOG_CRITICAL(Audio_DSP, "Can't initialize decoder"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     hr = transform->GetStreamIDs(1, &in_stream_id, 1, &out_stream_id); | ||||
|     if (hr == E_NOTIMPL) { | ||||
|         // if not implemented, it means this MFT does not assign stream ID for you
 | ||||
|         in_stream_id = 0; | ||||
|         out_stream_id = 0; | ||||
|     } else if (FAILED(hr)) { | ||||
|         ReportError("Decoder failed to initialize the stream ID", hr); | ||||
|         return; | ||||
|     } | ||||
|     transform_initialized = true; | ||||
|     is_valid = true; | ||||
| } | ||||
| 
 | ||||
| WMFDecoder::Impl::~Impl() { | ||||
|     if (transform_initialized) { | ||||
|         MFFlush(transform.get()); | ||||
|         // delete the transform object before shutting down MF
 | ||||
|         // otherwise access violation will occur
 | ||||
|         transform.reset(); | ||||
|     } | ||||
|     if (mf_started) { | ||||
|         MFDecoder::MFShutdown(); | ||||
|     } | ||||
|     if (coinited) { | ||||
|         CoUninitialize(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> WMFDecoder::Impl::ProcessRequest(const BinaryMessage& request) { | ||||
|     if (request.header.codec != DecoderCodec::DecodeAAC) { | ||||
|         LOG_ERROR(Audio_DSP, "Got unknown codec {}", static_cast<u16>(request.header.codec)); | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     switch (request.header.cmd) { | ||||
|     case DecoderCommand::Init: { | ||||
|         LOG_INFO(Audio_DSP, "WMFDecoder initializing"); | ||||
|         return Initalize(request); | ||||
|     } | ||||
|     case DecoderCommand::EncodeDecode: { | ||||
|         return Decode(request); | ||||
|     } | ||||
|     case DecoderCommand::Shutdown: | ||||
|     case DecoderCommand::SaveState: | ||||
|     case DecoderCommand::LoadState: { | ||||
|         LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", | ||||
|                     static_cast<u16>(request.header.cmd)); | ||||
|         BinaryMessage response = request; | ||||
|         response.header.result = ResultStatus::Success; | ||||
|         return response; | ||||
|     } | ||||
|     default: | ||||
|         LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", | ||||
|                   static_cast<u16>(request.header.cmd)); | ||||
|         return std::nullopt; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> WMFDecoder::Impl::Initalize(const BinaryMessage& request) { | ||||
|     BinaryMessage response = request; | ||||
|     response.header.result = ResultStatus::Success; | ||||
| 
 | ||||
|     format_selected = false; // select format again if application request initialize the DSP
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| MFOutputState WMFDecoder::Impl::DecodingLoop(AudioCore::ADTSData adts_header, | ||||
|                                              std::array<std::vector<u8>, 2>& out_streams) { | ||||
|     std::optional<std::vector<f32>> output_buffer; | ||||
| 
 | ||||
|     while (true) { | ||||
|         auto [output_status, output] = ReceiveSample(transform.get(), out_stream_id); | ||||
| 
 | ||||
|         // 0 -> okay; 3 -> okay but more data available (buffer too small)
 | ||||
|         if (output_status == MFOutputState::OK || output_status == MFOutputState::HaveMoreData) { | ||||
|             output_buffer = CopySampleToBuffer(output.get()); | ||||
| 
 | ||||
|             // the following was taken from ffmpeg version of the decoder
 | ||||
|             f32 val_f32; | ||||
|             for (std::size_t i = 0; i < output_buffer->size();) { | ||||
|                 for (std::size_t channel = 0; channel < adts_header.channels; channel++) { | ||||
|                     val_f32 = std::clamp(output_buffer->at(i), -1.0f, 1.0f); | ||||
|                     s16 val = static_cast<s16>(0x7FFF * val_f32); | ||||
|                     out_streams[channel].push_back(val & 0xFF); | ||||
|                     out_streams[channel].push_back(val >> 8); | ||||
|                     // i is incremented on per channel basis
 | ||||
|                     i++; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // If we return OK here, the decoder won't be in a state to receive new data and will fail
 | ||||
|         // on the next call; instead treat it like the HaveMoreData case
 | ||||
|         if (output_status == MFOutputState::OK) | ||||
|             continue; | ||||
| 
 | ||||
|         // for status = 2, reset MF
 | ||||
|         if (output_status == MFOutputState::NeedReconfig) { | ||||
|             format_selected = false; | ||||
|             return MFOutputState::NeedReconfig; | ||||
|         } | ||||
| 
 | ||||
|         // for status = 3, try again with new buffer
 | ||||
|         if (output_status == MFOutputState::HaveMoreData) | ||||
|             continue; | ||||
| 
 | ||||
|         // according to MS document, this is not an error (?!)
 | ||||
|         if (output_status == MFOutputState::NeedMoreInput) | ||||
|             return MFOutputState::NeedMoreInput; | ||||
| 
 | ||||
|         return MFOutputState::FatalError; // return on other status
 | ||||
|     } | ||||
| 
 | ||||
|     return MFOutputState::FatalError; | ||||
| } | ||||
| 
 | ||||
| std::optional<BinaryMessage> WMFDecoder::Impl::Decode(const BinaryMessage& request) { | ||||
|     BinaryMessage response{}; | ||||
|     response.header.codec = request.header.codec; | ||||
|     response.header.cmd = request.header.cmd; | ||||
|     response.decode_aac_response.size = request.decode_aac_request.size; | ||||
|     response.decode_aac_response.num_channels = 2; | ||||
|     response.decode_aac_response.num_samples = 1024; | ||||
| 
 | ||||
|     if (!transform_initialized) { | ||||
|         LOG_DEBUG(Audio_DSP, "Decoder not initialized"); | ||||
|         // This is a hack to continue games when decoder failed to initialize
 | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || | ||||
|         request.decode_aac_request.src_addr + request.decode_aac_request.size > | ||||
|             Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|         LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", | ||||
|                   request.decode_aac_request.src_addr); | ||||
|         return std::nullopt; | ||||
|     } | ||||
|     const u8* data = | ||||
|         memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); | ||||
| 
 | ||||
|     std::array<std::vector<u8>, 2> out_streams; | ||||
|     unique_mfptr<IMFSample> sample; | ||||
|     MFInputState input_status = MFInputState::OK; | ||||
|     MFOutputState output_status = MFOutputState::OK; | ||||
|     std::optional<ADTSMeta> adts_meta = DetectMediaType(data, request.decode_aac_request.size); | ||||
| 
 | ||||
|     if (!adts_meta) { | ||||
|         LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); | ||||
|         return response; | ||||
|     } | ||||
| 
 | ||||
|     response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_meta->ADTSHeader.samplerate); | ||||
|     response.decode_aac_response.num_channels = adts_meta->ADTSHeader.channels; | ||||
| 
 | ||||
|     if (!format_selected) { | ||||
|         LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}", | ||||
|                   adts_meta->ADTSHeader.channels, adts_meta->ADTSHeader.samplerate); | ||||
|         SelectInputMediaType(transform.get(), in_stream_id, adts_meta->ADTSHeader, | ||||
|                              adts_meta->AACTag, 14); | ||||
|         SelectOutputMediaType(transform.get(), out_stream_id); | ||||
|         SendSample(transform.get(), in_stream_id, nullptr); | ||||
|         // cache the result from detect_mediatype and call select_*_mediatype only once
 | ||||
|         // This could increase performance very slightly
 | ||||
|         transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); | ||||
|         format_selected = true; | ||||
|     } | ||||
| 
 | ||||
|     sample = CreateSample(data, request.decode_aac_request.size, 1, 0); | ||||
|     sample->SetUINT32(MFSampleExtension_CleanPoint, 1); | ||||
| 
 | ||||
|     while (true) { | ||||
|         input_status = SendSample(transform.get(), in_stream_id, sample.get()); | ||||
|         output_status = DecodingLoop(adts_meta->ADTSHeader, out_streams); | ||||
| 
 | ||||
|         if (output_status == MFOutputState::FatalError) { | ||||
|             // if the decode issues are caused by MFT not accepting new samples, try again
 | ||||
|             // NOTICE: you are required to check the output even if you already knew/guessed
 | ||||
|             // MFT didn't accept the input sample
 | ||||
|             if (input_status == MFInputState::NotAccepted) { | ||||
|                 // try again
 | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             LOG_ERROR(Audio_DSP, "Errors occurred when receiving output"); | ||||
|             return response; | ||||
|         } else if (output_status == MFOutputState::NeedReconfig) { | ||||
|             // flush the transform
 | ||||
|             MFFlush(transform.get()); | ||||
|             // decode again
 | ||||
|             return this->Decode(request); | ||||
|         } | ||||
| 
 | ||||
|         break; // jump out of the loop if at least we don't have obvious issues
 | ||||
|     } | ||||
| 
 | ||||
|     if (out_streams[0].size() != 0) { | ||||
|         if (request.decode_aac_request.dst_addr_ch0 < Memory::FCRAM_PADDR || | ||||
|             request.decode_aac_request.dst_addr_ch0 + out_streams[0].size() > | ||||
|                 Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|             LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", | ||||
|                       request.decode_aac_request.dst_addr_ch0); | ||||
|             return std::nullopt; | ||||
|         } | ||||
|         std::memcpy( | ||||
|             memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch0 - Memory::FCRAM_PADDR), | ||||
|             out_streams[0].data(), out_streams[0].size()); | ||||
|     } | ||||
| 
 | ||||
|     if (out_streams[1].size() != 0) { | ||||
|         if (request.decode_aac_request.dst_addr_ch1 < Memory::FCRAM_PADDR || | ||||
|             request.decode_aac_request.dst_addr_ch1 + out_streams[1].size() > | ||||
|                 Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||||
|             LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", | ||||
|                       request.decode_aac_request.dst_addr_ch1); | ||||
|             return std::nullopt; | ||||
|         } | ||||
|         std::memcpy( | ||||
|             memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch1 - Memory::FCRAM_PADDR), | ||||
|             out_streams[1].data(), out_streams[1].size()); | ||||
|     } | ||||
| 
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| WMFDecoder::WMFDecoder(Memory::MemorySystem& memory) : impl(std::make_unique<Impl>(memory)) {} | ||||
| 
 | ||||
| WMFDecoder::~WMFDecoder() = default; | ||||
| 
 | ||||
| std::optional<BinaryMessage> WMFDecoder::ProcessRequest(const BinaryMessage& request) { | ||||
|     return impl->ProcessRequest(request); | ||||
| } | ||||
| 
 | ||||
| bool WMFDecoder::IsValid() const { | ||||
|     return impl->IsValid(); | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -1,23 +0,0 @@ | |||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "audio_core/hle/decoder.h" | ||||
| 
 | ||||
| namespace AudioCore::HLE { | ||||
| 
 | ||||
| class WMFDecoder final : public DecoderBase { | ||||
| public: | ||||
|     explicit WMFDecoder(Memory::MemorySystem& memory); | ||||
|     ~WMFDecoder() override; | ||||
|     std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request) override; | ||||
|     bool IsValid() const override; | ||||
| 
 | ||||
| private: | ||||
|     class Impl; | ||||
|     std::unique_ptr<Impl> impl; | ||||
| }; | ||||
| 
 | ||||
| } // namespace AudioCore::HLE
 | ||||
|  | @ -1,464 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| #include "common/logging/log.h" | ||||
| #include "common/string_util.h" | ||||
| #include "wmf_decoder_utils.h" | ||||
| 
 | ||||
| namespace MFDecoder { | ||||
| 
 | ||||
| // utility functions
 | ||||
| void ReportError(std::string msg, HRESULT hr) { | ||||
|     if (SUCCEEDED(hr)) { | ||||
|         return; | ||||
|     } | ||||
|     LPWSTR err; | ||||
|     FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | | ||||
|                        FORMAT_MESSAGE_IGNORE_INSERTS, | ||||
|                    nullptr, hr, | ||||
|                    // hardcode to use en_US because if any user had problems with this
 | ||||
|                    // we can help them w/o translating anything
 | ||||
|                    // default is to use the language currently active on the operating system
 | ||||
|                    MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPWSTR)&err, 0, nullptr); | ||||
|     if (err != nullptr) { | ||||
|         LOG_CRITICAL(Audio_DSP, "{}: {}", msg, Common::UTF16ToUTF8(err)); | ||||
|         LocalFree(err); | ||||
|     } | ||||
|     LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr); | ||||
| } | ||||
| 
 | ||||
| unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format) { | ||||
| 
 | ||||
|     HRESULT hr = S_OK; | ||||
|     MFT_REGISTER_TYPE_INFO reg{}; | ||||
|     GUID category = MFT_CATEGORY_AUDIO_DECODER; | ||||
|     IMFActivate** activate; | ||||
|     unique_mfptr<IMFTransform> transform; | ||||
|     UINT32 num_activate; | ||||
| 
 | ||||
|     reg.guidMajorType = MFMediaType_Audio; | ||||
|     reg.guidSubtype = audio_format; | ||||
| 
 | ||||
|     hr = MFTEnumEx(category, | ||||
|                    MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER, | ||||
|                    ®, nullptr, &activate, &num_activate); | ||||
|     if (FAILED(hr) || num_activate < 1) { | ||||
|         ReportError("Failed to enumerate decoders", hr); | ||||
|         CoTaskMemFree(activate); | ||||
|         return nullptr; | ||||
|     } | ||||
|     LOG_INFO(Audio_DSP, "Windows(R) Media Foundation found {} suitable decoder(s)", num_activate); | ||||
|     for (unsigned int n = 0; n < num_activate; n++) { | ||||
|         hr = activate[n]->ActivateObject( | ||||
|             IID_IMFTransform, | ||||
|             reinterpret_cast<void**>(static_cast<IMFTransform**>(Amp(transform)))); | ||||
|         if (FAILED(hr)) | ||||
|             transform = nullptr; | ||||
|         activate[n]->Release(); | ||||
|         if (SUCCEEDED(hr)) | ||||
|             break; | ||||
|     } | ||||
|     if (transform == nullptr) { | ||||
|         ReportError("Failed to initialize MFT", hr); | ||||
|         CoTaskMemFree(activate); | ||||
|         return nullptr; | ||||
|     } | ||||
|     CoTaskMemFree(activate); | ||||
|     return transform; | ||||
| } | ||||
| 
 | ||||
| unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment, | ||||
|                                      LONGLONG duration) { | ||||
|     HRESULT hr = S_OK; | ||||
|     unique_mfptr<IMFMediaBuffer> buf; | ||||
|     unique_mfptr<IMFSample> sample; | ||||
| 
 | ||||
|     hr = MFCreateSample(Amp(sample)); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("Unable to allocate a sample", hr); | ||||
|         return nullptr; | ||||
|     } | ||||
|     // Yes, the argument for alignment is the actual alignment - 1
 | ||||
|     hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, Amp(buf)); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("Unable to allocate a memory buffer for sample", hr); | ||||
|         return nullptr; | ||||
|     } | ||||
|     if (data) { | ||||
|         BYTE* buffer; | ||||
|         // lock the MediaBuffer
 | ||||
|         // this is actually not a thread-safe lock
 | ||||
|         hr = buf->Lock(&buffer, nullptr, nullptr); | ||||
|         if (FAILED(hr)) { | ||||
|             ReportError("Unable to lock down MediaBuffer", hr); | ||||
|             return nullptr; | ||||
|         } | ||||
| 
 | ||||
|         std::memcpy(buffer, data, len); | ||||
| 
 | ||||
|         buf->SetCurrentLength(len); | ||||
|         buf->Unlock(); | ||||
|     } | ||||
| 
 | ||||
|     sample->AddBuffer(buf.get()); | ||||
|     hr = sample->SetSampleDuration(duration); | ||||
|     if (FAILED(hr)) { | ||||
|         // MFT will take a guess for you in this case
 | ||||
|         ReportError("Unable to set sample duration, but continuing anyway", hr); | ||||
|     } | ||||
| 
 | ||||
|     return sample; | ||||
| } | ||||
| 
 | ||||
| bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, | ||||
|                           const AudioCore::ADTSData& adts, const UINT8* user_data, | ||||
|                           UINT32 user_data_len, GUID audio_format) { | ||||
|     HRESULT hr = S_OK; | ||||
|     unique_mfptr<IMFMediaType> t; | ||||
| 
 | ||||
|     // actually you can get rid of the whole block of searching and filtering mess
 | ||||
|     // if you know the exact parameters of your media stream
 | ||||
|     hr = MFCreateMediaType(Amp(t)); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("Unable to create an empty MediaType", hr); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // basic definition
 | ||||
|     t->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); | ||||
|     t->SetGUID(MF_MT_SUBTYPE, audio_format); | ||||
| 
 | ||||
|     t->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 1); | ||||
|     t->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, adts.channels); | ||||
|     t->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, adts.samplerate); | ||||
|     // 0xfe = 254 = "unspecified"
 | ||||
|     t->SetUINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 254); | ||||
|     t->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1); | ||||
|     t->SetBlob(MF_MT_USER_DATA, user_data, user_data_len); | ||||
|     hr = transform->SetInputType(in_stream_id, t.get(), 0); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("failed to select input types for MFT", hr); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format) { | ||||
|     HRESULT hr = S_OK; | ||||
|     UINT32 tmp; | ||||
|     unique_mfptr<IMFMediaType> type; | ||||
| 
 | ||||
|     // If you know what you need and what you are doing, you can specify the conditions instead of
 | ||||
|     // searching but it's better to use search since MFT may or may not support your output
 | ||||
|     // parameters
 | ||||
|     for (DWORD i = 0;; i++) { | ||||
|         hr = transform->GetOutputAvailableType(out_stream_id, i, Amp(type)); | ||||
|         if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) { | ||||
|             return true; | ||||
|         } | ||||
|         if (FAILED(hr)) { | ||||
|             ReportError("failed to get output types for MFT", hr); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         hr = type->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp); | ||||
| 
 | ||||
|         if (FAILED(hr)) | ||||
|             continue; | ||||
|         // select PCM-16 format
 | ||||
|         if (tmp == 32) { | ||||
|             hr = type->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1); | ||||
|             if (FAILED(hr)) { | ||||
|                 ReportError("failed to set MF_MT_AUDIO_BLOCK_ALIGNMENT for MFT on output stream", | ||||
|                             hr); | ||||
|                 return false; | ||||
|             } | ||||
|             hr = transform->SetOutputType(out_stream_id, type.get(), 0); | ||||
|             if (FAILED(hr)) { | ||||
|                 ReportError("failed to select output types for MFT", hr); | ||||
|                 return false; | ||||
|             } | ||||
|             return true; | ||||
|         } else { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     ReportError("MFT: Unable to find preferred output format", E_NOTIMPL); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| std::optional<ADTSMeta> DetectMediaType(const u8* buffer, std::size_t len) { | ||||
|     if (len < 7) { | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     AudioCore::ADTSData tmp; | ||||
|     ADTSMeta result; | ||||
|     // see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag
 | ||||
|     // for the meaning of the byte array below
 | ||||
| 
 | ||||
|     // it might be a good idea to wrap the parameters into a struct
 | ||||
|     // and pass that struct into the function but doing that will lead to messier code
 | ||||
|     // const UINT8 aac_data[] = { 0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x11, 0x90
 | ||||
|     // }; first byte: 0: raw aac 1: adts 2: adif 3: latm/laos
 | ||||
|     UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; | ||||
|     uint16_t tag = 0; | ||||
| 
 | ||||
|     tmp = AudioCore::ParseADTS(buffer); | ||||
|     if (tmp.length == 0) { | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     tag = MFGetAACTag(tmp); | ||||
|     aac_tmp[12] |= (tag & 0xff00) >> 8; | ||||
|     aac_tmp[13] |= (tag & 0x00ff); | ||||
|     std::memcpy(&(result.ADTSHeader), &tmp, sizeof(AudioCore::ADTSData)); | ||||
|     std::memcpy(&(result.AACTag), aac_tmp, 14); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void MFFlush(IMFTransform* transform) { | ||||
|     HRESULT hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("MFT: Flush command failed", hr); | ||||
|     } | ||||
|     hr = transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("Failed to end streaming for MFT", hr); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample) { | ||||
|     HRESULT hr = S_OK; | ||||
| 
 | ||||
|     if (in_sample) { | ||||
|         hr = transform->ProcessInput(in_stream_id, in_sample, 0); | ||||
|         if (hr == MF_E_NOTACCEPTING) { | ||||
|             return MFInputState::NotAccepted; // try again
 | ||||
|         } else if (FAILED(hr)) { | ||||
|             ReportError("MFT: Failed to process input", hr); | ||||
|             return MFInputState::FatalError; | ||||
|         } // FAILED(hr)
 | ||||
|     } else { | ||||
|         hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); | ||||
|         if (FAILED(hr)) { | ||||
|             ReportError("MFT: Failed to drain when processing input", hr); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return MFInputState::OK; | ||||
| } | ||||
| 
 | ||||
| std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* transform, | ||||
|                                                                  DWORD out_stream_id) { | ||||
|     HRESULT hr; | ||||
|     MFT_OUTPUT_DATA_BUFFER out_buffers; | ||||
|     MFT_OUTPUT_STREAM_INFO out_info; | ||||
|     DWORD status = 0; | ||||
|     unique_mfptr<IMFSample> sample; | ||||
|     bool mft_create_sample = false; | ||||
| 
 | ||||
|     hr = transform->GetOutputStreamInfo(out_stream_id, &out_info); | ||||
| 
 | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("MFT: Failed to get stream info", hr); | ||||
|         return std::make_tuple(MFOutputState::FatalError, std::move(sample)); | ||||
|     } | ||||
|     mft_create_sample = (out_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) || | ||||
|                         (out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES); | ||||
| 
 | ||||
|     while (true) { | ||||
|         status = 0; | ||||
| 
 | ||||
|         if (!mft_create_sample) { | ||||
|             sample = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment); | ||||
|             if (!sample.get()) { | ||||
|                 ReportError("MFT: Unable to allocate memory for samples", hr); | ||||
|                 return std::make_tuple(MFOutputState::FatalError, std::move(sample)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         out_buffers.dwStreamID = out_stream_id; | ||||
|         out_buffers.pSample = sample.get(); | ||||
| 
 | ||||
|         hr = transform->ProcessOutput(0, 1, &out_buffers, &status); | ||||
| 
 | ||||
|         if (!FAILED(hr)) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { | ||||
|             // Most likely reasons: data corrupted; your actions not expected by MFT
 | ||||
|             return std::make_tuple(MFOutputState::NeedMoreInput, std::move(sample)); | ||||
|         } | ||||
| 
 | ||||
|         if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { | ||||
|             ReportError("MFT: stream format changed, re-configuration required", hr); | ||||
|             return std::make_tuple(MFOutputState::NeedReconfig, std::move(sample)); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { | ||||
|         // this status is also unreliable but whatever
 | ||||
|         return std::make_tuple(MFOutputState::HaveMoreData, std::move(sample)); | ||||
|     } | ||||
| 
 | ||||
|     if (out_buffers.pSample == nullptr) { | ||||
|         ReportError("MFT: decoding failure", hr); | ||||
|         return std::make_tuple(MFOutputState::FatalError, std::move(sample)); | ||||
|     } | ||||
| 
 | ||||
|     return std::make_tuple(MFOutputState::OK, std::move(sample)); | ||||
| } | ||||
| 
 | ||||
| std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample) { | ||||
|     unique_mfptr<IMFMediaBuffer> buffer; | ||||
|     HRESULT hr = S_OK; | ||||
|     std::optional<std::vector<f32>> output; | ||||
|     std::vector<f32> output_buffer; | ||||
|     BYTE* data; | ||||
|     DWORD len = 0; | ||||
| 
 | ||||
|     hr = sample->GetTotalLength(&len); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("Failed to get the length of sample buffer", hr); | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     hr = sample->ConvertToContiguousBuffer(Amp(buffer)); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("Failed to get sample buffer", hr); | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     hr = buffer->Lock(&data, nullptr, nullptr); | ||||
|     if (FAILED(hr)) { | ||||
|         ReportError("Failed to lock the buffer", hr); | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     output_buffer.resize(len / sizeof(f32)); | ||||
|     std::memcpy(output_buffer.data(), data, len); | ||||
|     output = output_buffer; | ||||
| 
 | ||||
|     // if buffer unlock fails, then... whatever, we have already got data
 | ||||
|     buffer->Unlock(); | ||||
|     return output; | ||||
| } | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| struct LibraryDeleter { | ||||
|     using pointer = HMODULE; | ||||
|     void operator()(HMODULE h) const { | ||||
|         if (h != nullptr) | ||||
|             FreeLibrary(h); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| std::unique_ptr<HMODULE, LibraryDeleter> mf_dll{nullptr}; | ||||
| std::unique_ptr<HMODULE, LibraryDeleter> mfplat_dll{nullptr}; | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| bool InitMFDLL() { | ||||
| 
 | ||||
|     mf_dll.reset(LoadLibrary(TEXT("mf.dll"))); | ||||
|     if (!mf_dll) { | ||||
|         DWORD error_message_id = GetLastError(); | ||||
|         LPSTR message_buffer = nullptr; | ||||
|         size_t size = | ||||
|             FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | ||||
|                                FORMAT_MESSAGE_IGNORE_INSERTS, | ||||
|                            nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||||
|                            reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr); | ||||
| 
 | ||||
|         std::string message(message_buffer, size); | ||||
| 
 | ||||
|         LocalFree(message_buffer); | ||||
|         LOG_ERROR(Audio_DSP, "Could not load mf.dll: {}", message); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     mfplat_dll.reset(LoadLibrary(TEXT("mfplat.dll"))); | ||||
|     if (!mfplat_dll) { | ||||
|         DWORD error_message_id = GetLastError(); | ||||
|         LPSTR message_buffer = nullptr; | ||||
|         size_t size = | ||||
|             FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | ||||
|                                FORMAT_MESSAGE_IGNORE_INSERTS, | ||||
|                            nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||||
|                            reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr); | ||||
| 
 | ||||
|         std::string message(message_buffer, size); | ||||
| 
 | ||||
|         LocalFree(message_buffer); | ||||
|         LOG_ERROR(Audio_DSP, "Could not load mfplat.dll: {}", message); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     MFStartup = Symbol<HRESULT(ULONG, DWORD)>(mfplat_dll.get(), "MFStartup"); | ||||
|     if (!MFStartup) { | ||||
|         LOG_ERROR(Audio_DSP, "Cannot load function MFStartup"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     MFShutdown = Symbol<HRESULT(void)>(mfplat_dll.get(), "MFShutdown"); | ||||
|     if (!MFShutdown) { | ||||
|         LOG_ERROR(Audio_DSP, "Cannot load function MFShutdown"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     MFShutdownObject = Symbol<HRESULT(IUnknown*)>(mf_dll.get(), "MFShutdownObject"); | ||||
|     if (!MFShutdownObject) { | ||||
|         LOG_ERROR(Audio_DSP, "Cannot load function MFShutdownObject"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     MFCreateAlignedMemoryBuffer = Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)>( | ||||
|         mfplat_dll.get(), "MFCreateAlignedMemoryBuffer"); | ||||
|     if (!MFCreateAlignedMemoryBuffer) { | ||||
|         LOG_ERROR(Audio_DSP, "Cannot load function MFCreateAlignedMemoryBuffer"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     MFCreateSample = Symbol<HRESULT(IMFSample**)>(mfplat_dll.get(), "MFCreateSample"); | ||||
|     if (!MFCreateSample) { | ||||
|         LOG_ERROR(Audio_DSP, "Cannot load function MFCreateSample"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     MFTEnumEx = | ||||
|         Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*, | ||||
|                        IMFActivate***, UINT32*)>(mfplat_dll.get(), "MFTEnumEx"); | ||||
|     if (!MFTEnumEx) { | ||||
|         LOG_ERROR(Audio_DSP, "Cannot load function MFTEnumEx"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     MFCreateMediaType = Symbol<HRESULT(IMFMediaType**)>(mfplat_dll.get(), "MFCreateMediaType"); | ||||
|     if (!MFCreateMediaType) { | ||||
|         LOG_ERROR(Audio_DSP, "Cannot load function MFCreateMediaType"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| Symbol<HRESULT(ULONG, DWORD)> MFStartup; | ||||
| Symbol<HRESULT(void)> MFShutdown; | ||||
| Symbol<HRESULT(IUnknown*)> MFShutdownObject; | ||||
| Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)> MFCreateAlignedMemoryBuffer; | ||||
| Symbol<HRESULT(IMFSample**)> MFCreateSample; | ||||
| Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*, | ||||
|                IMFActivate***, UINT32*)> | ||||
|     MFTEnumEx; | ||||
| Symbol<HRESULT(IMFMediaType**)> MFCreateMediaType; | ||||
| 
 | ||||
| } // namespace MFDecoder
 | ||||
|  | @ -1,125 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <optional> | ||||
| #include <string> | ||||
| #include <tuple> | ||||
| #include <vector> | ||||
| #include <comdef.h> | ||||
| #include <mfapi.h> | ||||
| #include <mferror.h> | ||||
| #include <mfidl.h> | ||||
| #include <mftransform.h> | ||||
| 
 | ||||
| #include "adts.h" | ||||
| 
 | ||||
| namespace MFDecoder { | ||||
| 
 | ||||
| template <typename T> | ||||
| struct Symbol { | ||||
|     Symbol() = default; | ||||
|     Symbol(HMODULE dll, const char* name) { | ||||
|         if (dll) { | ||||
|             ptr_symbol = reinterpret_cast<T*>(GetProcAddress(dll, name)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     operator T*() const { | ||||
|         return ptr_symbol; | ||||
|     } | ||||
| 
 | ||||
|     explicit operator bool() const { | ||||
|         return ptr_symbol != nullptr; | ||||
|     } | ||||
| 
 | ||||
|     T* ptr_symbol = nullptr; | ||||
| }; | ||||
| 
 | ||||
| // Runtime load the MF symbols to prevent mf.dll not found errors on citra load
 | ||||
| extern Symbol<HRESULT(ULONG, DWORD)> MFStartup; | ||||
| extern Symbol<HRESULT(void)> MFShutdown; | ||||
| extern Symbol<HRESULT(IUnknown*)> MFShutdownObject; | ||||
| extern Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)> MFCreateAlignedMemoryBuffer; | ||||
| extern Symbol<HRESULT(IMFSample**)> MFCreateSample; | ||||
| extern Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*, | ||||
|                       IMFActivate***, UINT32*)> | ||||
|     MFTEnumEx; | ||||
| extern Symbol<HRESULT(IMFMediaType**)> MFCreateMediaType; | ||||
| 
 | ||||
| enum class MFOutputState { FatalError, OK, NeedMoreInput, NeedReconfig, HaveMoreData }; | ||||
| enum class MFInputState { FatalError, OK, NotAccepted }; | ||||
| 
 | ||||
| // utility functions / templates
 | ||||
| template <class T> | ||||
| struct MFRelease { | ||||
|     void operator()(T* pointer) const { | ||||
|         pointer->Release(); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| template <> | ||||
| struct MFRelease<IMFTransform> { | ||||
|     void operator()(IMFTransform* pointer) const { | ||||
|         MFShutdownObject(pointer); | ||||
|         pointer->Release(); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| // wrapper facilities for dealing with pointers
 | ||||
| template <typename T> | ||||
| using unique_mfptr = std::unique_ptr<T, MFRelease<T>>; | ||||
| 
 | ||||
| template <typename SmartPtr, typename RawPtr> | ||||
| class AmpImpl { | ||||
| public: | ||||
|     AmpImpl(SmartPtr& smart_ptr) : smart_ptr(smart_ptr) {} | ||||
|     ~AmpImpl() { | ||||
|         smart_ptr.reset(raw_ptr); | ||||
|     } | ||||
| 
 | ||||
|     operator RawPtr*() { | ||||
|         return &raw_ptr; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     SmartPtr& smart_ptr; | ||||
|     RawPtr raw_ptr = nullptr; | ||||
| }; | ||||
| 
 | ||||
| template <typename SmartPtr> | ||||
| auto Amp(SmartPtr& smart_ptr) { | ||||
|     return AmpImpl<SmartPtr, decltype(smart_ptr.get())>(smart_ptr); | ||||
| } | ||||
| 
 | ||||
| // convient function for formatting error messages
 | ||||
| void ReportError(std::string msg, HRESULT hr); | ||||
| 
 | ||||
| // data type for transferring ADTS metadata between functions
 | ||||
| struct ADTSMeta { | ||||
|     AudioCore::ADTSData ADTSHeader; | ||||
|     u8 AACTag[14]; | ||||
| }; | ||||
| 
 | ||||
| // exported functions
 | ||||
| 
 | ||||
| /// Loads the symbols from mf.dll at runtime. Returns false if the symbols can't be loaded
 | ||||
| bool InitMFDLL(); | ||||
| unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); | ||||
| unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1, | ||||
|                                      LONGLONG duration = 0); | ||||
| bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, | ||||
|                           const AudioCore::ADTSData& adts, const UINT8* user_data, | ||||
|                           UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); | ||||
| std::optional<ADTSMeta> DetectMediaType(const u8* buffer, std::size_t len); | ||||
| bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, | ||||
|                            GUID audio_format = MFAudioFormat_PCM); | ||||
| void MFFlush(IMFTransform* transform); | ||||
| MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); | ||||
| std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* transform, | ||||
|                                                                  DWORD out_stream_id); | ||||
| std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample); | ||||
| 
 | ||||
| } // namespace MFDecoder
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue