From c521f3b7d65d2d5bb210e7a813382f2fec6d117c Mon Sep 17 00:00:00 2001 From: B3N30 Date: Sun, 9 Dec 2018 22:25:45 +0100 Subject: [PATCH 01/34] CoreAudio::HLE: Add FFmpeg aac decoder --- .travis/linux-frozen/docker.sh | 2 +- .travis/linux/docker.sh | 2 +- .travis/macos/deps.sh | 2 +- .travis/transifex/docker.sh | 2 +- CMakeLists.txt | 34 ++++ appveyor.yml | 2 +- externals/cmake-modules/FindFFmpeg.cmake | 183 +++++++++++++++++ src/audio_core/CMakeLists.txt | 13 ++ src/audio_core/hle/aac_decoder.cpp | 241 +++++++++++++++++++++++ src/audio_core/hle/aac_decoder.h | 22 +++ src/audio_core/hle/decoder.cpp | 36 ++++ src/audio_core/hle/decoder.h | 68 +++++++ src/audio_core/hle/ffmpeg_dl.h | 213 ++++++++++++++++++++ src/audio_core/hle/hle.cpp | 35 ++++ 14 files changed, 850 insertions(+), 5 deletions(-) create mode 100644 externals/cmake-modules/FindFFmpeg.cmake create mode 100644 src/audio_core/hle/aac_decoder.cpp create mode 100644 src/audio_core/hle/aac_decoder.h create mode 100644 src/audio_core/hle/decoder.cpp create mode 100644 src/audio_core/hle/decoder.h create mode 100644 src/audio_core/hle/ffmpeg_dl.h diff --git a/.travis/linux-frozen/docker.sh b/.travis/linux-frozen/docker.sh index a4814da1a..0c4e72b4f 100755 --- a/.travis/linux-frozen/docker.sh +++ b/.travis/linux-frozen/docker.sh @@ -3,7 +3,7 @@ cd /citra mkdir build && cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON +cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=OFF make -j4 ctest -VV -C Release diff --git a/.travis/linux/docker.sh b/.travis/linux/docker.sh index 2a583d6a5..64d8b9417 100755 --- a/.travis/linux/docker.sh +++ b/.travis/linux/docker.sh @@ -3,7 +3,7 @@ cd /citra mkdir build && cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON +cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=OFF make -j4 ctest -VV -C Release diff --git a/.travis/macos/deps.sh b/.travis/macos/deps.sh index dbb6b984b..6eaaf050f 100755 --- a/.travis/macos/deps.sh +++ b/.travis/macos/deps.sh @@ -1,4 +1,4 @@ #!/bin/sh -ex brew update -brew install qt5 sdl2 dylibbundler p7zip ccache +brew install qt5 sdl2 dylibbundler p7zip ccache ffmpeg diff --git a/.travis/transifex/docker.sh b/.travis/transifex/docker.sh index 003b298b6..f608f3848 100644 --- a/.travis/transifex/docker.sh +++ b/.travis/transifex/docker.sh @@ -26,7 +26,7 @@ tx --version cd /citra mkdir build && cd build -cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF +cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF -DENABLE_FFMPEG=OFF make translation cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 491a6d6e4..5d80b97b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,17 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) +option(ENABLE_FFMPEG "Enable FFmpeg decoder/encoder" ON) + option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) +<<<<<<< HEAD +======= +option(ENABLE_SCRIPTING "Enables scripting support" OFF) + +CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_FFMPEG "Download bundled FFmpeg binaries" ON "MSVC" OFF) + +>>>>>>> CoreAudio::HLE: Add FFmpeg aac decoder if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) message(STATUS "Copying pre-commit hook") file(COPY hooks/pre-commit @@ -251,6 +260,31 @@ if (ENABLE_QT) endif() endif() +if (ENABLE_FFMPEG) + if (CITRA_USE_BUNDLED_FFMPEG) + if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64) + set(FFmpeg_VER "ffmpeg-4.0.2-msvc") + else() + message(FATAL_ERROR "No bundled FFmpeg binaries for your toolchain. Disable CITRA_USE_BUNDLED_FFMPEG and provide your own.") + endif() + + if (DEFINED FFmpeg_VER) + download_bundled_external("ffmpeg/" ${FFmpeg_VER} FFmpeg_PREFIX) + set(FFMPEG_DIR "${FFmpeg_PREFIX}") + set(FFMPEG_FOUND YES) + endif() + else() + find_package(FFmpeg REQUIRED COMPONENTS avcodec) + if ("${FFmpeg_avcodec_VERSION}" VERSION_LESS "57.48.101") + message(FATAL_ERROR "Found version for libavcodec is too low. The required version is at least 57.48.101 (included in FFmpeg 3.1 and later).") + else() + set(FFMPEG_FOUND YES) + endif() + endif() +else() + set(FFMPEG_FOUND NO) +endif() + # Platform-specific library requirements # ====================================== diff --git a/appveyor.yml b/appveyor.yml index 3e4299250..fcb824d74 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,7 +26,7 @@ install: - git submodule update --init --recursive - ps: | if ($env:BUILD_TYPE -eq 'mingw') { - $dependencies = "mingw64/mingw-w64-x86_64-qt5" + $dependencies = "mingw64/mingw-w64-x86_64-qt5 mingw64/mingw-w64-x86_64-ffmpeg" # redirect err to null to prevent warnings from becoming errors # workaround to prevent pacman from failing due to cyclical dependencies C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw64/mingw-w64-x86_64-freetype mingw64/mingw-w64-x86_64-fontconfig" 2> $null diff --git a/externals/cmake-modules/FindFFmpeg.cmake b/externals/cmake-modules/FindFFmpeg.cmake new file mode 100644 index 000000000..6cb5960bb --- /dev/null +++ b/externals/cmake-modules/FindFFmpeg.cmake @@ -0,0 +1,183 @@ +# FindFFmpeg +# ---------- +# +# Find the native FFmpeg includes and libraries +# +# This module defines the following variables: +# +# FFmpeg_INCLUDE_: where to find .h +# FFmpeg_LIBRARY_: where to find the library +# FFmpeg_INCLUDES: aggregate all the include paths +# FFmpeg_LIBRARIES: aggregate all the paths to the libraries +# FFmpeg_FOUND: True if all components have been found +# +# This module defines the following targets, which are prefered over variables: +# +# FFmpeg::: Target to use directly, with include path, +# library and dependencies set up. If you are using a static build, you are +# responsible for adding any external dependencies (such as zlib, bzlib...). +# +# can be one of: +# avcodec +# avdevice +# avfilter +# avformat +# postproc +# swresample +# swscale +# + +set(_FFmpeg_ALL_COMPONENTS + avcodec + avdevice + avfilter + avformat + avutil + postproc + swresample + swscale +) + +set(_FFmpeg_DEPS_avcodec avutil) +set(_FFmpeg_DEPS_avdevice avcodec avformat avutil) +set(_FFmpeg_DEPS_avfilter avutil) +set(_FFmpeg_DEPS_avformat avcodec avutil) +set(_FFmpeg_DEPS_postproc avutil) +set(_FFmpeg_DEPS_swresample avutil) +set(_FFmpeg_DEPS_swscale avutil) + +function(find_ffmpeg LIBNAME) + if(DEFINED ENV{FFMPEG_DIR}) + set(FFMPEG_DIR $ENV{FFMPEG_DIR}) + endif() + + if(FFMPEG_DIR) + list(APPEND INCLUDE_PATHS + ${FFMPEG_DIR} + ${FFMPEG_DIR}/ffmpeg + ${FFMPEG_DIR}/lib${LIBNAME} + ${FFMPEG_DIR}/include/lib${LIBNAME} + ${FFMPEG_DIR}/include/ffmpeg + ${FFMPEG_DIR}/include + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + list(APPEND LIB_PATHS + ${FFMPEG_DIR} + ${FFMPEG_DIR}/lib + ${FFMPEG_DIR}/lib${LIBNAME} + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + else() + list(APPEND INCLUDE_PATHS + /usr/local/include/ffmpeg + /usr/local/include/lib${LIBNAME} + /usr/include/ffmpeg + /usr/include/lib${LIBNAME} + /usr/include/ffmpeg/lib${LIBNAME} + ) + + list(APPEND LIB_PATHS + /usr/local/lib + /usr/lib + ) + endif() + + find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h + HINTS ${INCLUDE_PATHS} + ) + + find_library(FFmpeg_LIBRARY_${LIBNAME} ${LIBNAME} + HINTS ${LIB_PATHS} + ) + + if(NOT FFMPEG_DIR AND (NOT FFmpeg_LIBRARY_${LIBNAME} OR NOT FFmpeg_INCLUDE_${LIBNAME})) + # Didn't find it in the usual paths, try pkg-config + find_package(PkgConfig QUIET) + pkg_check_modules(FFmpeg_PKGCONFIG_${LIBNAME} QUIET lib${LIBNAME}) + + find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h + ${FFmpeg_PKGCONFIG_${LIBNAME}_INCLUDE_DIRS} + ) + + find_library(FFmpeg_LIBRARY_${LIBNAME} ${LIBNAME} + ${FFmpeg_PKGCONFIG_${LIBNAME}_LIBRARY_DIRS} + ) + endif() + + if(FFmpeg_INCLUDE_${LIBNAME} AND FFmpeg_LIBRARY_${LIBNAME}) + set(FFmpeg_INCLUDE_${LIBNAME} "${FFmpeg_INCLUDE_${LIBNAME}}" PARENT_SCOPE) + set(FFmpeg_LIBRARY_${LIBNAME} "${FFmpeg_LIBRARY_${LIBNAME}}" PARENT_SCOPE) + + # Extract FFmpeg version from version.h + foreach(v MAJOR MINOR MICRO) + set(FFmpeg_${LIBNAME}_VERSION_${v} 0) + endforeach() + string(TOUPPER ${LIBNAME} LIBNAME_UPPER) + file(STRINGS "${FFmpeg_INCLUDE_${LIBNAME}}/lib${LIBNAME}/version.h" _FFmpeg_VERSION_H_CONTENTS REGEX "#define LIB${LIBNAME_UPPER}_VERSION_(MAJOR|MINOR|MICRO) ") + set(_FFmpeg_VERSION_REGEX "([0-9]+)") + foreach(v MAJOR MINOR MICRO) + if("${_FFmpeg_VERSION_H_CONTENTS}" MATCHES "#define LIB${LIBNAME_UPPER}_VERSION_${v}[\\t ]+${_FFmpeg_VERSION_REGEX}") + set(FFmpeg_${LIBNAME}_VERSION_${v} "${CMAKE_MATCH_1}") + endif() + endforeach() + set(FFmpeg_${LIBNAME}_VERSION "${FFmpeg_${LIBNAME}_VERSION_MAJOR}.${FFmpeg_${LIBNAME}_VERSION_MINOR}.${FFmpeg_${LIBNAME}_VERSION_MICRO}") + set(FFmpeg_${c}_VERSION "${FFmpeg_${LIBNAME}_VERSION}" PARENT_SCOPE) + unset(_FFmpeg_VERSION_REGEX) + unset(_FFmpeg_VERSION_H_CONTENTS) + + set(FFmpeg_${c}_FOUND TRUE PARENT_SCOPE) + if(NOT FFmpeg_FIND_QUIETLY) + message("-- Found ${LIBNAME}: ${FFmpeg_INCLUDE_${LIBNAME}} ${FFmpeg_LIBRARY_${LIBNAME}} (version: ${FFmpeg_${LIBNAME}_VERSION})") + endif() + endif() +endfunction() + +foreach(c ${_FFmpeg_ALL_COMPONENTS}) + find_ffmpeg(${c}) +endforeach() + +foreach(c ${_FFmpeg_ALL_COMPONENTS}) + if(FFmpeg_${c}_FOUND) + list(APPEND FFmpeg_INCLUDES ${FFmpeg_INCLUDE_${c}}) + list(APPEND FFmpeg_LIBRARIES ${FFmpeg_LIBRARY_${c}}) + + add_library(FFmpeg::${c} IMPORTED UNKNOWN) + set_target_properties(FFmpeg::${c} PROPERTIES + IMPORTED_LOCATION ${FFmpeg_LIBRARY_${c}} + INTERFACE_INCLUDE_DIRECTORIES ${FFmpeg_INCLUDE_${c}} + ) + if(_FFmpeg_DEPS_${c}) + set(deps) + foreach(dep ${_FFmpeg_DEPS_${c}}) + list(APPEND deps FFmpeg::${dep}) + endforeach() + + set_target_properties(FFmpeg::${c} PROPERTIES + INTERFACE_LINK_LIBRARIES "${deps}" + ) + unset(deps) + endif() + endif() +endforeach() + +if(FFmpeg_INCLUDES) + list(REMOVE_DUPLICATES FFmpeg_INCLUDES) +endif() + +foreach(c ${FFmpeg_FIND_COMPONENTS}) + list(APPEND _FFmpeg_REQUIRED_VARS FFmpeg_INCLUDE_${c} FFmpeg_LIBRARY_${c}) +endforeach() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFmpeg + REQUIRED_VARS ${_FFmpeg_REQUIRED_VARS} + HANDLE_COMPONENTS +) + +foreach(c ${_FFmpeg_ALL_COMPONENTS}) + unset(_FFmpeg_DEPS_${c}) +endforeach() +unset(_FFmpeg_ALL_COMPONENTS) +unset(_FFmpeg_REQUIRED_VARS) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 538cfd894..0fcb87859 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -5,6 +5,8 @@ add_library(audio_core STATIC dsp_interface.cpp dsp_interface.h hle/common.h + hle/decoder.cpp + hle/decoder.h hle/filter.cpp hle/filter.h hle/hle.cpp @@ -27,6 +29,7 @@ add_library(audio_core STATIC $<$:sdl2_sink.cpp sdl2_sink.h> $<$:cubeb_sink.cpp cubeb_sink.h> + $<$:hle/aac_decoder.cpp hle/aac_decoder.h hle/ffmpeg_dl.h> ) create_target_directory_groups(audio_core) @@ -43,3 +46,13 @@ if(ENABLE_CUBEB) target_link_libraries(audio_core PRIVATE cubeb) add_definitions(-DHAVE_CUBEB=1) endif() + +if(FFMPEG_FOUND) + if(UNIX) + target_link_libraries(audio_core PRIVATE FFmpeg::avcodec) + else() + target_include_directories(audio_core PRIVATE ${FFMPEG_DIR}/include) + endif() + target_compile_definitions(audio_core PRIVATE HAVE_FFMPEG) +endif() + diff --git a/src/audio_core/hle/aac_decoder.cpp b/src/audio_core/hle/aac_decoder.cpp new file mode 100644 index 000000000..1e8c56163 --- /dev/null +++ b/src/audio_core/hle/aac_decoder.cpp @@ -0,0 +1,241 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "audio_core/hle/aac_decoder.h" +#include "audio_core/hle/ffmpeg_dl.h" + +namespace AudioCore::HLE { + +class AACDecoder::Impl { +public: + Impl(Memory::MemorySystem& memory); + ~Impl(); + std::optional ProcessRequest(const BinaryRequest& request); + +private: + std::optional Initalize(const BinaryRequest& request); + + void Clear(); + + std::optional Decode(const BinaryRequest& request); + + bool initalized; + bool have_ffmpeg_dl; + + Memory::MemorySystem& memory; + + AVCodec* codec; + AVCodecContext* av_context = nullptr; + AVCodecParserContext* parser = nullptr; + AVPacket* av_packet; + AVFrame* decoded_frame = nullptr; +}; + +AACDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { + initalized = false; + + have_ffmpeg_dl = InitFFmpegDL(); +} + +AACDecoder::Impl::~Impl() { + if (initalized) + Clear(); +} + +std::optional AACDecoder::Impl::ProcessRequest(const BinaryRequest& request) { + if (request.codec != DecoderCodec::AAC) { + LOG_ERROR(Audio_DSP, "Got wrong codec {}", static_cast(request.codec)); + return {}; + } + + switch (request.cmd) { + case DecoderCommand::Init: { + return Initalize(request); + break; + } + case DecoderCommand::Decode: { + return Decode(request); + break; + } + case DecoderCommand::Unknown: { + BinaryResponse response; + std::memcpy(&response, &request, sizeof(response)); + response.unknown1 = 0x0; + return response; + break; + } + default: + LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", static_cast(request.cmd)); + return {}; + break; + } +} + +std::optional AACDecoder::Impl::Initalize(const BinaryRequest& request) { + if (initalized) { + Clear(); + } + + if (!have_ffmpeg_dl) { + return {}; + } + + av_packet = av_packet_alloc_dl(); + + codec = avcodec_find_decoder_dl(AV_CODEC_ID_AAC); + if (!codec) { + LOG_ERROR(Audio_DSP, "Codec not found\n"); + return {}; + } + + parser = av_parser_init_dl(codec->id); + if (!parser) { + LOG_ERROR(Audio_DSP, "Parser not found\n"); + return {}; + } + + av_context = avcodec_alloc_context3_dl(codec); + if (!av_context) { + LOG_ERROR(Audio_DSP, "Could not allocate audio codec context\n"); + return {}; + } + + if (avcodec_open2_dl(av_context, codec, NULL) < 0) { + LOG_ERROR(Audio_DSP, "Could not open codec\n"); + return {}; + } + + initalized = true; + + BinaryResponse response; + std::memcpy(&response, &request, sizeof(response)); + response.unknown1 = 0x0; + return response; +} + +void AACDecoder::Impl::Clear() { + if (!have_ffmpeg_dl) { + return; + } + + avcodec_free_context_dl(&av_context); + av_parser_close_dl(parser); + av_frame_free_dl(&decoded_frame); + av_packet_free_dl(&av_packet); +} + +std::optional AACDecoder::Impl::Decode(const BinaryRequest& request) { + if (!initalized) { + LOG_DEBUG(Audio_DSP, "Decoder not initalized"); + + // This is a hack to continue games that are not compiled with the aac codec + BinaryResponse response; + response.codec = request.codec; + response.cmd = request.cmd; + response.num_channels = 2; + response.num_samples = 1024; + response.size = request.size; + return response; + } + + if (request.src_addr < Memory::FCRAM_PADDR) { + LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", request.src_addr); + return {}; + } + u8* data = memory.GetFCRAMPointer(request.src_addr - Memory::FCRAM_PADDR); + + std::array, 2> out_streams; + + std::size_t data_size = request.size; + while (data_size > 0) { + if (!decoded_frame) { + if (!(decoded_frame = av_frame_alloc_dl())) { + LOG_ERROR(Audio_DSP, "Could not allocate audio frame"); + return {}; + } + } + + int ret = av_parser_parse2_dl(parser, av_context, &av_packet->data, &av_packet->size, data, + 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 = avcodec_send_packet_dl(av_context, av_packet); + if (ret < 0) { + LOG_ERROR(Audio_DSP, "Error submitting the packet to the decoder"); + return {}; + } + + if (av_packet->size) { + while (ret >= 0) { + ret = avcodec_receive_frame_dl(av_context, decoded_frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + break; + else if (ret < 0) { + LOG_ERROR(Audio_DSP, "Error during decoding"); + return {}; + } + int bytes_per_sample = av_get_bytes_per_sample_dl(av_context->sample_fmt); + if (bytes_per_sample < 0) { + LOG_ERROR(Audio_DSP, "Failed to calculate data size"); + return {}; + } + + ASSERT(decoded_frame->channels == out_streams.size()); + + std::size_t size = bytes_per_sample * (decoded_frame->nb_samples); + + // FFmpeg converts to 32 signed floating point PCM, we need s32 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 < out_streams.size(); channel++) { + std::memcpy(&val_float, decoded_frame->data[0] + current_pos, + sizeof(val_float)); + s16 val = static_cast(0x7FFF * val_float); + out_streams[channel].push_back(val & 0xFF); + out_streams[channel].push_back(val >> 8); + } + current_pos += sizeof(val_float); + } + } + } + } + + if (request.dst_addr_ch0 < Memory::FCRAM_PADDR) { + LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", request.dst_addr_ch0); + return {}; + } + std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch0 - Memory::FCRAM_PADDR), + out_streams[0].data(), out_streams[0].size()); + + if (request.dst_addr_ch1 < Memory::FCRAM_PADDR) { + LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", request.dst_addr_ch1); + return {}; + } + std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch1 - Memory::FCRAM_PADDR), + out_streams[1].data(), out_streams[1].size()); + + BinaryResponse response; + response.codec = request.codec; + response.cmd = request.cmd; + response.num_channels = 2; + response.num_samples = decoded_frame->nb_samples; + response.size = request.size; + return response; +} + +AACDecoder::AACDecoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} + +AACDecoder::~AACDecoder() = default; + +std::optional AACDecoder::ProcessRequest(const BinaryRequest& request) { + return impl->ProcessRequest(request); +} + +} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/aac_decoder.h b/src/audio_core/hle/aac_decoder.h new file mode 100644 index 000000000..96e47b61b --- /dev/null +++ b/src/audio_core/hle/aac_decoder.h @@ -0,0 +1,22 @@ +// 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 AACDecoder final : public DecoderBase { +public: + AACDecoder(Memory::MemorySystem& memory); + ~AACDecoder() override; + std::optional ProcessRequest(const BinaryRequest& request) override; + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/decoder.cpp b/src/audio_core/hle/decoder.cpp new file mode 100644 index 000000000..4832aab73 --- /dev/null +++ b/src/audio_core/hle/decoder.cpp @@ -0,0 +1,36 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "audio_core/hle/decoder.h" + +namespace AudioCore::HLE { + +NullDecoder::NullDecoder() {} + +NullDecoder::~NullDecoder() = default; + +std::optional NullDecoder::ProcessRequest(const BinaryRequest& request) { + BinaryResponse response; + switch (request.cmd) { + case DecoderCommand::Init: + case DecoderCommand::Unknown: + std::memcpy(&response, &request, sizeof(response)); + response.unknown1 = 0x0; + return response; + break; + case DecoderCommand::Decode: + response.codec = request.codec; + response.cmd = DecoderCommand::Decode; + response.num_channels = 2; // Just assume stereo here + response.size = request.size; + response.num_samples = 1024; // Just assume 1024 here + return response; + break; + default: + LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", static_cast(request.cmd)); + return {}; + break; + } +}; +} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/decoder.h b/src/audio_core/hle/decoder.h new file mode 100644 index 000000000..fc2859760 --- /dev/null +++ b/src/audio_core/hle/decoder.h @@ -0,0 +1,68 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "common/common_types.h" +#include "common/swap.h" +#include "core/core.h" + +namespace AudioCore::HLE { + +enum class DecoderCommand : u16 { + Init, + Decode, + Unknown, +}; + +enum class DecoderCodec : u16 { + None, + AAC, +}; + +struct BinaryRequest { + enum_le codec = + DecoderCodec::None; // this is a guess. until now only 0x1 was observed here + enum_le cmd = DecoderCommand::Init; + u32_le fixed = 0; + u32_le src_addr = 0; + u32_le size = 0; + u32_le dst_addr_ch0 = 0; + u32_le dst_addr_ch1 = 0; + u32_le unknown1 = 0; + u32_le unknown2 = 0; +}; +static_assert(sizeof(BinaryRequest) == 32, "Unexpected struct size for BinaryRequest"); + +struct BinaryResponse { + enum_le codec = + DecoderCodec::None; // this could be something else. until now only 0x1 was observed here + enum_le cmd = DecoderCommand::Init; + u32_le unknown1 = 0; + u32_le unknown2 = 0; + u32_le num_channels = 0; // this is a guess, so far I only observed 2 here + u32_le size = 0; + u32_le unknown3 = 0; + u32_le unknown4 = 0; + u32_le num_samples = 0; // this is a guess, so far I only observed 1024 here +}; +static_assert(sizeof(BinaryResponse) == 32, "Unexpected struct size for BinaryResponse"); + +class DecoderBase { +public: + virtual ~DecoderBase(){}; + virtual std::optional ProcessRequest(const BinaryRequest& request) = 0; +}; + +class NullDecoder final : public DecoderBase { +public: + NullDecoder(); + ~NullDecoder() override; + std::optional ProcessRequest(const BinaryRequest& request) override; +}; + +} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/ffmpeg_dl.h b/src/audio_core/hle/ffmpeg_dl.h new file mode 100644 index 000000000..26aad1d96 --- /dev/null +++ b/src/audio_core/hle/ffmpeg_dl.h @@ -0,0 +1,213 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#ifdef _WIN32 +#include +#endif + +#include "common/logging/log.h" + +extern "C" { +#include "libavcodec/avcodec.h" +} + +#ifdef _WIN32 + +template +struct FuncDL { + FuncDL() = default; + FuncDL(HMODULE dll, const char* name) { + ptr_function = nullptr; + if (dll) { + *(void**)&ptr_function = (void*)GetProcAddress(dll, name); + } + } + + operator T*() const { + return ptr_function; + } + + operator bool() const { + return ptr_function != nullptr; + } + + T* ptr_function = nullptr; + ; +}; + +FuncDL av_get_bytes_per_sample_dl; +FuncDL av_frame_alloc_dl; +FuncDL av_frame_free_dl; +FuncDL avcodec_alloc_context3_dl; +FuncDL avcodec_free_context_dl; +FuncDL avcodec_open2_dl; +FuncDL av_packet_alloc_dl; +FuncDL av_packet_free_dl; +FuncDL avcodec_find_decoder_dl; +FuncDL avcodec_send_packet_dl; +FuncDL avcodec_receive_frame_dl; +FuncDL av_parser_init_dl; +FuncDL + av_parser_parse2_dl; +FuncDL av_parser_close_dl; + +bool InitFFmpegDL() { + HMODULE dll_util = nullptr; + dll_util = LoadLibrary("avutil-56.dll"); + if (!dll_util) { + DWORD errorMessageID = GetLastError(); + LPSTR messageBuffer = nullptr; + size_t size = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + + // Free the buffer. + LocalFree(messageBuffer); + LOG_ERROR(Audio_DSP, "Could not load avcodec-58.dll: {}", message); + return false; + } + + HMODULE dll_codec = nullptr; + dll_codec = LoadLibrary("avcodec-58.dll"); + if (!dll_codec) { + DWORD errorMessageID = GetLastError(); + LPSTR messageBuffer = nullptr; + size_t size = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + + // Free the buffer. + LocalFree(messageBuffer); + LOG_ERROR(Audio_DSP, "Could not load avcodec-58.dll: {}", message); + return false; + } + av_get_bytes_per_sample_dl = FuncDL(dll_util, "av_get_bytes_per_sample"); + if (!av_get_bytes_per_sample_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_get_bytes_per_sample"); + return false; + } + + av_frame_alloc_dl = FuncDL(dll_util, "av_frame_alloc"); + if (!av_frame_alloc_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_frame_alloc"); + return false; + } + + av_frame_free_dl = FuncDL(dll_util, "av_frame_free"); + if (!av_frame_free_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_frame_free"); + return false; + } + + avcodec_alloc_context3_dl = + FuncDL(dll_codec, "avcodec_alloc_context3"); + if (!avcodec_alloc_context3_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_alloc_context3"); + return false; + } + + avcodec_free_context_dl = FuncDL(dll_codec, "avcodec_free_context"); + if (!av_get_bytes_per_sample_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_free_context"); + return false; + } + + avcodec_open2_dl = + FuncDL(dll_codec, "avcodec_open2"); + if (!avcodec_open2_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_open2"); + return false; + } + av_packet_alloc_dl = FuncDL(dll_codec, "av_packet_alloc"); + if (!av_packet_alloc_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_packet_alloc"); + return false; + } + + av_packet_free_dl = FuncDL(dll_codec, "av_packet_free"); + if (!av_packet_free_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_packet_free"); + return false; + } + + avcodec_find_decoder_dl = FuncDL(dll_codec, "avcodec_find_decoder"); + if (!avcodec_find_decoder_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_find_decoder"); + return false; + } + + avcodec_send_packet_dl = + FuncDL(dll_codec, "avcodec_send_packet"); + if (!avcodec_send_packet_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_send_packet"); + return false; + } + + avcodec_receive_frame_dl = + FuncDL(dll_codec, "avcodec_receive_frame"); + if (!avcodec_receive_frame_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_receive_frame"); + return false; + } + + av_parser_init_dl = FuncDL(dll_codec, "av_parser_init"); + if (!av_parser_init_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_parser_init"); + return false; + } + + av_parser_parse2_dl = + FuncDL(dll_codec, "av_parser_parse2"); + if (!av_parser_parse2_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_parser_parse2"); + return false; + } + + av_parser_close_dl = FuncDL(dll_codec, "av_parser_close"); + if (!av_parser_close_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_parser_close"); + return false; + } + + return true; +} + +#endif // _Win32 + +#if defined(__APPLE__) || defined(__linux__) + +// No dynamic loading for Unix and Apple + +const auto av_get_bytes_per_sample_dl = &av_get_bytes_per_sample; +const auto av_frame_alloc_dl = &av_frame_alloc; +const auto av_frame_free_dl = &av_frame_free; +const auto avcodec_alloc_context3_dl = &avcodec_alloc_context3; +const auto avcodec_free_context_dl = &avcodec_free_context; +const auto avcodec_open2_dl = &avcodec_open2; +const auto av_packet_alloc_dl = &av_packet_alloc; +const auto av_packet_free_dl = &av_packet_free; +const auto avcodec_find_decoder_dl = &avcodec_find_decoder; +const auto avcodec_send_packet_dl = &avcodec_send_packet; +const auto avcodec_receive_frame_dl = &avcodec_receive_frame; +const auto av_parser_init_dl = &av_parser_init; +const auto av_parser_parse2_dl = &av_parser_parse2; +const auto av_parser_close_dl = &av_parser_close; + +bool InitFFmpegDL() { + return true; +} + +#endif // defined(__APPLE__) || defined(__linux__) diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index 6e7b45278..b2a9873a7 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -3,7 +3,11 @@ // Refer to the license.txt file included. #include "audio_core/audio_types.h" +#ifdef HAVE_FFMPEG +#include "audio_core/hle/aac_decoder.h" +#endif #include "audio_core/hle/common.h" +#include "audio_core/hle/decoder.h" #include "audio_core/hle/hle.h" #include "audio_core/hle/mixers.h" #include "audio_core/hle/shared_memory.h" @@ -69,6 +73,8 @@ private: DspHle& parent; Core::TimingEventType* tick_event; + std::unique_ptr decoder; + std::weak_ptr dsp_dsp; }; @@ -79,6 +85,13 @@ DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory) : parent(paren source.SetMemory(memory); } +#ifdef HAVE_FFMPEG + decoder = std::make_unique(memory); +#else + LOG_WARNING(Audio_DSP, "FFmpeg missing, this could lead to missing audio"); + decoder = std::make_unique(); +#endif // HAVE_FFMPEG + Core::Timing& timing = Core::System::GetInstance().CoreTiming(); tick_event = timing.RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) { @@ -215,6 +228,28 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector& buffer) return; } + case DspPipe::Binary: { + // TODO(B3N30): Make this async, and signal the interrupt + HLE::BinaryRequest request; + if (sizeof(request) != buffer.size()) { + LOG_CRITICAL(Audio_DSP, "got binary pipe with wrong size {}", buffer.size()); + UNIMPLEMENTED(); + return; + } + std::memcpy(&request, buffer.data(), buffer.size()); + if (request.codec != HLE::DecoderCodec::AAC) { + LOG_CRITICAL(Audio_DSP, "got unknown codec {}", static_cast(request.codec)); + UNIMPLEMENTED(); + return; + } + std::optional response = decoder->ProcessRequest(request); + if (response) { + const HLE::BinaryResponse& value = *response; + pipe_data[static_cast(pipe_number)].resize(sizeof(value)); + std::memcpy(pipe_data[static_cast(pipe_number)].data(), &value, sizeof(value)); + } + break; + } default: LOG_CRITICAL(Audio_DSP, "pipe_number = {} unimplemented", static_cast(pipe_number)); From 45b5de7a182385682ac581531355d602d2dcdec4 Mon Sep 17 00:00:00 2001 From: B3N30 Date: Sat, 15 Dec 2018 15:06:54 +0100 Subject: [PATCH 02/34] Fixup: Merge conflict with dsp lle --- src/audio_core/CMakeLists.txt | 18 +++++++++--------- src/audio_core/hle/ffmpeg_dl.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 0fcb87859..a0edc355d 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -37,6 +37,15 @@ create_target_directory_groups(audio_core) target_link_libraries(audio_core PUBLIC common core) target_link_libraries(audio_core PRIVATE SoundTouch teakra) +if(FFMPEG_FOUND) + if(UNIX) + target_link_libraries(audio_core PRIVATE FFmpeg::avcodec) + else() + target_include_directories(audio_core PRIVATE ${FFMPEG_DIR}/include) + endif() + target_compile_definitions(audio_core PRIVATE HAVE_FFMPEG) +endif() + if(SDL2_FOUND) target_link_libraries(audio_core PRIVATE SDL2) target_compile_definitions(audio_core PRIVATE HAVE_SDL2) @@ -47,12 +56,3 @@ if(ENABLE_CUBEB) add_definitions(-DHAVE_CUBEB=1) endif() -if(FFMPEG_FOUND) - if(UNIX) - target_link_libraries(audio_core PRIVATE FFmpeg::avcodec) - else() - target_include_directories(audio_core PRIVATE ${FFMPEG_DIR}/include) - endif() - target_compile_definitions(audio_core PRIVATE HAVE_FFMPEG) -endif() - diff --git a/src/audio_core/hle/ffmpeg_dl.h b/src/audio_core/hle/ffmpeg_dl.h index 26aad1d96..df787f160 100644 --- a/src/audio_core/hle/ffmpeg_dl.h +++ b/src/audio_core/hle/ffmpeg_dl.h @@ -6,7 +6,7 @@ #ifdef _WIN32 #include -#endif +#endif // _WIN32 #include "common/logging/log.h" @@ -71,7 +71,7 @@ bool InitFFmpegDL() { // Free the buffer. LocalFree(messageBuffer); - LOG_ERROR(Audio_DSP, "Could not load avcodec-58.dll: {}", message); + LOG_ERROR(Audio_DSP, "Could not load avutil-56.dll: {}", message); return false; } From 847003cc1c2de0802608eebae6774fedee041b8c Mon Sep 17 00:00:00 2001 From: B3N30 Date: Sat, 15 Dec 2018 21:33:33 +0100 Subject: [PATCH 03/34] FFmpegDL: Added external_dlls folder to user folder AACDecoder: addressed reviews --- src/audio_core/hle/aac_decoder.cpp | 31 ++++++++++++++++-------------- src/audio_core/hle/ffmpeg_dl.h | 11 +++++++---- src/common/common_paths.h | 1 + src/common/file_util.cpp | 1 + src/common/file_util.h | 1 + 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/audio_core/hle/aac_decoder.cpp b/src/audio_core/hle/aac_decoder.cpp index 1e8c56163..db8a09065 100644 --- a/src/audio_core/hle/aac_decoder.cpp +++ b/src/audio_core/hle/aac_decoder.cpp @@ -9,7 +9,7 @@ namespace AudioCore::HLE { class AACDecoder::Impl { public: - Impl(Memory::MemorySystem& memory); + explicit Impl(Memory::MemorySystem& memory); ~Impl(); std::optional ProcessRequest(const BinaryRequest& request); @@ -77,8 +77,12 @@ std::optional AACDecoder::Impl::Initalize(const BinaryRequest& r Clear(); } + BinaryResponse response; + std::memcpy(&response, &request, sizeof(response)); + response.unknown1 = 0x0; + if (!have_ffmpeg_dl) { - return {}; + return response; } av_packet = av_packet_alloc_dl(); @@ -86,31 +90,27 @@ std::optional AACDecoder::Impl::Initalize(const BinaryRequest& r codec = avcodec_find_decoder_dl(AV_CODEC_ID_AAC); if (!codec) { LOG_ERROR(Audio_DSP, "Codec not found\n"); - return {}; + return response; } parser = av_parser_init_dl(codec->id); if (!parser) { LOG_ERROR(Audio_DSP, "Parser not found\n"); - return {}; + return response; } av_context = avcodec_alloc_context3_dl(codec); if (!av_context) { LOG_ERROR(Audio_DSP, "Could not allocate audio codec context\n"); - return {}; + return response; } if (avcodec_open2_dl(av_context, codec, NULL) < 0) { LOG_ERROR(Audio_DSP, "Could not open codec\n"); - return {}; + return response; } initalized = true; - - BinaryResponse response; - std::memcpy(&response, &request, sizeof(response)); - response.unknown1 = 0x0; return response; } @@ -139,7 +139,8 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ return response; } - if (request.src_addr < Memory::FCRAM_PADDR) { + if (request.src_addr < Memory::FCRAM_PADDR || + request.src_addr + request.size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", request.src_addr); return {}; } @@ -190,7 +191,7 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ std::size_t size = bytes_per_sample * (decoded_frame->nb_samples); - // FFmpeg converts to 32 signed floating point PCM, we need s32 PCM so we need to + // 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;) { @@ -207,14 +208,16 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ } } - if (request.dst_addr_ch0 < Memory::FCRAM_PADDR) { + if (request.dst_addr_ch0 < Memory::FCRAM_PADDR || + 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.dst_addr_ch0); return {}; } std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch0 - Memory::FCRAM_PADDR), out_streams[0].data(), out_streams[0].size()); - if (request.dst_addr_ch1 < Memory::FCRAM_PADDR) { + if (request.dst_addr_ch1 < Memory::FCRAM_PADDR || + 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.dst_addr_ch1); return {}; } diff --git a/src/audio_core/hle/ffmpeg_dl.h b/src/audio_core/hle/ffmpeg_dl.h index df787f160..61af403b9 100644 --- a/src/audio_core/hle/ffmpeg_dl.h +++ b/src/audio_core/hle/ffmpeg_dl.h @@ -6,8 +6,9 @@ #ifdef _WIN32 #include -#endif // _WIN32 +#endif // _WIN32 +#include "common/file_util.h" #include "common/logging/log.h" extern "C" { @@ -20,9 +21,8 @@ template struct FuncDL { FuncDL() = default; FuncDL(HMODULE dll, const char* name) { - ptr_function = nullptr; if (dll) { - *(void**)&ptr_function = (void*)GetProcAddress(dll, name); + ptr_function = reinterpret_cast(GetProcAddress(dll, name)); } } @@ -35,7 +35,6 @@ struct FuncDL { } T* ptr_function = nullptr; - ; }; FuncDL av_get_bytes_per_sample_dl; @@ -56,6 +55,10 @@ FuncDL av_parser_close_dl; bool InitFFmpegDL() { + + FileUtil::CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::DLLDir)); + SetDllDirectoryA(FileUtil::GetUserPath(FileUtil::UserPath::DLLDir).c_str()); + HMODULE dll_util = nullptr; dll_util = LoadLibrary("avutil-56.dll"); if (!dll_util) { diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 908225ac8..4942c02ab 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -38,6 +38,7 @@ #define SYSDATA_DIR "sysdata" #define LOG_DIR "log" #define CHEATS_DIR "cheats" +#define DLL_DIR "external_dlls" // Filenames // Files in the directory returned by GetUserPath(UserPath::LogDir) diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 4b95174d6..0ae9e2c5f 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -711,6 +711,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { // TODO: Put the logs in a better location for each OS paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP); paths.emplace(UserPath::CheatsDir, user_path + CHEATS_DIR DIR_SEP); + paths.emplace(UserPath::DLLDir, user_path + DLL_DIR DIR_SEP); } if (!new_path.empty()) { diff --git a/src/common/file_util.h b/src/common/file_util.h index 96a30675c..d30780b97 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -24,6 +24,7 @@ enum class UserPath { CacheDir, CheatsDir, ConfigDir, + DLLDir, LogDir, NANDDir, RootDir, From bd9984b5f838a899b035398f7414576f6d98659f Mon Sep 17 00:00:00 2001 From: B3N30 Date: Sun, 16 Dec 2018 15:03:23 +0100 Subject: [PATCH 04/34] Addressed reviews --- .travis/linux-frozen/docker.sh | 2 +- .travis/linux-mingw/docker.sh | 2 +- .travis/linux/docker.sh | 2 +- .travis/macos/build.sh | 2 +- .travis/transifex/docker.sh | 2 +- CMakeLists.txt | 2 +- appveyor.yml | 4 +- src/audio_core/CMakeLists.txt | 2 +- src/audio_core/hle/aac_decoder.cpp | 129 ++++++++++++--------- src/audio_core/hle/aac_decoder.h | 2 +- src/audio_core/hle/decoder.cpp | 4 +- src/audio_core/hle/decoder.h | 2 +- src/audio_core/hle/ffmpeg_dl.cpp | 173 ++++++++++++++++++++++++++++ src/audio_core/hle/ffmpeg_dl.h | 179 ++++------------------------- 14 files changed, 282 insertions(+), 225 deletions(-) create mode 100644 src/audio_core/hle/ffmpeg_dl.cpp diff --git a/.travis/linux-frozen/docker.sh b/.travis/linux-frozen/docker.sh index 0c4e72b4f..a4814da1a 100755 --- a/.travis/linux-frozen/docker.sh +++ b/.travis/linux-frozen/docker.sh @@ -3,7 +3,7 @@ cd /citra mkdir build && cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=OFF +cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON make -j4 ctest -VV -C Release diff --git a/.travis/linux-mingw/docker.sh b/.travis/linux-mingw/docker.sh index 2514a5058..4a0e4304b 100755 --- a/.travis/linux-mingw/docker.sh +++ b/.travis/linux-mingw/docker.sh @@ -5,7 +5,7 @@ cd /citra echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf" mkdir build && cd build -cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON +cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=ON make -j4 echo "Tests skipped" diff --git a/.travis/linux/docker.sh b/.travis/linux/docker.sh index 64d8b9417..2a583d6a5 100755 --- a/.travis/linux/docker.sh +++ b/.travis/linux/docker.sh @@ -3,7 +3,7 @@ cd /citra mkdir build && cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=OFF +cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON make -j4 ctest -VV -C Release diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh index bb3c687e6..7a4b32e4f 100755 --- a/.travis/macos/build.sh +++ b/.travis/macos/build.sh @@ -7,7 +7,7 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5 export PATH="/usr/local/opt/ccache/libexec:$PATH" mkdir build && cd build -cmake .. -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON +cmake .. -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=ON make -j4 ctest -VV -C Release diff --git a/.travis/transifex/docker.sh b/.travis/transifex/docker.sh index f608f3848..003b298b6 100644 --- a/.travis/transifex/docker.sh +++ b/.travis/transifex/docker.sh @@ -26,7 +26,7 @@ tx --version cd /citra mkdir build && cd build -cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF -DENABLE_FFMPEG=OFF +cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF make translation cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d80b97b1..5e72baefe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) -option(ENABLE_FFMPEG "Enable FFmpeg decoder/encoder" ON) +option(ENABLE_FFMPEG "Enable FFmpeg decoder/encoder" OFF) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) diff --git a/appveyor.yml b/appveyor.yml index fcb824d74..3c8646a88 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,9 +43,9 @@ before_build: $COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING} if ($env:BUILD_TYPE -eq 'msvc') { # redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning - cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0' + cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=ON .. 2>&1 && exit 0' } else { - C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON .. 2>&1" + C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=ON .. 2>&1" } - cd .. diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index a0edc355d..d00d23d98 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -29,7 +29,7 @@ add_library(audio_core STATIC $<$:sdl2_sink.cpp sdl2_sink.h> $<$:cubeb_sink.cpp cubeb_sink.h> - $<$:hle/aac_decoder.cpp hle/aac_decoder.h hle/ffmpeg_dl.h> + $<$:hle/aac_decoder.cpp hle/aac_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h> ) create_target_directory_groups(audio_core) diff --git a/src/audio_core/hle/aac_decoder.cpp b/src/audio_core/hle/aac_decoder.cpp index db8a09065..774d1a8ee 100644 --- a/src/audio_core/hle/aac_decoder.cpp +++ b/src/audio_core/hle/aac_decoder.cpp @@ -20,28 +20,47 @@ private: std::optional Decode(const BinaryRequest& request); - bool initalized; + struct AVPacketDeleter { + void operator()(AVPacket* packet) const { + av_packet_free_dl(&packet); + } + }; + + struct AVCodecContextDeleter { + void operator()(AVCodecContext* context) const { + avcodec_free_context_dl(&context); + } + }; + + struct AVCodecParserContextDeleter { + void operator()(AVCodecParserContext* parser) const { + av_parser_close_dl(parser); + } + }; + + struct AVFrameDeleter { + void operator()(AVFrame* frame) const { + av_frame_free_dl(&frame); + } + }; + + bool initalized = false; bool have_ffmpeg_dl; Memory::MemorySystem& memory; AVCodec* codec; - AVCodecContext* av_context = nullptr; - AVCodecParserContext* parser = nullptr; - AVPacket* av_packet; - AVFrame* decoded_frame = nullptr; + std::unique_ptr av_context; + std::unique_ptr parser; + std::unique_ptr av_packet; + std::unique_ptr decoded_frame; }; AACDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { - initalized = false; - have_ffmpeg_dl = InitFFmpegDL(); } -AACDecoder::Impl::~Impl() { - if (initalized) - Clear(); -} +AACDecoder::Impl::~Impl() = default; std::optional AACDecoder::Impl::ProcessRequest(const BinaryRequest& request) { if (request.codec != DecoderCodec::AAC) { @@ -52,23 +71,19 @@ std::optional AACDecoder::Impl::ProcessRequest(const BinaryReque switch (request.cmd) { case DecoderCommand::Init: { return Initalize(request); - break; } case DecoderCommand::Decode: { return Decode(request); - break; } case DecoderCommand::Unknown: { BinaryResponse response; std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; return response; - break; } default: LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", static_cast(request.cmd)); return {}; - break; } } @@ -85,7 +100,7 @@ std::optional AACDecoder::Impl::Initalize(const BinaryRequest& r return response; } - av_packet = av_packet_alloc_dl(); + av_packet.reset(av_packet_alloc_dl()); codec = avcodec_find_decoder_dl(AV_CODEC_ID_AAC); if (!codec) { @@ -93,19 +108,19 @@ std::optional AACDecoder::Impl::Initalize(const BinaryRequest& r return response; } - parser = av_parser_init_dl(codec->id); + parser.reset(av_parser_init_dl(codec->id)); if (!parser) { LOG_ERROR(Audio_DSP, "Parser not found\n"); return response; } - av_context = avcodec_alloc_context3_dl(codec); + av_context.reset(avcodec_alloc_context3_dl(codec)); if (!av_context) { LOG_ERROR(Audio_DSP, "Could not allocate audio codec context\n"); return response; } - if (avcodec_open2_dl(av_context, codec, NULL) < 0) { + if (avcodec_open2_dl(av_context.get(), codec, nullptr) < 0) { LOG_ERROR(Audio_DSP, "Could not open codec\n"); return response; } @@ -119,23 +134,23 @@ void AACDecoder::Impl::Clear() { return; } - avcodec_free_context_dl(&av_context); - av_parser_close_dl(parser); - av_frame_free_dl(&decoded_frame); - av_packet_free_dl(&av_packet); + av_context.reset(); + parser.reset(); + decoded_frame.reset(); + av_packet.reset(); } std::optional AACDecoder::Impl::Decode(const BinaryRequest& request) { + BinaryResponse response; + response.codec = request.codec; + response.cmd = request.cmd; + response.size = 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 - BinaryResponse response; - response.codec = request.codec; - response.cmd = request.cmd; response.num_channels = 2; response.num_samples = 1024; - response.size = request.size; return response; } @@ -151,14 +166,16 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ std::size_t data_size = request.size; while (data_size > 0) { if (!decoded_frame) { - if (!(decoded_frame = av_frame_alloc_dl())) { + decoded_frame.reset(av_frame_alloc_dl()); + if (!decoded_frame) { LOG_ERROR(Audio_DSP, "Could not allocate audio frame"); return {}; } } - int ret = av_parser_parse2_dl(parser, av_context, &av_packet->data, &av_packet->size, data, - data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); + int ret = + av_parser_parse2_dl(parser.get(), av_context.get(), &av_packet->data, &av_packet->size, + data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (ret < 0) { LOG_ERROR(Audio_DSP, "Error while parsing"); return {}; @@ -166,7 +183,7 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ data += ret; data_size -= ret; - ret = avcodec_send_packet_dl(av_context, av_packet); + ret = avcodec_send_packet_dl(av_context.get(), av_packet.get()); if (ret < 0) { LOG_ERROR(Audio_DSP, "Error submitting the packet to the decoder"); return {}; @@ -174,7 +191,7 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ if (av_packet->size) { while (ret >= 0) { - ret = avcodec_receive_frame_dl(av_context, decoded_frame); + ret = avcodec_receive_frame_dl(av_context.get(), decoded_frame.get()); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) { @@ -187,16 +204,19 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ return {}; } - ASSERT(decoded_frame->channels == out_streams.size()); + ASSERT(decoded_frame->channels <= out_streams.size()); std::size_t size = bytes_per_sample * (decoded_frame->nb_samples); + response.num_channels = decoded_frame->channels; + 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 < out_streams.size(); channel++) { - std::memcpy(&val_float, decoded_frame->data[0] + current_pos, + for (std::size_t channel(0); channel < decoded_frame->channels; channel++) { + std::memcpy(&val_float, decoded_frame->data[channel] + current_pos, sizeof(val_float)); s16 val = static_cast(0x7FFF * val_float); out_streams[channel].push_back(val & 0xFF); @@ -208,28 +228,27 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ } } - if (request.dst_addr_ch0 < Memory::FCRAM_PADDR || - 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.dst_addr_ch0); - return {}; + if (out_streams[0].size() != 0) { + if (request.dst_addr_ch0 < Memory::FCRAM_PADDR || + 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.dst_addr_ch0); + return {}; + } + std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch0 - Memory::FCRAM_PADDR), + out_streams[0].data(), out_streams[0].size()); } - std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch0 - Memory::FCRAM_PADDR), - out_streams[0].data(), out_streams[0].size()); - if (request.dst_addr_ch1 < Memory::FCRAM_PADDR || - 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.dst_addr_ch1); - return {}; + if (out_streams[1].size() != 0) { + if (request.dst_addr_ch1 < Memory::FCRAM_PADDR || + 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.dst_addr_ch1); + return {}; + } + std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch1 - Memory::FCRAM_PADDR), + out_streams[1].data(), out_streams[1].size()); } - std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch1 - Memory::FCRAM_PADDR), - out_streams[1].data(), out_streams[1].size()); - - BinaryResponse response; - response.codec = request.codec; - response.cmd = request.cmd; - response.num_channels = 2; - response.num_samples = decoded_frame->nb_samples; - response.size = request.size; return response; } diff --git a/src/audio_core/hle/aac_decoder.h b/src/audio_core/hle/aac_decoder.h index 96e47b61b..ec941dfb0 100644 --- a/src/audio_core/hle/aac_decoder.h +++ b/src/audio_core/hle/aac_decoder.h @@ -10,7 +10,7 @@ namespace AudioCore::HLE { class AACDecoder final : public DecoderBase { public: - AACDecoder(Memory::MemorySystem& memory); + explicit AACDecoder(Memory::MemorySystem& memory); ~AACDecoder() override; std::optional ProcessRequest(const BinaryRequest& request) override; diff --git a/src/audio_core/hle/decoder.cpp b/src/audio_core/hle/decoder.cpp index 4832aab73..e181e3cde 100644 --- a/src/audio_core/hle/decoder.cpp +++ b/src/audio_core/hle/decoder.cpp @@ -6,7 +6,9 @@ namespace AudioCore::HLE { -NullDecoder::NullDecoder() {} +DecoderBase::~DecoderBase(){}; + +NullDecoder::NullDecoder() = default; NullDecoder::~NullDecoder() = default; diff --git a/src/audio_core/hle/decoder.h b/src/audio_core/hle/decoder.h index fc2859760..a551df4eb 100644 --- a/src/audio_core/hle/decoder.h +++ b/src/audio_core/hle/decoder.h @@ -54,7 +54,7 @@ static_assert(sizeof(BinaryResponse) == 32, "Unexpected struct size for BinaryRe class DecoderBase { public: - virtual ~DecoderBase(){}; + virtual ~DecoderBase(); virtual std::optional ProcessRequest(const BinaryRequest& request) = 0; }; diff --git a/src/audio_core/hle/ffmpeg_dl.cpp b/src/audio_core/hle/ffmpeg_dl.cpp new file mode 100644 index 000000000..0a3b2148c --- /dev/null +++ b/src/audio_core/hle/ffmpeg_dl.cpp @@ -0,0 +1,173 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifdef _WIN32 + +#include +#include "audio_core/hle/ffmpeg_dl.h" +#include "common/file_util.h" +#include "common/logging/log.h" + +struct LibraryDeleter { + typedef HMODULE pointer; + void operator()(HMODULE h) { + if (h != nullptr) + FreeLibrary(h); + } +}; + +std::unique_ptr dll_util{nullptr}; +std::unique_ptr dll_codec{nullptr}; + +FuncDL av_get_bytes_per_sample_dl; +FuncDL av_frame_alloc_dl; +FuncDL av_frame_free_dl; +FuncDL avcodec_alloc_context3_dl; +FuncDL avcodec_free_context_dl; +FuncDL avcodec_open2_dl; +FuncDL av_packet_alloc_dl; +FuncDL av_packet_free_dl; +FuncDL avcodec_find_decoder_dl; +FuncDL avcodec_send_packet_dl; +FuncDL avcodec_receive_frame_dl; +FuncDL av_parser_init_dl; +FuncDL + av_parser_parse2_dl; +FuncDL av_parser_close_dl; + +bool InitFFmpegDL() { + std::string dll_path = FileUtil::GetUserPath(FileUtil::UserPath::DLLDir); + FileUtil::CreateDir(dll_path); + std::wstring w_dll_path = std::wstring(dll_path.begin(), dll_path.end()); + SetDllDirectoryW(w_dll_path.c_str()); + + dll_util.reset(LoadLibrary("avutil-56.dll")); + if (!dll_util) { + 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(&message_buffer), 0, nullptr); + + std::string message(message_buffer, size); + + LocalFree(message_buffer); + LOG_ERROR(Audio_DSP, "Could not load avutil-56.dll: {}", message); + return false; + } + + dll_codec.reset(LoadLibrary("avcodec-58.dll")); + if (!dll_codec) { + 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(&message_buffer), 0, nullptr); + + std::string message(message_buffer, size); + + LocalFree(message_buffer); + LOG_ERROR(Audio_DSP, "Could not load avcodec-58.dll: {}", message); + return false; + } + av_get_bytes_per_sample_dl = + FuncDL(dll_util.get(), "av_get_bytes_per_sample"); + if (!av_get_bytes_per_sample_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_get_bytes_per_sample"); + return false; + } + + av_frame_alloc_dl = FuncDL(dll_util.get(), "av_frame_alloc"); + if (!av_frame_alloc_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_frame_alloc"); + return false; + } + + av_frame_free_dl = FuncDL(dll_util.get(), "av_frame_free"); + if (!av_frame_free_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_frame_free"); + return false; + } + + avcodec_alloc_context3_dl = + FuncDL(dll_codec.get(), "avcodec_alloc_context3"); + if (!avcodec_alloc_context3_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_alloc_context3"); + return false; + } + + avcodec_free_context_dl = + FuncDL(dll_codec.get(), "avcodec_free_context"); + if (!av_get_bytes_per_sample_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_free_context"); + return false; + } + + avcodec_open2_dl = FuncDL( + dll_codec.get(), "avcodec_open2"); + if (!avcodec_open2_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_open2"); + return false; + } + av_packet_alloc_dl = FuncDL(dll_codec.get(), "av_packet_alloc"); + if (!av_packet_alloc_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_packet_alloc"); + return false; + } + + av_packet_free_dl = FuncDL(dll_codec.get(), "av_packet_free"); + if (!av_packet_free_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_packet_free"); + return false; + } + + avcodec_find_decoder_dl = FuncDL(dll_codec.get(), "avcodec_find_decoder"); + if (!avcodec_find_decoder_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_find_decoder"); + return false; + } + + avcodec_send_packet_dl = + FuncDL(dll_codec.get(), "avcodec_send_packet"); + if (!avcodec_send_packet_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_send_packet"); + return false; + } + + avcodec_receive_frame_dl = + FuncDL(dll_codec.get(), "avcodec_receive_frame"); + if (!avcodec_receive_frame_dl) { + LOG_ERROR(Audio_DSP, "Can not load function avcodec_receive_frame"); + return false; + } + + av_parser_init_dl = FuncDL(dll_codec.get(), "av_parser_init"); + if (!av_parser_init_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_parser_init"); + return false; + } + + av_parser_parse2_dl = + FuncDL(dll_codec.get(), "av_parser_parse2"); + if (!av_parser_parse2_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_parser_parse2"); + return false; + } + + av_parser_close_dl = FuncDL(dll_codec.get(), "av_parser_close"); + if (!av_parser_close_dl) { + LOG_ERROR(Audio_DSP, "Can not load function av_parser_close"); + return false; + } + + return true; +} + +#endif // _Win32 diff --git a/src/audio_core/hle/ffmpeg_dl.h b/src/audio_core/hle/ffmpeg_dl.h index 61af403b9..57e150f95 100644 --- a/src/audio_core/hle/ffmpeg_dl.h +++ b/src/audio_core/hle/ffmpeg_dl.h @@ -8,9 +8,6 @@ #include #endif // _WIN32 -#include "common/file_util.h" -#include "common/logging/log.h" - extern "C" { #include "libavcodec/avcodec.h" } @@ -20,9 +17,9 @@ extern "C" { template struct FuncDL { FuncDL() = default; - FuncDL(HMODULE dll, const char* name) { + FuncDL(void* dll, const char* name) { if (dll) { - ptr_function = reinterpret_cast(GetProcAddress(dll, name)); + ptr_function = reinterpret_cast(GetProcAddress((HMODULE)dll, name)); } } @@ -30,167 +27,33 @@ struct FuncDL { return ptr_function; } - operator bool() const { + explicit operator bool() const { return ptr_function != nullptr; } T* ptr_function = nullptr; }; -FuncDL av_get_bytes_per_sample_dl; -FuncDL av_frame_alloc_dl; -FuncDL av_frame_free_dl; -FuncDL avcodec_alloc_context3_dl; -FuncDL avcodec_free_context_dl; -FuncDL avcodec_open2_dl; -FuncDL av_packet_alloc_dl; -FuncDL av_packet_free_dl; -FuncDL avcodec_find_decoder_dl; -FuncDL avcodec_send_packet_dl; -FuncDL avcodec_receive_frame_dl; -FuncDL av_parser_init_dl; -FuncDL +extern FuncDL av_get_bytes_per_sample_dl; +extern FuncDL av_frame_alloc_dl; +extern FuncDL av_frame_free_dl; +extern FuncDL avcodec_alloc_context3_dl; +extern FuncDL avcodec_free_context_dl; +extern FuncDL avcodec_open2_dl; +extern FuncDL av_packet_alloc_dl; +extern FuncDL av_packet_free_dl; +extern FuncDL avcodec_find_decoder_dl; +extern FuncDL avcodec_send_packet_dl; +extern FuncDL avcodec_receive_frame_dl; +extern FuncDL av_parser_init_dl; +extern FuncDL av_parser_parse2_dl; -FuncDL av_parser_close_dl; +extern FuncDL av_parser_close_dl; -bool InitFFmpegDL() { +bool InitFFmpegDL(); - FileUtil::CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::DLLDir)); - SetDllDirectoryA(FileUtil::GetUserPath(FileUtil::UserPath::DLLDir).c_str()); - - HMODULE dll_util = nullptr; - dll_util = LoadLibrary("avutil-56.dll"); - if (!dll_util) { - DWORD errorMessageID = GetLastError(); - LPSTR messageBuffer = nullptr; - size_t size = - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&messageBuffer, 0, NULL); - - std::string message(messageBuffer, size); - - // Free the buffer. - LocalFree(messageBuffer); - LOG_ERROR(Audio_DSP, "Could not load avutil-56.dll: {}", message); - return false; - } - - HMODULE dll_codec = nullptr; - dll_codec = LoadLibrary("avcodec-58.dll"); - if (!dll_codec) { - DWORD errorMessageID = GetLastError(); - LPSTR messageBuffer = nullptr; - size_t size = - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&messageBuffer, 0, NULL); - - std::string message(messageBuffer, size); - - // Free the buffer. - LocalFree(messageBuffer); - LOG_ERROR(Audio_DSP, "Could not load avcodec-58.dll: {}", message); - return false; - } - av_get_bytes_per_sample_dl = FuncDL(dll_util, "av_get_bytes_per_sample"); - if (!av_get_bytes_per_sample_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_get_bytes_per_sample"); - return false; - } - - av_frame_alloc_dl = FuncDL(dll_util, "av_frame_alloc"); - if (!av_frame_alloc_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_frame_alloc"); - return false; - } - - av_frame_free_dl = FuncDL(dll_util, "av_frame_free"); - if (!av_frame_free_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_frame_free"); - return false; - } - - avcodec_alloc_context3_dl = - FuncDL(dll_codec, "avcodec_alloc_context3"); - if (!avcodec_alloc_context3_dl) { - LOG_ERROR(Audio_DSP, "Can not load function avcodec_alloc_context3"); - return false; - } - - avcodec_free_context_dl = FuncDL(dll_codec, "avcodec_free_context"); - if (!av_get_bytes_per_sample_dl) { - LOG_ERROR(Audio_DSP, "Can not load function avcodec_free_context"); - return false; - } - - avcodec_open2_dl = - FuncDL(dll_codec, "avcodec_open2"); - if (!avcodec_open2_dl) { - LOG_ERROR(Audio_DSP, "Can not load function avcodec_open2"); - return false; - } - av_packet_alloc_dl = FuncDL(dll_codec, "av_packet_alloc"); - if (!av_packet_alloc_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_packet_alloc"); - return false; - } - - av_packet_free_dl = FuncDL(dll_codec, "av_packet_free"); - if (!av_packet_free_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_packet_free"); - return false; - } - - avcodec_find_decoder_dl = FuncDL(dll_codec, "avcodec_find_decoder"); - if (!avcodec_find_decoder_dl) { - LOG_ERROR(Audio_DSP, "Can not load function avcodec_find_decoder"); - return false; - } - - avcodec_send_packet_dl = - FuncDL(dll_codec, "avcodec_send_packet"); - if (!avcodec_send_packet_dl) { - LOG_ERROR(Audio_DSP, "Can not load function avcodec_send_packet"); - return false; - } - - avcodec_receive_frame_dl = - FuncDL(dll_codec, "avcodec_receive_frame"); - if (!avcodec_receive_frame_dl) { - LOG_ERROR(Audio_DSP, "Can not load function avcodec_receive_frame"); - return false; - } - - av_parser_init_dl = FuncDL(dll_codec, "av_parser_init"); - if (!av_parser_init_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_parser_init"); - return false; - } - - av_parser_parse2_dl = - FuncDL(dll_codec, "av_parser_parse2"); - if (!av_parser_parse2_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_parser_parse2"); - return false; - } - - av_parser_close_dl = FuncDL(dll_codec, "av_parser_close"); - if (!av_parser_close_dl) { - LOG_ERROR(Audio_DSP, "Can not load function av_parser_close"); - return false; - } - - return true; -} - -#endif // _Win32 - -#if defined(__APPLE__) || defined(__linux__) +#else // _Win32 // No dynamic loading for Unix and Apple @@ -213,4 +76,4 @@ bool InitFFmpegDL() { return true; } -#endif // defined(__APPLE__) || defined(__linux__) +#endif // _Win32 From 8fe3e37df596dbf6c51fde11dfe8268b037bfd48 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sun, 16 Dec 2018 13:58:33 -0700 Subject: [PATCH 05/34] audio_core: fix msvc include issue --- CMakeLists.txt | 2 +- src/audio_core/hle/ffmpeg_dl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e72baefe..d9433456c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,7 +270,7 @@ if (ENABLE_FFMPEG) if (DEFINED FFmpeg_VER) download_bundled_external("ffmpeg/" ${FFmpeg_VER} FFmpeg_PREFIX) - set(FFMPEG_DIR "${FFmpeg_PREFIX}") + set(FFMPEG_DIR "${FFmpeg_PREFIX}/../") set(FFMPEG_FOUND YES) endif() else() diff --git a/src/audio_core/hle/ffmpeg_dl.h b/src/audio_core/hle/ffmpeg_dl.h index 57e150f95..a882e4f7b 100644 --- a/src/audio_core/hle/ffmpeg_dl.h +++ b/src/audio_core/hle/ffmpeg_dl.h @@ -9,7 +9,7 @@ #endif // _WIN32 extern "C" { -#include "libavcodec/avcodec.h" +#include } #ifdef _WIN32 From 1581dea6dee808675200a0cfb54c80f6541a31b9 Mon Sep 17 00:00:00 2001 From: B3N30 Date: Mon, 17 Dec 2018 19:35:34 +0100 Subject: [PATCH 06/34] more reviews addressed --- src/audio_core/hle/decoder.cpp | 3 --- src/audio_core/hle/ffmpeg_dl.cpp | 11 ++++++++--- src/audio_core/hle/ffmpeg_dl.h | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/audio_core/hle/decoder.cpp b/src/audio_core/hle/decoder.cpp index e181e3cde..6569a6f45 100644 --- a/src/audio_core/hle/decoder.cpp +++ b/src/audio_core/hle/decoder.cpp @@ -20,7 +20,6 @@ std::optional NullDecoder::ProcessRequest(const BinaryRequest& r std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; return response; - break; case DecoderCommand::Decode: response.codec = request.codec; response.cmd = DecoderCommand::Decode; @@ -28,11 +27,9 @@ std::optional NullDecoder::ProcessRequest(const BinaryRequest& r response.size = request.size; response.num_samples = 1024; // Just assume 1024 here return response; - break; default: LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", static_cast(request.cmd)); return {}; - break; } }; } // namespace AudioCore::HLE diff --git a/src/audio_core/hle/ffmpeg_dl.cpp b/src/audio_core/hle/ffmpeg_dl.cpp index 0a3b2148c..073d0aedd 100644 --- a/src/audio_core/hle/ffmpeg_dl.cpp +++ b/src/audio_core/hle/ffmpeg_dl.cpp @@ -8,10 +8,13 @@ #include "audio_core/hle/ffmpeg_dl.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/string_util.h" + +namespace { struct LibraryDeleter { - typedef HMODULE pointer; - void operator()(HMODULE h) { + using pointer = HMODULE; + void operator()(HMODULE h) const { if (h != nullptr) FreeLibrary(h); } @@ -20,6 +23,8 @@ struct LibraryDeleter { std::unique_ptr dll_util{nullptr}; std::unique_ptr dll_codec{nullptr}; +} // namespace + FuncDL av_get_bytes_per_sample_dl; FuncDL av_frame_alloc_dl; FuncDL av_frame_free_dl; @@ -40,7 +45,7 @@ FuncDL av_parser_close_dl; bool InitFFmpegDL() { std::string dll_path = FileUtil::GetUserPath(FileUtil::UserPath::DLLDir); FileUtil::CreateDir(dll_path); - std::wstring w_dll_path = std::wstring(dll_path.begin(), dll_path.end()); + std::wstring w_dll_path = Common::UTF8ToUTF16W(dll_path); SetDllDirectoryW(w_dll_path.c_str()); dll_util.reset(LoadLibrary("avutil-56.dll")); diff --git a/src/audio_core/hle/ffmpeg_dl.h b/src/audio_core/hle/ffmpeg_dl.h index a882e4f7b..6ca2066fc 100644 --- a/src/audio_core/hle/ffmpeg_dl.h +++ b/src/audio_core/hle/ffmpeg_dl.h @@ -17,9 +17,9 @@ extern "C" { template struct FuncDL { FuncDL() = default; - FuncDL(void* dll, const char* name) { + FuncDL(HMODULE dll, const char* name) { if (dll) { - ptr_function = reinterpret_cast(GetProcAddress((HMODULE)dll, name)); + ptr_function = reinterpret_cast(GetProcAddress(dll, name)); } } From 80b4dd21d2c97c246d66b3d4541913df20b98141 Mon Sep 17 00:00:00 2001 From: B3N30 Date: Wed, 19 Dec 2018 17:12:57 +0100 Subject: [PATCH 07/34] audio_core: dsp_hle: add Media Foundation decoder... * appveyor: switch to Media Foundation API * Travis CI MinGW build needs an update with the container image --- CMakeLists.txt | 7 +- appveyor.yml | 4 +- src/audio_core/CMakeLists.txt | 12 +- src/audio_core/hle/adts.h | 31 ++ src/audio_core/hle/adts_reader.c | 49 +++ src/audio_core/hle/hle.cpp | 10 +- src/audio_core/hle/wmf_decoder.cpp | 254 ++++++++++++++++ src/audio_core/hle/wmf_decoder.h | 22 ++ src/audio_core/hle/wmf_decoder_utils.cpp | 366 +++++++++++++++++++++++ src/audio_core/hle/wmf_decoder_utils.h | 48 +++ src/tests/CMakeLists.txt | 3 +- src/tests/audio_core/audio_fixures.h | 5 + src/tests/audio_core/decoder_tests.cpp | 50 ++++ 13 files changed, 839 insertions(+), 22 deletions(-) create mode 100644 src/audio_core/hle/adts.h create mode 100644 src/audio_core/hle/adts_reader.c create mode 100644 src/audio_core/hle/wmf_decoder.cpp create mode 100644 src/audio_core/hle/wmf_decoder.h create mode 100644 src/audio_core/hle/wmf_decoder_utils.cpp create mode 100644 src/audio_core/hle/wmf_decoder_utils.h create mode 100644 src/tests/audio_core/audio_fixures.h create mode 100644 src/tests/audio_core/decoder_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d9433456c..cba5c70ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,13 +24,8 @@ option(ENABLE_FFMPEG "Enable FFmpeg decoder/encoder" OFF) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) -<<<<<<< HEAD -======= -option(ENABLE_SCRIPTING "Enables scripting support" OFF) +CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder" ON "WIN32;NOT ENABLE_FFMPEG" OFF) -CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_FFMPEG "Download bundled FFmpeg binaries" ON "MSVC" OFF) - ->>>>>>> CoreAudio::HLE: Add FFmpeg aac decoder if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) message(STATUS "Copying pre-commit hook") file(COPY hooks/pre-commit diff --git a/appveyor.yml b/appveyor.yml index 3c8646a88..0e1ee94c4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,9 +43,9 @@ before_build: $COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING} if ($env:BUILD_TYPE -eq 'msvc') { # redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning - cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=ON .. 2>&1 && exit 0' + cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_MF=ON .. 2>&1 && exit 0' } else { - C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=ON .. 2>&1" + C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_MF=ON .. 2>&1" } - cd .. diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index d00d23d98..95b9790e9 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -29,7 +29,7 @@ add_library(audio_core STATIC $<$:sdl2_sink.cpp sdl2_sink.h> $<$:cubeb_sink.cpp cubeb_sink.h> - $<$:hle/aac_decoder.cpp hle/aac_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h> + $<$:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h hle/adts_reader.c> ) create_target_directory_groups(audio_core) @@ -37,13 +37,9 @@ create_target_directory_groups(audio_core) target_link_libraries(audio_core PUBLIC common core) target_link_libraries(audio_core PRIVATE SoundTouch teakra) -if(FFMPEG_FOUND) - if(UNIX) - target_link_libraries(audio_core PRIVATE FFmpeg::avcodec) - else() - target_include_directories(audio_core PRIVATE ${FFMPEG_DIR}/include) - endif() - target_compile_definitions(audio_core PRIVATE HAVE_FFMPEG) +if(ENABLE_MF) + target_link_libraries(audio_core PRIVATE mf.lib mfplat.lib mfuuid.lib) + target_compile_definitions(audio_core PUBLIC HAVE_MF) endif() if(SDL2_FOUND) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h new file mode 100644 index 000000000..cba952a22 --- /dev/null +++ b/src/audio_core/hle/adts.h @@ -0,0 +1,31 @@ +#pragma once +#ifndef ADTS_ADT +#define ADTS_ADT + +#include +#include +#include + +struct ADTSData { + bool MPEG2; + uint8_t profile; + uint8_t channels; + uint8_t channel_idx; + uint8_t framecount; + uint8_t samplerate_idx; + uint32_t length; + uint32_t samplerate; +}; + +typedef struct ADTSData ADTSData; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +uint32_t parse_adts(char* buffer, struct ADTSData* out); +// last two bytes of MF AAC decoder user data +uint16_t mf_get_aac_tag(struct ADTSData input); +#ifdef __cplusplus +} +#endif // __cplusplus +#endif // ADTS_ADT diff --git a/src/audio_core/hle/adts_reader.c b/src/audio_core/hle/adts_reader.c new file mode 100644 index 000000000..7be57d4fc --- /dev/null +++ b/src/audio_core/hle/adts_reader.c @@ -0,0 +1,49 @@ + +#include "adts.h" + +const uint32_t freq_table[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; +const short channel_table[8] = {0, 1, 2, 3, 4, 5, 6, 8}; + +uint32_t parse_adts(char* buffer, struct ADTSData* out) { + uint32_t tmp = 0; + + // sync word 0xfff + tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); + if ((tmp & 0xffff) != 0xfff0) + return 0; + out->MPEG2 = (buffer[1] >> 3) & 0x1; + // bit 17 to 18 + out->profile = (buffer[2] >> 6) + 1; + // bit 19 to 22 + tmp = (buffer[2] >> 2) & 0xf; + out->samplerate_idx = tmp; + out->samplerate = (tmp > 15) ? 0 : freq_table[tmp]; + // bit 24 to 26 + tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3); + out->channel_idx = tmp; + out->channels = (tmp > 7) ? 0 : channel_table[tmp]; + + // bit 55 to 56 + out->framecount = (buffer[6] & 0x3) + 1; + + // bit 31 to 43 + tmp = (buffer[3] & 0x3) << 11; + tmp |= (buffer[4] << 3) & 0x7f8; + tmp |= (buffer[5] >> 5) & 0x7; + + out->length = tmp; + + return tmp; +} + +// last two bytes of MF AAC decoder user data +uint16_t mf_get_aac_tag(struct ADTSData input) { + uint16_t tag = 0; + + tag |= input.profile << 11; + tag |= input.samplerate_idx << 7; + tag |= input.channel_idx << 3; + + return tag; +} diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index b2a9873a7..6482ee3e5 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -3,8 +3,8 @@ // Refer to the license.txt file included. #include "audio_core/audio_types.h" -#ifdef HAVE_FFMPEG -#include "audio_core/hle/aac_decoder.h" +#ifdef HAVE_MF +#include "audio_core/hle/wmf_decoder.h" #endif #include "audio_core/hle/common.h" #include "audio_core/hle/decoder.h" @@ -85,12 +85,12 @@ DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory) : parent(paren source.SetMemory(memory); } -#ifdef HAVE_FFMPEG - decoder = std::make_unique(memory); +#ifdef HAVE_MF + decoder = std::make_unique(memory); #else LOG_WARNING(Audio_DSP, "FFmpeg missing, this could lead to missing audio"); decoder = std::make_unique(); -#endif // HAVE_FFMPEG +#endif // HAVE_MF Core::Timing& timing = Core::System::GetInstance().CoreTiming(); tick_event = diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp new file mode 100644 index 000000000..f612b8983 --- /dev/null +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -0,0 +1,254 @@ +// 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 { + +class WMFDecoder::Impl { +public: + explicit Impl(Memory::MemorySystem& memory); + ~Impl(); + std::optional ProcessRequest(const BinaryRequest& request); + +private: + std::optional Initalize(const BinaryRequest& request); + + void Clear(); + + std::optional Decode(const BinaryRequest& request); + + int DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams); + + bool initalized = false; + bool selected = false; + + Memory::MemorySystem& memory; + + IMFTransform* transform = NULL; + DWORD in_stream_id = 0; + DWORD out_stream_id = 0; +}; + +WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { + mf_coinit(); +} + +WMFDecoder::Impl::~Impl() = default; + +std::optional WMFDecoder::Impl::ProcessRequest(const BinaryRequest& request) { + if (request.codec != DecoderCodec::AAC) { + LOG_ERROR(Audio_DSP, "Got unknown codec {}", static_cast(request.codec)); + return {}; + } + + switch (request.cmd) { + case DecoderCommand::Init: { + LOG_INFO(Audio_DSP, "AACDecoder initializing"); + return Initalize(request); + } + case DecoderCommand::Decode: { + return Decode(request); + } + case DecoderCommand::Unknown: { + BinaryResponse response; + std::memcpy(&response, &request, sizeof(response)); + response.unknown1 = 0x0; + return response; + } + default: + LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", static_cast(request.cmd)); + return {}; + } +} + +std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& request) { + if (initalized) { + Clear(); + } + + BinaryResponse response; + std::memcpy(&response, &request, sizeof(response)); + response.unknown1 = 0x0; + + if (mf_decoder_init(&transform) != 0) { + LOG_CRITICAL(Audio_DSP, "Can't init decoder"); + return response; + } + + HRESULT 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); + SafeRelease(&transform); + return response; + } + + initalized = true; + return response; +} + +void WMFDecoder::Impl::Clear() { + if (initalized) { + mf_flush(&transform); + mf_deinit(&transform); + } + initalized = false; + selected = false; +} + +int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, + std::array, 2>& out_streams) { + int output_status = 0; + char* output_buffer = NULL; + DWORD output_len = 0; + IMFSample* output = NULL; + + while (true) { + output_status = receive_sample(transform, out_stream_id, &output); + + // 0 -> okay; 3 -> okay but more data available (buffer too small) + if (output_status == 0 || output_status == 3) { + copy_sample_to_buffer(output, (void**)&output_buffer, &output_len); + + // the following was taken from ffmpeg version of the decoder + f32 val_f32; + for (size_t i = 0; i < output_len;) { + for (std::size_t channel = 0; channel < adts_header.channels; channel++) { + std::memcpy(&val_f32, output_buffer + i, sizeof(val_f32)); + s16 val = static_cast(0x7FFF * val_f32); + out_streams[channel].push_back(val & 0xFF); + out_streams[channel].push_back(val >> 8); + i += sizeof(val_f32); + } + } + + if (output_buffer) + free(output_buffer); + } + + // in case of "ok" only, just return quickly + if (output_status == 0) + return 0; + + // for status = 2, reset MF + if (output_status == 2) { + Clear(); + return -1; + } + + // for status = 3, try again with new buffer + if (output_status == 3) + continue; + + return output_status; // return on other status + } + + return -1; +} + +std::optional WMFDecoder::Impl::Decode(const BinaryRequest& request) { + BinaryResponse response; + response.codec = request.codec; + response.cmd = request.cmd; + response.size = request.size; + response.num_channels = 2; + response.num_samples = 1024; + + if (!initalized) { + LOG_DEBUG(Audio_DSP, "Decoder not initalized"); + // This is a hack to continue games that are not compiled with the aac codec + return response; + } + + if (request.src_addr < Memory::FCRAM_PADDR || + request.src_addr + request.size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { + LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", request.src_addr); + return {}; + } + u8* data = memory.GetFCRAMPointer(request.src_addr - Memory::FCRAM_PADDR); + + std::array, 2> out_streams; + IMFSample* sample = NULL; + ADTSData adts_header; + char* aac_tag = (char*)calloc(1, 14); + int input_status = 0; + + if (detect_mediatype((char*)data, request.size, &adts_header, &aac_tag) != 0) { + LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); + return response; + } + + if (!selected) { + LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}", + adts_header.channels, adts_header.samplerate); + select_input_mediatype(transform, in_stream_id, adts_header, (UINT8*)aac_tag, 14); + select_output_mediatype(transform, out_stream_id); + send_sample(transform, in_stream_id, NULL); + // 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); + selected = true; + } + + sample = create_sample((void*)data, request.size, 1, 0); + sample->SetUINT32(MFSampleExtension_CleanPoint, 1); + + while (true) { + input_status = send_sample(transform, in_stream_id, sample); + + if (DecodingLoop(adts_header, out_streams) < 0) { + // if the decode issues is 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 == 1) { + // try again + continue; + } + + return response; + } + + break; // jump out of the loop if at least we don't have obvious issues + } + + if (out_streams[0].size() != 0) { + if (request.dst_addr_ch0 < Memory::FCRAM_PADDR || + 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.dst_addr_ch0); + return {}; + } + std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch0 - Memory::FCRAM_PADDR), + out_streams[0].data(), out_streams[0].size()); + } + + if (out_streams[1].size() != 0) { + if (request.dst_addr_ch1 < Memory::FCRAM_PADDR || + 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.dst_addr_ch1); + return {}; + } + std::memcpy(memory.GetFCRAMPointer(request.dst_addr_ch1 - Memory::FCRAM_PADDR), + out_streams[1].data(), out_streams[1].size()); + } + + response.num_channels = adts_header.channels; + return response; +} + +WMFDecoder::WMFDecoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} + +WMFDecoder::~WMFDecoder() = default; + +std::optional WMFDecoder::ProcessRequest(const BinaryRequest& request) { + return impl->ProcessRequest(request); +} + +} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/wmf_decoder.h b/src/audio_core/hle/wmf_decoder.h new file mode 100644 index 000000000..34e223740 --- /dev/null +++ b/src/audio_core/hle/wmf_decoder.h @@ -0,0 +1,22 @@ +// 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 ProcessRequest(const BinaryRequest& request) override; + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp new file mode 100644 index 000000000..58b00505b --- /dev/null +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -0,0 +1,366 @@ +#include "common/logging/log.h" +#include "wmf_decoder_utils.h" + +// utility functions +void ReportError(std::string msg, HRESULT hr) { + if (SUCCEEDED(hr)) { + return; + } + LPSTR err; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, hr, + // hardcode to use en_US because if any user had problems with this + // we can help them w/o translating anything + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&err, 0, NULL); + if (err != NULL) { + LOG_CRITICAL(Audio_DSP, "{}: {}", msg, err); + } + LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr); +} + +int mf_coinit() { + HRESULT hr = S_OK; + + // lite startup is faster and all what we need is included + hr = 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); + return -1; + } + + LOG_INFO(Audio_DSP, "Media Foundation activated"); + + return 0; +} + +int mf_decoder_init(IMFTransform** transform, GUID audio_format) { + HRESULT hr = S_OK; + MFT_REGISTER_TYPE_INFO reg = {0}; + GUID category = MFT_CATEGORY_AUDIO_DECODER; + IMFActivate** activate; + 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, + ®, NULL, &activate, &num_activate); + if (FAILED(hr) || num_activate < 1) { + ReportError("Failed to enumerate decoders", hr); + CoTaskMemFree(activate); + return -1; + } + 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, (void**)transform); + if (FAILED(hr)) + *transform = NULL; + activate[n]->Release(); + } + if (*transform == NULL) { + ReportError("Failed to initialize MFT", hr); + CoTaskMemFree(activate); + return -1; + } + CoTaskMemFree(activate); + return 0; +} + +void mf_deinit(IMFTransform** transform) { + MFShutdownObject(*transform); + SafeRelease(transform); + CoUninitialize(); +} + +IMFSample* create_sample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { + HRESULT hr = S_OK; + IMFMediaBuffer* buf = NULL; + IMFSample* sample = NULL; + + hr = MFCreateSample(&sample); + if (FAILED(hr)) { + ReportError("Unable to allocate a sample", hr); + return NULL; + } + // Yes, the argument for alignment is the actual alignment - 1 + hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf); + if (FAILED(hr)) { + ReportError("Unable to allocate a memory buffer for sample", hr); + return NULL; + } + if (data) { + BYTE* buffer; + // lock the MediaBuffer + // this is actually not a thread-safe lock + hr = buf->Lock(&buffer, NULL, NULL); + if (FAILED(hr)) { + SafeRelease(&sample); + SafeRelease(&buf); + return NULL; + } + + memcpy(buffer, data, len); + + buf->SetCurrentLength(len); + buf->Unlock(); + } + + sample->AddBuffer(buf); + hr = sample->SetSampleDuration(duration); + SafeRelease(&buf); + return sample; +} + +int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData adts, + UINT8* user_data, UINT32 user_data_len, GUID audio_format) { + HRESULT hr = S_OK; + 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(&t); + if (FAILED(hr)) { + ReportError("Unable to create an empty MediaType", hr); + return -1; + } + + // basic definition + t->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); + t->SetGUID(MF_MT_SUBTYPE, audio_format); + + // see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types + // and https://docs.microsoft.com/zh-cn/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag + // for the meaning of the byte array below + + // for integrate into a larger project, it is recommended to wrap the parameters into a struct + // and pass that struct into the function + // const UINT8 aac_data[] = { 0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x11, 0x90 + // }; 0: raw aac 1: adts 2: adif 3: latm/laos + 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, 0); + if (FAILED(hr)) { + ReportError("failed to select input types for MFT", hr); + return -1; + } + + return 0; +} + +int select_output_mediatype(IMFTransform* transform, int out_stream_id, GUID audio_format) { + HRESULT hr = S_OK; + UINT32 tmp; + IMFMediaType* t; + + // If you know what you need and what you are doing, you can specify the condition 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, &t); + if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) { + return 0; + } + if (FAILED(hr)) { + ReportError("failed to get output types for MFT", hr); + return -1; + } + + hr = t->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp); + + if (FAILED(hr)) + continue; + // select PCM-16 format + if (tmp == 32) { + hr = t->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 -1; + } + hr = transform->SetOutputType(out_stream_id, t, 0); + if (FAILED(hr)) { + ReportError("failed to select output types for MFT", hr); + return -1; + } + return 0; + } else { + continue; + } + + return -1; + } + + ReportError("MFT: Unable to find preferred output format", E_NOTIMPL); + return -1; +} + +int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag) { + if (len < 7) { + return -1; + } + + ADTSData tmp; + UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; + uint16_t tag = 0; + + uint32_t result = parse_adts(buffer, &tmp); + if (result == 0) { + return -1; + } + + tag = mf_get_aac_tag(tmp); + aac_tmp[12] |= (tag & 0xff00) >> 8; + aac_tmp[13] |= (tag & 0x00ff); + memcpy(*aac_tag, aac_tmp, 14); + memcpy(output, &tmp, sizeof(ADTSData)); + return 0; +} + +int mf_flush(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); + } + + return 0; +} + +int send_sample(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 1; // try again + } else if (FAILED(hr)) { + ReportError("MFT: Failed to process input", hr); + return -1; + } // FAILED(hr) + } else { + hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); + // ffmpeg: Some MFTs (AC3) will send a frame after each drain command (???), so + // ffmpeg: this is required to make draining actually terminate. + if (FAILED(hr)) { + ReportError("MFT: Failed to drain when processing input", hr); + } + } + + return 0; +} + +// return: 0: okay; 1: needs more sample; 2: needs reconfiguring; 3: more data available +int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) { + HRESULT hr; + MFT_OUTPUT_DATA_BUFFER out_buffers; + IMFSample* sample = NULL; + MFT_OUTPUT_STREAM_INFO out_info; + DWORD status = 0; + bool mft_create_sample = false; + + if (!out_sample) { + ReportError("NULL pointer passed to receive_sample()", MF_E_SAMPLE_NOT_WRITABLE); + return -1; + } + + hr = transform->GetOutputStreamInfo(out_stream_id, &out_info); + + if (FAILED(hr)) { + ReportError("MFT: Failed to get stream info", hr); + return -1; + } + mft_create_sample = (out_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) || + (out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES); + + while (true) { + sample = NULL; + *out_sample = NULL; + status = 0; + + if (!mft_create_sample) { + sample = create_sample(NULL, out_info.cbSize, out_info.cbAlignment); + if (!sample) { + ReportError("MFT: Unable to allocate memory for samples", hr); + return -1; + } + } + + out_buffers.dwStreamID = out_stream_id; + out_buffers.pSample = sample; + + hr = transform->ProcessOutput(0, 1, &out_buffers, &status); + + if (!FAILED(hr)) { + *out_sample = out_buffers.pSample; + break; + } + + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + // TODO: better handling try again and EOF cases using drain value + return 1; + } + + if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { + ReportError("MFT: stream format changed, re-configuration required", hr); + return 2; + } + + break; + } + + if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { + return 3; + } + + // TODO: better handling try again and EOF cases using drain value + if (*out_sample == NULL) { + ReportError("MFT: decoding failure", hr); + return -1; + } + + return 0; +} + +int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len) { + IMFMediaBuffer* buffer; + HRESULT hr = S_OK; + BYTE* data; + + hr = sample->GetTotalLength(len); + if (FAILED(hr)) { + ReportError("Failed to get the length of sample buffer", hr); + return -1; + } + + sample->ConvertToContiguousBuffer(&buffer); + if (FAILED(hr)) { + ReportError("Failed to get sample buffer", hr); + return -1; + } + + hr = buffer->Lock(&data, NULL, NULL); + if (FAILED(hr)) { + ReportError("Failed to lock the buffer", hr); + SafeRelease(&buffer); + return -1; + } + + *output = malloc(*len); + memcpy(*output, data, *len); + + // if buffer unlock fails, then... whatever, we have already got data + buffer->Unlock(); + SafeRelease(&buffer); + return 0; +} diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h new file mode 100644 index 000000000..ac7e522d7 --- /dev/null +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -0,0 +1,48 @@ +#pragma once + +#ifndef MF_DECODER +#define MF_DECODER + +#define WINVER _WIN32_WINNT_WIN7 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "adts.h" + +// utility functions +template +void SafeRelease(T** ppT) { + if (*ppT) { + (*ppT)->Release(); + *ppT = NULL; + } +} + +void ReportError(std::string msg, HRESULT hr); + +// exported functions +int mf_coinit(); +int mf_decoder_init(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); +void mf_deinit(IMFTransform** transform); +IMFSample* create_sample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); +int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData adts, + UINT8* user_data, UINT32 user_data_len, + GUID audio_format = MFAudioFormat_AAC); +int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag); +int select_output_mediatype(IMFTransform* transform, int out_stream_id, + GUID audio_format = MFAudioFormat_PCM); +int mf_flush(IMFTransform** transform); +int send_sample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); +int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample); +int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len); + +#endif // MF_DECODER diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 0cb36a2ce..b6af20886 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(tests core/hle/kernel/hle_ipc.cpp core/memory/memory.cpp core/memory/vm_manager.cpp + audio_core/decoder_tests.cpp tests.cpp ) @@ -21,7 +22,7 @@ endif() create_target_directory_groups(tests) -target_link_libraries(tests PRIVATE common core video_core) +target_link_libraries(tests PRIVATE common core video_core audio_core) target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include nihstro-headers Threads::Threads) add_test(NAME tests COMMAND tests) diff --git a/src/tests/audio_core/audio_fixures.h b/src/tests/audio_core/audio_fixures.h new file mode 100644 index 000000000..3035840a3 --- /dev/null +++ b/src/tests/audio_core/audio_fixures.h @@ -0,0 +1,5 @@ +const int fixure_buffer_size = 41; +const unsigned char fixure_buffer[41] = { + 0xff, 0xf1, 0x4c, 0x80, 0x05, 0x3f, 0xfc, 0x21, 0x1a, 0x4e, 0xb0, 0x00, 0x00, 0x00, + 0x05, 0xfc, 0x4e, 0x1f, 0x08, 0x88, 0x00, 0x00, 0x00, 0xc4, 0x1a, 0x03, 0xfc, 0x9c, + 0x3e, 0x1d, 0x08, 0x84, 0x03, 0xd8, 0x3f, 0xe4, 0xe1, 0x20, 0x00, 0x0b, 0x38}; diff --git a/src/tests/audio_core/decoder_tests.cpp b/src/tests/audio_core/decoder_tests.cpp new file mode 100644 index 000000000..3a197f0dc --- /dev/null +++ b/src/tests/audio_core/decoder_tests.cpp @@ -0,0 +1,50 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. +#ifdef HAVE_MF + +#include +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/memory.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/shared_page.h" +#include "core/memory.h" + +#include "audio_core/hle/decoder.h" +#include "audio_core/hle/wmf_decoder.h" +#include "audio_fixures.h" + +TEST_CASE("DSP HLE Audio Decoder", "[audio_core]") { + // HACK: see comments of member timing + Core::System::GetInstance().timing = std::make_unique(); + Core::System::GetInstance().memory = std::make_unique(); + Kernel::KernelSystem kernel(*Core::System::GetInstance().memory, 0); + SECTION("decoder should produce correct samples") { + auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); + auto decoder = + std::make_unique(*Core::System::GetInstance().memory); + AudioCore::HLE::BinaryRequest request; + + request.codec = AudioCore::HLE::DecoderCodec::AAC; + request.cmd = AudioCore::HLE::DecoderCommand::Init; + // initialize decoder + std::optional response = decoder->ProcessRequest(request); + + request.cmd = AudioCore::HLE::DecoderCommand::Decode; + u8* fcram = Core::System::GetInstance().memory->GetFCRAMPointer(0); + + memcpy(fcram, fixure_buffer, fixure_buffer_size); + request.src_addr = Memory::FCRAM_PADDR; + request.dst_addr_ch0 = Memory::FCRAM_PADDR + 1024; + request.dst_addr_ch1 = Memory::FCRAM_PADDR + 1048576; // 1 MB + request.size = fixure_buffer_size; + + response = decoder->ProcessRequest(request); + response = decoder->ProcessRequest(request); + // remove this line + request.src_addr = Memory::FCRAM_PADDR; + } +} + +#endif From 11e277149cc84861e609c6108cb6aa4377c98c25 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Fri, 4 Jan 2019 17:06:12 -0700 Subject: [PATCH 08/34] audio_core: hle: fix compile --- src/audio_core/CMakeLists.txt | 10 +++++++++ .../{aac_decoder.cpp => ffmpeg_decoder.cpp} | 22 +++++++++---------- .../hle/{aac_decoder.h => ffmpeg_decoder.h} | 6 ++--- src/audio_core/hle/hle.cpp | 6 ++++- src/tests/audio_core/decoder_tests.cpp | 10 ++++++++- 5 files changed, 38 insertions(+), 16 deletions(-) rename src/audio_core/hle/{aac_decoder.cpp => ffmpeg_decoder.cpp} (91%) rename src/audio_core/hle/{aac_decoder.h => ffmpeg_decoder.h} (74%) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 95b9790e9..a6fe42594 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -29,6 +29,7 @@ add_library(audio_core STATIC $<$:sdl2_sink.cpp sdl2_sink.h> $<$:cubeb_sink.cpp cubeb_sink.h> + $<$:hle/ffmpeg_decoder.cpp hle/ffmpeg_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h> $<$:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h hle/adts_reader.c> ) @@ -37,6 +38,15 @@ create_target_directory_groups(audio_core) target_link_libraries(audio_core PUBLIC common core) target_link_libraries(audio_core PRIVATE SoundTouch teakra) +if(FFMPEG_FOUND) + if(UNIX) + target_link_libraries(audio_core PRIVATE FFmpeg::avcodec) + else() + target_include_directories(audio_core PRIVATE ${FFMPEG_DIR}/include) + endif() + target_compile_definitions(audio_core PUBLIC HAVE_FFMPEG) +endif() + if(ENABLE_MF) target_link_libraries(audio_core PRIVATE mf.lib mfplat.lib mfuuid.lib) target_compile_definitions(audio_core PUBLIC HAVE_MF) diff --git a/src/audio_core/hle/aac_decoder.cpp b/src/audio_core/hle/ffmpeg_decoder.cpp similarity index 91% rename from src/audio_core/hle/aac_decoder.cpp rename to src/audio_core/hle/ffmpeg_decoder.cpp index 774d1a8ee..0bed4bbb4 100644 --- a/src/audio_core/hle/aac_decoder.cpp +++ b/src/audio_core/hle/ffmpeg_decoder.cpp @@ -2,12 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "audio_core/hle/aac_decoder.h" +#include "audio_core/hle/ffmpeg_decoder.h" #include "audio_core/hle/ffmpeg_dl.h" namespace AudioCore::HLE { -class AACDecoder::Impl { +class FFMPEGDecoder::Impl { public: explicit Impl(Memory::MemorySystem& memory); ~Impl(); @@ -56,13 +56,13 @@ private: std::unique_ptr decoded_frame; }; -AACDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { +FFMPEGDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { have_ffmpeg_dl = InitFFmpegDL(); } -AACDecoder::Impl::~Impl() = default; +FFMPEGDecoder::Impl::~Impl() = default; -std::optional AACDecoder::Impl::ProcessRequest(const BinaryRequest& request) { +std::optional FFMPEGDecoder::Impl::ProcessRequest(const BinaryRequest& request) { if (request.codec != DecoderCodec::AAC) { LOG_ERROR(Audio_DSP, "Got wrong codec {}", static_cast(request.codec)); return {}; @@ -87,7 +87,7 @@ std::optional AACDecoder::Impl::ProcessRequest(const BinaryReque } } -std::optional AACDecoder::Impl::Initalize(const BinaryRequest& request) { +std::optional FFMPEGDecoder::Impl::Initalize(const BinaryRequest& request) { if (initalized) { Clear(); } @@ -129,7 +129,7 @@ std::optional AACDecoder::Impl::Initalize(const BinaryRequest& r return response; } -void AACDecoder::Impl::Clear() { +void FFMPEGDecoder::Impl::Clear() { if (!have_ffmpeg_dl) { return; } @@ -140,7 +140,7 @@ void AACDecoder::Impl::Clear() { av_packet.reset(); } -std::optional AACDecoder::Impl::Decode(const BinaryRequest& request) { +std::optional FFMPEGDecoder::Impl::Decode(const BinaryRequest& request) { BinaryResponse response; response.codec = request.codec; response.cmd = request.cmd; @@ -252,11 +252,11 @@ std::optional AACDecoder::Impl::Decode(const BinaryRequest& requ return response; } -AACDecoder::AACDecoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} +FFMPEGDecoder::FFMPEGDecoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} -AACDecoder::~AACDecoder() = default; +FFMPEGDecoder::~FFMPEGDecoder() = default; -std::optional AACDecoder::ProcessRequest(const BinaryRequest& request) { +std::optional FFMPEGDecoder::ProcessRequest(const BinaryRequest& request) { return impl->ProcessRequest(request); } diff --git a/src/audio_core/hle/aac_decoder.h b/src/audio_core/hle/ffmpeg_decoder.h similarity index 74% rename from src/audio_core/hle/aac_decoder.h rename to src/audio_core/hle/ffmpeg_decoder.h index ec941dfb0..190251543 100644 --- a/src/audio_core/hle/aac_decoder.h +++ b/src/audio_core/hle/ffmpeg_decoder.h @@ -8,10 +8,10 @@ namespace AudioCore::HLE { -class AACDecoder final : public DecoderBase { +class FFMPEGDecoder final : public DecoderBase { public: - explicit AACDecoder(Memory::MemorySystem& memory); - ~AACDecoder() override; + explicit FFMPEGDecoder(Memory::MemorySystem& memory); + ~FFMPEGDecoder() override; std::optional ProcessRequest(const BinaryRequest& request) override; private: diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index 6482ee3e5..0bb7d4413 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -5,6 +5,8 @@ #include "audio_core/audio_types.h" #ifdef HAVE_MF #include "audio_core/hle/wmf_decoder.h" +#elif HAVE_FFMPEG +#include "audio_core/hle/ffmpeg_decoder.h" #endif #include "audio_core/hle/common.h" #include "audio_core/hle/decoder.h" @@ -87,8 +89,10 @@ DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory) : parent(paren #ifdef HAVE_MF decoder = std::make_unique(memory); +#elif HAVE_FFMPEG + decoder = std::make_unique(memory); #else - LOG_WARNING(Audio_DSP, "FFmpeg missing, this could lead to missing audio"); + LOG_WARNING(Audio_DSP, "No decoder found, this could lead to missing audio"); decoder = std::make_unique(); #endif // HAVE_MF diff --git a/src/tests/audio_core/decoder_tests.cpp b/src/tests/audio_core/decoder_tests.cpp index 3a197f0dc..5a723c299 100644 --- a/src/tests/audio_core/decoder_tests.cpp +++ b/src/tests/audio_core/decoder_tests.cpp @@ -1,7 +1,7 @@ // Copyright 2017 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#ifdef HAVE_MF +#if defined(HAVE_MF) || defined(HAVE_FFMPEG) #include #include "core/core.h" @@ -12,7 +12,11 @@ #include "core/memory.h" #include "audio_core/hle/decoder.h" +#ifdef HAVE_MF #include "audio_core/hle/wmf_decoder.h" +#elif HAVE_FFMPEG +#include "audio_core/hle/ffmpeg_decoder.h" +#endif #include "audio_fixures.h" TEST_CASE("DSP HLE Audio Decoder", "[audio_core]") { @@ -23,7 +27,11 @@ TEST_CASE("DSP HLE Audio Decoder", "[audio_core]") { SECTION("decoder should produce correct samples") { auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); auto decoder = +#ifdef HAVE_MF std::make_unique(*Core::System::GetInstance().memory); +#elif HAVE_FFMPEG + std::make_unique(*Core::System::GetInstance().memory); +#endif AudioCore::HLE::BinaryRequest request; request.codec = AudioCore::HLE::DecoderCodec::AAC; From 7f5b54fda4550bc312323d68ee41d1bfad59b6e9 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 5 Jan 2019 21:53:24 -0700 Subject: [PATCH 09/34] audio_core: hle: mf: address reviews from @B3N30 --- src/audio_core/CMakeLists.txt | 2 +- src/audio_core/hle/adts.h | 29 +++--- .../hle/{adts_reader.c => adts_reader.cpp} | 16 ++-- src/audio_core/hle/wmf_decoder.cpp | 36 ++++---- src/audio_core/hle/wmf_decoder_utils.cpp | 89 ++++++++++--------- src/audio_core/hle/wmf_decoder_utils.h | 33 ++++--- src/tests/audio_core/audio_fixures.h | 11 ++- src/tests/audio_core/decoder_tests.cpp | 2 +- 8 files changed, 113 insertions(+), 105 deletions(-) rename src/audio_core/hle/{adts_reader.c => adts_reader.cpp} (70%) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index a6fe42594..8da8420da 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -30,7 +30,7 @@ add_library(audio_core STATIC $<$:sdl2_sink.cpp sdl2_sink.h> $<$:cubeb_sink.cpp cubeb_sink.h> $<$:hle/ffmpeg_decoder.cpp hle/ffmpeg_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h> - $<$:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h hle/adts_reader.c> + $<$:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h hle/adts_reader.cpp> ) create_target_directory_groups(audio_core) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index cba952a22..c806e2d82 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -1,20 +1,20 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. #pragma once -#ifndef ADTS_ADT -#define ADTS_ADT -#include -#include -#include +#include +#include "common/common_types.h" struct ADTSData { bool MPEG2; - uint8_t profile; - uint8_t channels; - uint8_t channel_idx; - uint8_t framecount; - uint8_t samplerate_idx; - uint32_t length; - uint32_t samplerate; + u8 profile; + u8 channels; + u8 channel_idx; + u8 framecount; + u8 samplerate_idx; + u32 length; + u32 samplerate; }; typedef struct ADTSData ADTSData; @@ -22,10 +22,9 @@ typedef struct ADTSData ADTSData; #ifdef __cplusplus extern "C" { #endif // __cplusplus -uint32_t parse_adts(char* buffer, struct ADTSData* out); +u32 parse_adts(char* buffer, struct ADTSData* out); // last two bytes of MF AAC decoder user data -uint16_t mf_get_aac_tag(struct ADTSData input); +u16 mf_get_aac_tag(struct ADTSData input); #ifdef __cplusplus } #endif // __cplusplus -#endif // ADTS_ADT diff --git a/src/audio_core/hle/adts_reader.c b/src/audio_core/hle/adts_reader.cpp similarity index 70% rename from src/audio_core/hle/adts_reader.c rename to src/audio_core/hle/adts_reader.cpp index 7be57d4fc..ce3d1eda4 100644 --- a/src/audio_core/hle/adts_reader.c +++ b/src/audio_core/hle/adts_reader.cpp @@ -1,12 +1,14 @@ - +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. #include "adts.h" -const uint32_t freq_table[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, +constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; -const short channel_table[8] = {0, 1, 2, 3, 4, 5, 6, 8}; +constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; -uint32_t parse_adts(char* buffer, struct ADTSData* out) { - uint32_t tmp = 0; +u32 parse_adts(char* buffer, struct ADTSData* out) { + u32 tmp = 0; // sync word 0xfff tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); @@ -38,8 +40,8 @@ uint32_t parse_adts(char* buffer, struct ADTSData* out) { } // last two bytes of MF AAC decoder user data -uint16_t mf_get_aac_tag(struct ADTSData input) { - uint16_t tag = 0; +u16 mf_get_aac_tag(struct ADTSData input) { + u16 tag = 0; tag |= input.profile << 11; tag |= input.samplerate_idx << 7; diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index f612b8983..390ccd8dc 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -27,13 +27,13 @@ private: Memory::MemorySystem& memory; - IMFTransform* transform = NULL; + IMFTransform* transform = nullptr; DWORD in_stream_id = 0; DWORD out_stream_id = 0; }; WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { - mf_coinit(); + MFCoInit(); } WMFDecoder::Impl::~Impl() = default; @@ -46,7 +46,7 @@ std::optional WMFDecoder::Impl::ProcessRequest(const BinaryReque switch (request.cmd) { case DecoderCommand::Init: { - LOG_INFO(Audio_DSP, "AACDecoder initializing"); + LOG_INFO(Audio_DSP, "WMFDecoder initializing"); return Initalize(request); } case DecoderCommand::Decode: { @@ -73,7 +73,7 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; - if (mf_decoder_init(&transform) != 0) { + if (MFDecoderInit(&transform) != 0) { LOG_CRITICAL(Audio_DSP, "Can't init decoder"); return response; } @@ -95,8 +95,8 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r void WMFDecoder::Impl::Clear() { if (initalized) { - mf_flush(&transform); - mf_deinit(&transform); + MFFlush(&transform); + MFDeInit(&transform); } initalized = false; selected = false; @@ -105,16 +105,16 @@ void WMFDecoder::Impl::Clear() { int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams) { int output_status = 0; - char* output_buffer = NULL; + char* output_buffer = nullptr; DWORD output_len = 0; - IMFSample* output = NULL; + IMFSample* output = nullptr; while (true) { - output_status = receive_sample(transform, out_stream_id, &output); + output_status = ReceiveSample(transform, out_stream_id, &output); // 0 -> okay; 3 -> okay but more data available (buffer too small) if (output_status == 0 || output_status == 3) { - copy_sample_to_buffer(output, (void**)&output_buffer, &output_len); + CopySampleToBuffer(output, (void**)&output_buffer, &output_len); // the following was taken from ffmpeg version of the decoder f32 val_f32; @@ -174,12 +174,12 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ u8* data = memory.GetFCRAMPointer(request.src_addr - Memory::FCRAM_PADDR); std::array, 2> out_streams; - IMFSample* sample = NULL; + IMFSample* sample = nullptr; ADTSData adts_header; char* aac_tag = (char*)calloc(1, 14); int input_status = 0; - if (detect_mediatype((char*)data, request.size, &adts_header, &aac_tag) != 0) { + if (DetectMediaType((char*)data, request.size, &adts_header, &aac_tag) != 0) { LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); return response; } @@ -187,23 +187,23 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ if (!selected) { LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}", adts_header.channels, adts_header.samplerate); - select_input_mediatype(transform, in_stream_id, adts_header, (UINT8*)aac_tag, 14); - select_output_mediatype(transform, out_stream_id); - send_sample(transform, in_stream_id, NULL); + SelectInputMediaType(transform, in_stream_id, adts_header, (UINT8*)aac_tag, 14); + SelectOutputMediaType(transform, out_stream_id); + SendSample(transform, 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); selected = true; } - sample = create_sample((void*)data, request.size, 1, 0); + sample = CreateSample((void*)data, request.size, 1, 0); sample->SetUINT32(MFSampleExtension_CleanPoint, 1); while (true) { - input_status = send_sample(transform, in_stream_id, sample); + input_status = SendSample(transform, in_stream_id, sample); if (DecodingLoop(adts_header, out_streams) < 0) { - // if the decode issues is caused by MFT not accepting new samples, try again + // 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 == 1) { diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 58b00505b..50a8a5554 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -1,3 +1,6 @@ +// 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 "wmf_decoder_utils.h" @@ -9,17 +12,17 @@ void ReportError(std::string msg, HRESULT hr) { LPSTR err; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, hr, + nullptr, hr, // hardcode to use en_US because if any user had problems with this // we can help them w/o translating anything - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&err, 0, NULL); - if (err != NULL) { + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&err, 0, nullptr); + if (err != nullptr) { LOG_CRITICAL(Audio_DSP, "{}: {}", msg, err); } LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr); } -int mf_coinit() { +int MFCoInit() { HRESULT hr = S_OK; // lite startup is faster and all what we need is included @@ -35,7 +38,7 @@ int mf_coinit() { return 0; } -int mf_decoder_init(IMFTransform** transform, GUID audio_format) { +int MFDecoderInit(IMFTransform** transform, GUID audio_format) { HRESULT hr = S_OK; MFT_REGISTER_TYPE_INFO reg = {0}; GUID category = MFT_CATEGORY_AUDIO_DECODER; @@ -47,7 +50,7 @@ int mf_decoder_init(IMFTransform** transform, GUID audio_format) { hr = MFTEnumEx(category, MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER, - ®, NULL, &activate, &num_activate); + ®, nullptr, &activate, &num_activate); if (FAILED(hr) || num_activate < 1) { ReportError("Failed to enumerate decoders", hr); CoTaskMemFree(activate); @@ -57,10 +60,10 @@ int mf_decoder_init(IMFTransform** transform, GUID audio_format) { for (unsigned int n = 0; n < num_activate; n++) { hr = activate[n]->ActivateObject(IID_IMFTransform, (void**)transform); if (FAILED(hr)) - *transform = NULL; + *transform = nullptr; activate[n]->Release(); } - if (*transform == NULL) { + if (*transform == nullptr) { ReportError("Failed to initialize MFT", hr); CoTaskMemFree(activate); return -1; @@ -69,37 +72,37 @@ int mf_decoder_init(IMFTransform** transform, GUID audio_format) { return 0; } -void mf_deinit(IMFTransform** transform) { +void MFDeInit(IMFTransform** transform) { MFShutdownObject(*transform); SafeRelease(transform); CoUninitialize(); } -IMFSample* create_sample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { +IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { HRESULT hr = S_OK; - IMFMediaBuffer* buf = NULL; - IMFSample* sample = NULL; + IMFMediaBuffer* buf = nullptr; + IMFSample* sample = nullptr; hr = MFCreateSample(&sample); if (FAILED(hr)) { ReportError("Unable to allocate a sample", hr); - return NULL; + return nullptr; } // Yes, the argument for alignment is the actual alignment - 1 hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf); if (FAILED(hr)) { ReportError("Unable to allocate a memory buffer for sample", hr); - return NULL; + return nullptr; } if (data) { BYTE* buffer; // lock the MediaBuffer // this is actually not a thread-safe lock - hr = buf->Lock(&buffer, NULL, NULL); + hr = buf->Lock(&buffer, nullptr, nullptr); if (FAILED(hr)) { SafeRelease(&sample); SafeRelease(&buf); - return NULL; + return nullptr; } memcpy(buffer, data, len); @@ -114,7 +117,7 @@ IMFSample* create_sample(void* data, DWORD len, DWORD alignment, LONGLONG durati return sample; } -int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData adts, +bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, ADTSData adts, UINT8* user_data, UINT32 user_data_len, GUID audio_format) { HRESULT hr = S_OK; IMFMediaType* t; @@ -124,7 +127,7 @@ int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData a hr = MFCreateMediaType(&t); if (FAILED(hr)) { ReportError("Unable to create an empty MediaType", hr); - return -1; + return false; } // basic definition @@ -149,13 +152,13 @@ int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData a hr = transform->SetInputType(in_stream_id, t, 0); if (FAILED(hr)) { ReportError("failed to select input types for MFT", hr); - return -1; + return false; } - return 0; + return true; } -int select_output_mediatype(IMFTransform* transform, int out_stream_id, GUID audio_format) { +bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format) { HRESULT hr = S_OK; UINT32 tmp; IMFMediaType* t; @@ -166,11 +169,11 @@ int select_output_mediatype(IMFTransform* transform, int out_stream_id, GUID aud for (DWORD i = 0;; i++) { hr = transform->GetOutputAvailableType(out_stream_id, i, &t); if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) { - return 0; + return true; } if (FAILED(hr)) { ReportError("failed to get output types for MFT", hr); - return -1; + return false; } hr = t->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp); @@ -183,26 +186,26 @@ int select_output_mediatype(IMFTransform* transform, int out_stream_id, GUID aud if (FAILED(hr)) { ReportError("failed to set MF_MT_AUDIO_BLOCK_ALIGNMENT for MFT on output stream", hr); - return -1; + return false; } hr = transform->SetOutputType(out_stream_id, t, 0); if (FAILED(hr)) { ReportError("failed to select output types for MFT", hr); - return -1; + return false; } - return 0; + return true; } else { continue; } - return -1; + return false; } ReportError("MFT: Unable to find preferred output format", E_NOTIMPL); - return -1; + return false; } -int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag) { +int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) { if (len < 7) { return -1; } @@ -224,7 +227,7 @@ int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag) return 0; } -int mf_flush(IMFTransform** transform) { +void MFFlush(IMFTransform** transform) { HRESULT hr = (*transform)->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); if (FAILED(hr)) { ReportError("MFT: Flush command failed", hr); @@ -233,11 +236,9 @@ int mf_flush(IMFTransform** transform) { if (FAILED(hr)) { ReportError("Failed to end streaming for MFT", hr); } - - return 0; } -int send_sample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample) { +int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample) { HRESULT hr = S_OK; if (in_sample) { @@ -261,16 +262,16 @@ int send_sample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sampl } // return: 0: okay; 1: needs more sample; 2: needs reconfiguring; 3: more data available -int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) { +int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) { HRESULT hr; MFT_OUTPUT_DATA_BUFFER out_buffers; - IMFSample* sample = NULL; + IMFSample* sample = nullptr; MFT_OUTPUT_STREAM_INFO out_info; DWORD status = 0; bool mft_create_sample = false; if (!out_sample) { - ReportError("NULL pointer passed to receive_sample()", MF_E_SAMPLE_NOT_WRITABLE); + ReportError("nullptr pointer passed to receive_sample()", MF_E_SAMPLE_NOT_WRITABLE); return -1; } @@ -284,12 +285,12 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out (out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES); while (true) { - sample = NULL; - *out_sample = NULL; + sample = nullptr; + *out_sample = nullptr; status = 0; if (!mft_create_sample) { - sample = create_sample(NULL, out_info.cbSize, out_info.cbAlignment); + sample = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment); if (!sample) { ReportError("MFT: Unable to allocate memory for samples", hr); return -1; @@ -307,7 +308,7 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out } if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - // TODO: better handling try again and EOF cases using drain value + // Most likely reasons: data corrupted; your actions not expected by MFT return 1; } @@ -320,11 +321,11 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out } if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { + // this status is also unreliable but whatever return 3; } - // TODO: better handling try again and EOF cases using drain value - if (*out_sample == NULL) { + if (*out_sample == nullptr) { ReportError("MFT: decoding failure", hr); return -1; } @@ -332,7 +333,7 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out return 0; } -int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len) { +int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { IMFMediaBuffer* buffer; HRESULT hr = S_OK; BYTE* data; @@ -349,7 +350,7 @@ int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len) { return -1; } - hr = buffer->Lock(&data, NULL, NULL); + hr = buffer->Lock(&data, nullptr, nullptr); if (FAILED(hr)) { ReportError("Failed to lock the buffer", hr); SafeRelease(&buffer); diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index ac7e522d7..128c30477 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -1,8 +1,9 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. #pragma once -#ifndef MF_DECODER -#define MF_DECODER - +// AAC decoder related APIs are only available with WIN7+ #define WINVER _WIN32_WINNT_WIN7 #include @@ -23,26 +24,24 @@ template void SafeRelease(T** ppT) { if (*ppT) { (*ppT)->Release(); - *ppT = NULL; + *ppT = nullptr; } } void ReportError(std::string msg, HRESULT hr); // exported functions -int mf_coinit(); -int mf_decoder_init(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); -void mf_deinit(IMFTransform** transform); -IMFSample* create_sample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); -int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData adts, +int MFCoInit(); +int MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); +void MFDeInit(IMFTransform** transform); +IMFSample* CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); +bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, ADTSData adts, UINT8* user_data, UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); -int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag); -int select_output_mediatype(IMFTransform* transform, int out_stream_id, +int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag); +bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format = MFAudioFormat_PCM); -int mf_flush(IMFTransform** transform); -int send_sample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); -int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample); -int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len); - -#endif // MF_DECODER +void MFFlush(IMFTransform** transform); +int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); +int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample); +int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len); diff --git a/src/tests/audio_core/audio_fixures.h b/src/tests/audio_core/audio_fixures.h index 3035840a3..58147b6fd 100644 --- a/src/tests/audio_core/audio_fixures.h +++ b/src/tests/audio_core/audio_fixures.h @@ -1,5 +1,12 @@ -const int fixure_buffer_size = 41; -const unsigned char fixure_buffer[41] = { +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once +#include + +constexpr int fixure_buffer_size = 41; +constexpr std::array fixure_buffer[41] = { 0xff, 0xf1, 0x4c, 0x80, 0x05, 0x3f, 0xfc, 0x21, 0x1a, 0x4e, 0xb0, 0x00, 0x00, 0x00, 0x05, 0xfc, 0x4e, 0x1f, 0x08, 0x88, 0x00, 0x00, 0x00, 0xc4, 0x1a, 0x03, 0xfc, 0x9c, 0x3e, 0x1d, 0x08, 0x84, 0x03, 0xd8, 0x3f, 0xe4, 0xe1, 0x20, 0x00, 0x0b, 0x38}; diff --git a/src/tests/audio_core/decoder_tests.cpp b/src/tests/audio_core/decoder_tests.cpp index 5a723c299..d8f31127d 100644 --- a/src/tests/audio_core/decoder_tests.cpp +++ b/src/tests/audio_core/decoder_tests.cpp @@ -1,4 +1,4 @@ -// Copyright 2017 Citra Emulator Project +// Copyright 2019 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #if defined(HAVE_MF) || defined(HAVE_FFMPEG) From c03861c2d9b7b5087064367adeae0fc7954a8e77 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 5 Jan 2019 22:28:56 -0700 Subject: [PATCH 10/34] audio_core: hle: mf: address another batch of reviews from @B3N30 --- src/audio_core/CMakeLists.txt | 4 ++- src/audio_core/hle/adts.h | 8 ----- src/audio_core/hle/adts_reader.cpp | 2 +- src/audio_core/hle/wmf_decoder.cpp | 15 +++++----- src/audio_core/hle/wmf_decoder_utils.cpp | 37 ++++++++++++------------ src/audio_core/hle/wmf_decoder_utils.h | 16 +++++----- 6 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 8da8420da..27e7d723b 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -4,6 +4,8 @@ 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 @@ -30,7 +32,7 @@ add_library(audio_core STATIC $<$:sdl2_sink.cpp sdl2_sink.h> $<$:cubeb_sink.cpp cubeb_sink.h> $<$:hle/ffmpeg_decoder.cpp hle/ffmpeg_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h> - $<$:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h hle/adts_reader.cpp> + $<$:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h> ) create_target_directory_groups(audio_core) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index c806e2d82..c2f5304fa 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -17,14 +17,6 @@ struct ADTSData { u32 samplerate; }; -typedef struct ADTSData ADTSData; - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus u32 parse_adts(char* buffer, struct ADTSData* out); // last two bytes of MF AAC decoder user data u16 mf_get_aac_tag(struct ADTSData input); -#ifdef __cplusplus -} -#endif // __cplusplus diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index ce3d1eda4..540aed577 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -4,7 +4,7 @@ #include "adts.h" constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; + 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; u32 parse_adts(char* buffer, struct ADTSData* out) { diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 390ccd8dc..5dd28b7b5 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -73,7 +73,7 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; - if (MFDecoderInit(&transform) != 0) { + if (!MFDecoderInit(&transform)) { LOG_CRITICAL(Audio_DSP, "Can't init decoder"); return response; } @@ -104,7 +104,7 @@ void WMFDecoder::Impl::Clear() { int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams) { - int output_status = 0; + MFOutputState output_status = OK; char* output_buffer = nullptr; DWORD output_len = 0; IMFSample* output = nullptr; @@ -113,7 +113,7 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, output_status = ReceiveSample(transform, out_stream_id, &output); // 0 -> okay; 3 -> okay but more data available (buffer too small) - if (output_status == 0 || output_status == 3) { + if (output_status == OK || output_status == HAVE_MORE_DATA) { CopySampleToBuffer(output, (void**)&output_buffer, &output_len); // the following was taken from ffmpeg version of the decoder @@ -133,20 +133,21 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, } // in case of "ok" only, just return quickly - if (output_status == 0) + if (output_status == OK) return 0; // for status = 2, reset MF - if (output_status == 2) { + if (output_status == NEED_RECONFIG) { Clear(); return -1; } // for status = 3, try again with new buffer - if (output_status == 3) + if (output_status == HAVE_MORE_DATA) continue; - return output_status; // return on other status + LOG_ERROR(Audio_DSP, "Errors occurred when receiving output: {}", output_status); + return -1; // return on other status } return -1; diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 50a8a5554..459f5322c 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -22,7 +22,7 @@ void ReportError(std::string msg, HRESULT hr) { LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr); } -int MFCoInit() { +bool MFCoInit() { HRESULT hr = S_OK; // lite startup is faster and all what we need is included @@ -30,15 +30,15 @@ int MFCoInit() { 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); - return -1; + return false; } LOG_INFO(Audio_DSP, "Media Foundation activated"); - return 0; + return true; } -int MFDecoderInit(IMFTransform** transform, GUID audio_format) { +bool MFDecoderInit(IMFTransform** transform, GUID audio_format) { HRESULT hr = S_OK; MFT_REGISTER_TYPE_INFO reg = {0}; GUID category = MFT_CATEGORY_AUDIO_DECODER; @@ -54,7 +54,7 @@ int MFDecoderInit(IMFTransform** transform, GUID audio_format) { if (FAILED(hr) || num_activate < 1) { ReportError("Failed to enumerate decoders", hr); CoTaskMemFree(activate); - return -1; + return false; } LOG_INFO(Audio_DSP, "Windows(R) Media Foundation found {} suitable decoder(s)", num_activate); for (unsigned int n = 0; n < num_activate; n++) { @@ -66,10 +66,10 @@ int MFDecoderInit(IMFTransform** transform, GUID audio_format) { if (*transform == nullptr) { ReportError("Failed to initialize MFT", hr); CoTaskMemFree(activate); - return -1; + return false; } CoTaskMemFree(activate); - return 0; + return true; } void MFDeInit(IMFTransform** transform) { @@ -117,8 +117,8 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio return sample; } -bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, ADTSData adts, - UINT8* user_data, UINT32 user_data_len, GUID audio_format) { +bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, + UINT8* user_data, UINT32 user_data_len, GUID audio_format) { HRESULT hr = S_OK; IMFMediaType* t; @@ -261,8 +261,7 @@ int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample return 0; } -// return: 0: okay; 1: needs more sample; 2: needs reconfiguring; 3: more data available -int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) { +MFOutputState ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) { HRESULT hr; MFT_OUTPUT_DATA_BUFFER out_buffers; IMFSample* sample = nullptr; @@ -272,14 +271,14 @@ int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_ if (!out_sample) { ReportError("nullptr pointer passed to receive_sample()", MF_E_SAMPLE_NOT_WRITABLE); - return -1; + return FATAL_ERROR; } hr = transform->GetOutputStreamInfo(out_stream_id, &out_info); if (FAILED(hr)) { ReportError("MFT: Failed to get stream info", hr); - return -1; + return FATAL_ERROR; } mft_create_sample = (out_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) || (out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES); @@ -293,7 +292,7 @@ int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_ sample = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment); if (!sample) { ReportError("MFT: Unable to allocate memory for samples", hr); - return -1; + return FATAL_ERROR; } } @@ -309,12 +308,12 @@ int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_ if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { // Most likely reasons: data corrupted; your actions not expected by MFT - return 1; + return NEED_MORE_INPUT; } if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { ReportError("MFT: stream format changed, re-configuration required", hr); - return 2; + return NEED_RECONFIG; } break; @@ -322,15 +321,15 @@ int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_ if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { // this status is also unreliable but whatever - return 3; + return HAVE_MORE_DATA; } if (*out_sample == nullptr) { ReportError("MFT: decoding failure", hr); - return -1; + return FATAL_ERROR; } - return 0; + return OK; } int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 128c30477..49b443fc2 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -19,6 +19,8 @@ #include "adts.h" +enum MFOutputState { FATAL_ERROR = -1, OK = 0, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA }; + // utility functions template void SafeRelease(T** ppT) { @@ -31,17 +33,17 @@ void SafeRelease(T** ppT) { void ReportError(std::string msg, HRESULT hr); // exported functions -int MFCoInit(); -int MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); +bool MFCoInit(); +bool MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); void MFDeInit(IMFTransform** transform); IMFSample* CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); -bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, ADTSData adts, - UINT8* user_data, UINT32 user_data_len, - GUID audio_format = MFAudioFormat_AAC); +bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, + UINT8* user_data, UINT32 user_data_len, + GUID audio_format = MFAudioFormat_AAC); int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag); bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, - GUID audio_format = MFAudioFormat_PCM); + GUID audio_format = MFAudioFormat_PCM); void MFFlush(IMFTransform** transform); int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); -int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample); +MFOutputState ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample); int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len); From 06316be8a78954c8ce2f052dfe104a6a8b7af694 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 5 Jan 2019 21:05:12 -0700 Subject: [PATCH 11/34] audio_core: hle: mf: minor fix --- src/audio_core/hle/wmf_decoder.cpp | 3 ++- src/tests/CMakeLists.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 5dd28b7b5..1a6e147ee 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -185,6 +185,8 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ return response; } + response.num_channels = adts_header.channels; + if (!selected) { LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}", adts_header.channels, adts_header.samplerate); @@ -240,7 +242,6 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ out_streams[1].data(), out_streams[1].size()); } - response.num_channels = adts_header.channels; return response; } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index b6af20886..c6b222ef0 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(tests core/hle/kernel/hle_ipc.cpp core/memory/memory.cpp core/memory/vm_manager.cpp + audio_core/audio_fixures.h audio_core/decoder_tests.cpp tests.cpp ) From bee5ba3e33a808c8ddc733b02c4ad143b6ce8960 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sun, 6 Jan 2019 13:29:21 -0700 Subject: [PATCH 12/34] audio_core: hle: mf: fix a regression... ... an introduction in 8f591d3b85714383196fd21e744a7eed5302ff0a --- src/audio_core/hle/wmf_decoder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 1a6e147ee..92b53750c 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -146,7 +146,9 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, if (output_status == HAVE_MORE_DATA) continue; - LOG_ERROR(Audio_DSP, "Errors occurred when receiving output: {}", output_status); + if (output_status == NEED_MORE_INPUT) // according to MS document, this is not an error (?!) + return 1; + return -1; // return on other status } @@ -214,6 +216,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ continue; } + LOG_ERROR(Audio_DSP, "Errors occurred when receiving output"); return response; } From 10f876653dbf2ab80104ab1b6f63a496ad080c94 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 7 Jan 2019 16:57:20 -0700 Subject: [PATCH 13/34] audio_core: hle: mf: transition to use smart pointers --- src/audio_core/hle/wmf_decoder.cpp | 21 +++++++++++---------- src/audio_core/hle/wmf_decoder_utils.cpp | 19 +++++++++---------- src/audio_core/hle/wmf_decoder_utils.h | 11 +++++++++-- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 92b53750c..3cb4ea1b2 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -27,7 +27,7 @@ private: Memory::MemorySystem& memory; - IMFTransform* transform = nullptr; + std::unique_ptr> transform; DWORD in_stream_id = 0; DWORD out_stream_id = 0; }; @@ -70,13 +70,15 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r } BinaryResponse response; + IMFTransform* tmp = nullptr; std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; - if (!MFDecoderInit(&transform)) { + if (!MFDecoderInit(&tmp)) { LOG_CRITICAL(Audio_DSP, "Can't init decoder"); return response; } + transform.reset(tmp); HRESULT hr = transform->GetStreamIDs(1, &in_stream_id, 1, &out_stream_id); if (hr == E_NOTIMPL) { @@ -85,7 +87,6 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r out_stream_id = 0; } else if (FAILED(hr)) { ReportError("Decoder failed to initialize the stream ID", hr); - SafeRelease(&transform); return response; } @@ -95,8 +96,8 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r void WMFDecoder::Impl::Clear() { if (initalized) { - MFFlush(&transform); - MFDeInit(&transform); + MFFlush(transform.get()); + MFDeInit(transform.get()); } initalized = false; selected = false; @@ -110,7 +111,7 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, IMFSample* output = nullptr; while (true) { - output_status = ReceiveSample(transform, out_stream_id, &output); + output_status = ReceiveSample(transform.get(), out_stream_id, &output); // 0 -> okay; 3 -> okay but more data available (buffer too small) if (output_status == OK || output_status == HAVE_MORE_DATA) { @@ -192,9 +193,9 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ if (!selected) { LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}", adts_header.channels, adts_header.samplerate); - SelectInputMediaType(transform, in_stream_id, adts_header, (UINT8*)aac_tag, 14); - SelectOutputMediaType(transform, out_stream_id); - SendSample(transform, in_stream_id, nullptr); + SelectInputMediaType(transform.get(), in_stream_id, adts_header, (UINT8*)aac_tag, 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); @@ -205,7 +206,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ sample->SetUINT32(MFSampleExtension_CleanPoint, 1); while (true) { - input_status = SendSample(transform, in_stream_id, sample); + input_status = SendSample(transform.get(), in_stream_id, sample); if (DecodingLoop(adts_header, out_streams) < 0) { // if the decode issues are caused by MFT not accepting new samples, try again diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 459f5322c..8debac9d2 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -72,9 +72,8 @@ bool MFDecoderInit(IMFTransform** transform, GUID audio_format) { return true; } -void MFDeInit(IMFTransform** transform) { - MFShutdownObject(*transform); - SafeRelease(transform); +void MFDeInit(IMFTransform* transform) { + MFShutdownObject(transform); CoUninitialize(); } @@ -227,12 +226,12 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) return 0; } -void MFFlush(IMFTransform** transform) { - HRESULT hr = (*transform)->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); +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); + hr = (transform)->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0); if (FAILED(hr)) { ReportError("Failed to end streaming for MFT", hr); } @@ -333,7 +332,8 @@ MFOutputState ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSam } int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { - IMFMediaBuffer* buffer; + std::unique_ptr> buffer; + IMFMediaBuffer* tmp; HRESULT hr = S_OK; BYTE* data; @@ -343,16 +343,16 @@ int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { return -1; } - sample->ConvertToContiguousBuffer(&buffer); + sample->ConvertToContiguousBuffer(&tmp); if (FAILED(hr)) { ReportError("Failed to get sample buffer", hr); return -1; } + buffer.reset(tmp); hr = buffer->Lock(&data, nullptr, nullptr); if (FAILED(hr)) { ReportError("Failed to lock the buffer", hr); - SafeRelease(&buffer); return -1; } @@ -361,6 +361,5 @@ int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { // if buffer unlock fails, then... whatever, we have already got data buffer->Unlock(); - SafeRelease(&buffer); return 0; } diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 49b443fc2..0b27badcf 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -30,12 +30,19 @@ void SafeRelease(T** ppT) { } } +template +struct MFRelease { + void operator()(T* pointer) const { + pointer->Release(); + }; +}; + void ReportError(std::string msg, HRESULT hr); // exported functions bool MFCoInit(); bool MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); -void MFDeInit(IMFTransform** transform); +void MFDeInit(IMFTransform* transform); IMFSample* CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, UINT8* user_data, UINT32 user_data_len, @@ -43,7 +50,7 @@ bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSD int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag); bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format = MFAudioFormat_PCM); -void MFFlush(IMFTransform** transform); +void MFFlush(IMFTransform* transform); int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); MFOutputState ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample); int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len); From b1638b31a00d8c95687815ba905e1dfd09691afa Mon Sep 17 00:00:00 2001 From: B3N30 Date: Wed, 9 Jan 2019 10:52:54 +0100 Subject: [PATCH 14/34] Change travis MinGW build from FFmpeg to WMF --- .travis/linux-mingw/docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/linux-mingw/docker.sh b/.travis/linux-mingw/docker.sh index 4a0e4304b..5a3b8f2b0 100755 --- a/.travis/linux-mingw/docker.sh +++ b/.travis/linux-mingw/docker.sh @@ -5,7 +5,7 @@ cd /citra echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf" mkdir build && cd build -cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_FFMPEG=ON +cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_MF=ON make -j4 echo "Tests skipped" From ea8a1f8754b3c783d69d84ea4ec404ee2f33c278 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 14 Jan 2019 14:27:43 -0700 Subject: [PATCH 15/34] Media Foundation Memory Fix * audio_core: hle: mf: more smart pointers * audio_core: hle: mf: fix memory leaks * audio_core: hle: mf: even more smart pointers --- src/audio_core/hle/wmf_decoder.cpp | 17 ++++---- src/audio_core/hle/wmf_decoder_utils.cpp | 53 +++++++++++------------- src/audio_core/hle/wmf_decoder_utils.h | 7 +++- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 3cb4ea1b2..2be505e25 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -27,7 +27,7 @@ private: Memory::MemorySystem& memory; - std::unique_ptr> transform; + unique_mfptr transform; DWORD in_stream_id = 0; DWORD out_stream_id = 0; }; @@ -108,14 +108,17 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, MFOutputState output_status = OK; char* output_buffer = nullptr; DWORD output_len = 0; - IMFSample* output = nullptr; + DWORD tmp = 0; + // IMFSample* output_tmp = nullptr; + IMFMediaBuffer* mdbuf = nullptr; + unique_mfptr output; while (true) { - output_status = ReceiveSample(transform.get(), out_stream_id, &output); + auto [output_status, output] = ReceiveSample(transform.get(), out_stream_id); // 0 -> okay; 3 -> okay but more data available (buffer too small) if (output_status == OK || output_status == HAVE_MORE_DATA) { - CopySampleToBuffer(output, (void**)&output_buffer, &output_len); + CopySampleToBuffer(output.get(), (void**)&output_buffer, &output_len); // the following was taken from ffmpeg version of the decoder f32 val_f32; @@ -178,7 +181,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ u8* data = memory.GetFCRAMPointer(request.src_addr - Memory::FCRAM_PADDR); std::array, 2> out_streams; - IMFSample* sample = nullptr; + unique_mfptr sample; ADTSData adts_header; char* aac_tag = (char*)calloc(1, 14); int input_status = 0; @@ -202,11 +205,11 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ selected = true; } - sample = CreateSample((void*)data, request.size, 1, 0); + sample.reset(CreateSample((void*)data, request.size, 1, 0)); sample->SetUINT32(MFSampleExtension_CleanPoint, 1); while (true) { - input_status = SendSample(transform.get(), in_stream_id, sample); + input_status = SendSample(transform.get(), in_stream_id, sample.get()); if (DecodingLoop(adts_header, out_streams) < 0) { // if the decode issues are caused by MFT not accepting new samples, try again diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 8debac9d2..515dc0e5c 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -79,7 +79,8 @@ void MFDeInit(IMFTransform* transform) { IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { HRESULT hr = S_OK; - IMFMediaBuffer* buf = nullptr; + IMFMediaBuffer* buf_tmp = nullptr; + unique_mfptr buf; IMFSample* sample = nullptr; hr = MFCreateSample(&sample); @@ -88,11 +89,12 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio return nullptr; } // Yes, the argument for alignment is the actual alignment - 1 - hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf); + hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf_tmp); if (FAILED(hr)) { ReportError("Unable to allocate a memory buffer for sample", hr); return nullptr; } + buf.reset(buf_tmp); if (data) { BYTE* buffer; // lock the MediaBuffer @@ -100,7 +102,7 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio hr = buf->Lock(&buffer, nullptr, nullptr); if (FAILED(hr)) { SafeRelease(&sample); - SafeRelease(&buf); + buf.reset(); return nullptr; } @@ -110,9 +112,8 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio buf->Unlock(); } - sample->AddBuffer(buf); + sample->AddBuffer(buf.get()); hr = sample->SetSampleDuration(duration); - SafeRelease(&buf); return sample; } @@ -260,59 +261,55 @@ int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample return 0; } -MFOutputState ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) { +std::tuple> ReceiveSample(IMFTransform* transform, + DWORD out_stream_id) { HRESULT hr; MFT_OUTPUT_DATA_BUFFER out_buffers; - IMFSample* sample = nullptr; + IMFSample* sample_tmp = nullptr; MFT_OUTPUT_STREAM_INFO out_info; DWORD status = 0; + unique_mfptr sample; bool mft_create_sample = false; - if (!out_sample) { - ReportError("nullptr pointer passed to receive_sample()", MF_E_SAMPLE_NOT_WRITABLE); - return FATAL_ERROR; - } - hr = transform->GetOutputStreamInfo(out_stream_id, &out_info); if (FAILED(hr)) { ReportError("MFT: Failed to get stream info", hr); - return FATAL_ERROR; + return std::make_tuple(FATAL_ERROR, 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) { sample = nullptr; - *out_sample = nullptr; status = 0; if (!mft_create_sample) { - sample = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment); - if (!sample) { + sample_tmp = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment); + if (!sample_tmp) { ReportError("MFT: Unable to allocate memory for samples", hr); - return FATAL_ERROR; + return std::make_tuple(FATAL_ERROR, std::move(sample)); } + sample.reset(sample_tmp); } out_buffers.dwStreamID = out_stream_id; - out_buffers.pSample = sample; + out_buffers.pSample = sample.get(); hr = transform->ProcessOutput(0, 1, &out_buffers, &status); if (!FAILED(hr)) { - *out_sample = out_buffers.pSample; break; } if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { // Most likely reasons: data corrupted; your actions not expected by MFT - return NEED_MORE_INPUT; + return std::make_tuple(NEED_MORE_INPUT, std::move(sample)); } if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { ReportError("MFT: stream format changed, re-configuration required", hr); - return NEED_RECONFIG; + return std::make_tuple(NEED_RECONFIG, std::move(sample)); } break; @@ -320,19 +317,19 @@ MFOutputState ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSam if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { // this status is also unreliable but whatever - return HAVE_MORE_DATA; + return std::make_tuple(HAVE_MORE_DATA, std::move(sample)); } - if (*out_sample == nullptr) { + if (out_buffers.pSample == nullptr) { ReportError("MFT: decoding failure", hr); - return FATAL_ERROR; + return std::make_tuple(FATAL_ERROR, std::move(sample)); } - return OK; + return std::make_tuple(OK, std::move(sample)); } int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { - std::unique_ptr> buffer; + unique_mfptr buffer; IMFMediaBuffer* tmp; HRESULT hr = S_OK; BYTE* data; @@ -343,14 +340,14 @@ int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { return -1; } - sample->ConvertToContiguousBuffer(&tmp); + hr = sample->ConvertToContiguousBuffer(&tmp); if (FAILED(hr)) { ReportError("Failed to get sample buffer", hr); return -1; } buffer.reset(tmp); - hr = buffer->Lock(&data, nullptr, nullptr); + hr = tmp->Lock(&data, nullptr, nullptr); if (FAILED(hr)) { ReportError("Failed to lock the buffer", hr); return -1; diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 0b27badcf..22d114881 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -16,6 +16,7 @@ #include #include +#include #include "adts.h" @@ -37,6 +38,9 @@ struct MFRelease { }; }; +template +using unique_mfptr = std::unique_ptr>; + void ReportError(std::string msg, HRESULT hr); // exported functions @@ -52,5 +56,6 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format = MFAudioFormat_PCM); void MFFlush(IMFTransform* transform); int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); -MFOutputState ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample); +std::tuple> ReceiveSample(IMFTransform* transform, + DWORD out_stream_id); int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len); From 2c40ef2a9064315deb5d1bf8e0799a23d5c6abaa Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 28 Jan 2019 21:20:46 -0700 Subject: [PATCH 16/34] audio_core: hle: mf: rename functions --- src/audio_core/hle/adts.h | 4 ++-- src/audio_core/hle/adts_reader.cpp | 4 ++-- src/audio_core/hle/wmf_decoder_utils.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index c2f5304fa..bd68422be 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -17,6 +17,6 @@ struct ADTSData { u32 samplerate; }; -u32 parse_adts(char* buffer, struct ADTSData* out); +u32 ParseADTS(char* buffer, struct ADTSData* out); // last two bytes of MF AAC decoder user data -u16 mf_get_aac_tag(struct ADTSData input); +u16 MFGetAACTag(struct ADTSData input); diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index 540aed577..1bb3b4c76 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -7,7 +7,7 @@ constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 3 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; -u32 parse_adts(char* buffer, struct ADTSData* out) { +u32 ParseADTS(char* buffer, struct ADTSData* out) { u32 tmp = 0; // sync word 0xfff @@ -40,7 +40,7 @@ u32 parse_adts(char* buffer, struct ADTSData* out) { } // last two bytes of MF AAC decoder user data -u16 mf_get_aac_tag(struct ADTSData input) { +u16 MFGetAACTag(struct ADTSData input) { u16 tag = 0; tag |= input.profile << 11; diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 515dc0e5c..4568a1147 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -214,12 +214,12 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; uint16_t tag = 0; - uint32_t result = parse_adts(buffer, &tmp); + uint32_t result = ParseADTS(buffer, &tmp); if (result == 0) { return -1; } - tag = mf_get_aac_tag(tmp); + tag = MFGetAACTag(tmp); aac_tmp[12] |= (tag & 0xff00) >> 8; aac_tmp[13] |= (tag & 0x00ff); memcpy(*aac_tag, aac_tmp, 14); From 972b52737473c920172fc5cece6ab6d4ce47511c Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 28 Jan 2019 21:25:05 -0700 Subject: [PATCH 17/34] audio_core: hle: mf: cleanup headers --- src/audio_core/hle/wmf_decoder_utils.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 22d114881..82b6b1a73 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -6,17 +6,14 @@ // AAC decoder related APIs are only available with WIN7+ #define WINVER _WIN32_WINNT_WIN7 -#include +#include +#include +#include #include #include #include #include #include -#include - -#include -#include -#include #include "adts.h" From 4bc6bfd51ff4d8789bea6343a97535cfd3021df7 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 28 Jan 2019 21:47:41 -0700 Subject: [PATCH 18/34] audio_core: hle: mf: re-arrange comments --- src/audio_core/hle/adts.h | 6 ++++-- src/audio_core/hle/adts_reader.cpp | 10 ++++++++-- src/audio_core/hle/wmf_decoder_utils.cpp | 15 +++++++-------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index bd68422be..45d1add1c 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -17,6 +17,8 @@ struct ADTSData { u32 samplerate; }; -u32 ParseADTS(char* buffer, struct ADTSData* out); +u32 ParseADTS(char* buffer, ADTSData* out); + // last two bytes of MF AAC decoder user data -u16 MFGetAACTag(struct ADTSData input); +// see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types +u16 MFGetAACTag(ADTSData input); diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index 1bb3b4c76..a2df93125 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -7,7 +7,7 @@ constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 3 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; -u32 ParseADTS(char* buffer, struct ADTSData* out) { +u32 ParseADTS(char* buffer, ADTSData* out) { u32 tmp = 0; // sync word 0xfff @@ -40,7 +40,13 @@ u32 ParseADTS(char* buffer, struct ADTSData* out) { } // last two bytes of MF AAC decoder user data -u16 MFGetAACTag(struct ADTSData input) { +// 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(ADTSData input) { u16 tag = 0; tag |= input.profile << 11; diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 4568a1147..5c1a3713c 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -134,14 +134,6 @@ bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSD t->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); t->SetGUID(MF_MT_SUBTYPE, audio_format); - // see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types - // and https://docs.microsoft.com/zh-cn/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag - // for the meaning of the byte array below - - // for integrate into a larger project, it is recommended to wrap the parameters into a struct - // and pass that struct into the function - // const UINT8 aac_data[] = { 0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x11, 0x90 - // }; 0: raw aac 1: adts 2: adif 3: latm/laos 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); @@ -211,6 +203,13 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) } ADTSData tmp; + // 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 this 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; From be764e4f88f7f756f31e57ca52ed5b392ce7ba78 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 28 Jan 2019 22:23:57 -0700 Subject: [PATCH 19/34] audio_core: hle: mf: multiple fixes... ... more smart pointers and re-arrange code --- src/audio_core/hle/adts.h | 1 - src/audio_core/hle/adts_reader.cpp | 1 + src/audio_core/hle/wmf_decoder.cpp | 18 +++++----- src/audio_core/hle/wmf_decoder_utils.cpp | 42 +++++++++++++----------- src/audio_core/hle/wmf_decoder_utils.h | 10 +----- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index 45d1add1c..d662c221c 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #pragma once -#include #include "common/common_types.h" struct ADTSData { diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index a2df93125..58201d162 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -1,6 +1,7 @@ // Copyright 2019 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "adts.h" constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 2be505e25..8aa586b8b 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -20,7 +20,7 @@ private: std::optional Decode(const BinaryRequest& request); - int DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams); + MFOutputState DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams); bool initalized = false; bool selected = false; @@ -103,7 +103,7 @@ void WMFDecoder::Impl::Clear() { selected = false; } -int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, +MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams) { MFOutputState output_status = OK; char* output_buffer = nullptr; @@ -138,12 +138,12 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, // in case of "ok" only, just return quickly if (output_status == OK) - return 0; + return OK; // for status = 2, reset MF if (output_status == NEED_RECONFIG) { Clear(); - return -1; + return FATAL_ERROR; } // for status = 3, try again with new buffer @@ -151,12 +151,12 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, continue; if (output_status == NEED_MORE_INPUT) // according to MS document, this is not an error (?!) - return 1; + return NEED_MORE_INPUT; - return -1; // return on other status + return FATAL_ERROR; // return on other status } - return -1; + return FATAL_ERROR; } std::optional WMFDecoder::Impl::Decode(const BinaryRequest& request) { @@ -205,13 +205,13 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ selected = true; } - sample.reset(CreateSample((void*)data, request.size, 1, 0)); + sample = CreateSample((void*)data, request.size, 1, 0); sample->SetUINT32(MFSampleExtension_CleanPoint, 1); while (true) { input_status = SendSample(transform.get(), in_stream_id, sample.get()); - if (DecodingLoop(adts_header, out_streams) < 0) { + if (DecodingLoop(adts_header, out_streams) == FATAL_ERROR) { // 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 diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 5c1a3713c..fc6a8a0e8 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -77,17 +77,19 @@ void MFDeInit(IMFTransform* transform) { CoUninitialize(); } -IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { +unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { HRESULT hr = S_OK; IMFMediaBuffer* buf_tmp = nullptr; unique_mfptr buf; - IMFSample* sample = nullptr; + IMFSample* sample_tmp = nullptr; + unique_mfptr sample; - hr = MFCreateSample(&sample); + hr = MFCreateSample(&sample_tmp); if (FAILED(hr)) { ReportError("Unable to allocate a sample", hr); return nullptr; } + sample.reset(sample_tmp); // Yes, the argument for alignment is the actual alignment - 1 hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf_tmp); if (FAILED(hr)) { @@ -101,12 +103,11 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio // this is actually not a thread-safe lock hr = buf->Lock(&buffer, nullptr, nullptr); if (FAILED(hr)) { - SafeRelease(&sample); - buf.reset(); + ReportError("Unable to lock down MediaBuffer", hr); return nullptr; } - memcpy(buffer, data, len); + std::memcpy(buffer, data, len); buf->SetCurrentLength(len); buf->Unlock(); @@ -114,7 +115,11 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio sample->AddBuffer(buf.get()); hr = sample->SetSampleDuration(duration); - return sample; + if (FAILED(hr)) { + ReportError("Unable to set sample duration, but continuing anyway", hr); + } + + return std::move(sample); } bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, @@ -153,13 +158,15 @@ bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSD bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format) { HRESULT hr = S_OK; UINT32 tmp; - IMFMediaType* t; + IMFMediaType* type; + unique_mfptr t; // If you know what you need and what you are doing, you can specify the condition 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, &t); + hr = transform->GetOutputAvailableType(out_stream_id, i, &type); + t.reset(type); if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) { return true; } @@ -180,7 +187,7 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi hr); return false; } - hr = transform->SetOutputType(out_stream_id, t, 0); + hr = transform->SetOutputType(out_stream_id, t.get(), 0); if (FAILED(hr)) { ReportError("failed to select output types for MFT", hr); return false; @@ -221,8 +228,8 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) tag = MFGetAACTag(tmp); aac_tmp[12] |= (tag & 0xff00) >> 8; aac_tmp[13] |= (tag & 0x00ff); - memcpy(*aac_tag, aac_tmp, 14); - memcpy(output, &tmp, sizeof(ADTSData)); + std::memcpy(*aac_tag, aac_tmp, 14); + std::memcpy(output, &tmp, sizeof(ADTSData)); return 0; } @@ -250,8 +257,6 @@ int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample } // FAILED(hr) } else { hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); - // ffmpeg: Some MFTs (AC3) will send a frame after each drain command (???), so - // ffmpeg: this is required to make draining actually terminate. if (FAILED(hr)) { ReportError("MFT: Failed to drain when processing input", hr); } @@ -264,7 +269,6 @@ std::tuple> ReceiveSample(IMFTransform* t DWORD out_stream_id) { HRESULT hr; MFT_OUTPUT_DATA_BUFFER out_buffers; - IMFSample* sample_tmp = nullptr; MFT_OUTPUT_STREAM_INFO out_info; DWORD status = 0; unique_mfptr sample; @@ -280,16 +284,14 @@ std::tuple> ReceiveSample(IMFTransform* t (out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES); while (true) { - sample = nullptr; status = 0; if (!mft_create_sample) { - sample_tmp = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment); - if (!sample_tmp) { + 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(FATAL_ERROR, std::move(sample)); } - sample.reset(sample_tmp); } out_buffers.dwStreamID = out_stream_id; @@ -353,7 +355,7 @@ int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { } *output = malloc(*len); - memcpy(*output, data, *len); + std::memcpy(*output, data, *len); // if buffer unlock fails, then... whatever, we have already got data buffer->Unlock(); diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 82b6b1a73..da39319c9 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -20,14 +20,6 @@ enum MFOutputState { FATAL_ERROR = -1, OK = 0, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA }; // utility functions -template -void SafeRelease(T** ppT) { - if (*ppT) { - (*ppT)->Release(); - *ppT = nullptr; - } -} - template struct MFRelease { void operator()(T* pointer) const { @@ -44,7 +36,7 @@ void ReportError(std::string msg, HRESULT hr); bool MFCoInit(); bool MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); void MFDeInit(IMFTransform* transform); -IMFSample* CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); +unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, UINT8* user_data, UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); From 452ac7b874cb7fb0c23bbf4fec96b1776ced3c88 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 28 Jan 2019 22:53:55 -0700 Subject: [PATCH 20/34] audio_core: hle: mf: use more enum --- src/audio_core/hle/wmf_decoder.cpp | 6 +++--- src/audio_core/hle/wmf_decoder_utils.cpp | 12 ++++++------ src/audio_core/hle/wmf_decoder_utils.h | 8 +++++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 8aa586b8b..eb04ee34b 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -104,7 +104,7 @@ void WMFDecoder::Impl::Clear() { } MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, - std::array, 2>& out_streams) { + std::array, 2>& out_streams) { MFOutputState output_status = OK; char* output_buffer = nullptr; DWORD output_len = 0; @@ -184,7 +184,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ unique_mfptr sample; ADTSData adts_header; char* aac_tag = (char*)calloc(1, 14); - int input_status = 0; + MFInputState input_status = INPUT_OK; if (DetectMediaType((char*)data, request.size, &adts_header, &aac_tag) != 0) { LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); @@ -215,7 +215,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ // 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 == 1) { + if (input_status == TRY_AGAIN) { // try again continue; } diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index fc6a8a0e8..a74e1bf64 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -234,26 +234,26 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) } void MFFlush(IMFTransform* transform) { - HRESULT hr = (transform)->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); + 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); + hr = transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0); if (FAILED(hr)) { ReportError("Failed to end streaming for MFT", hr); } } -int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample) { +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 1; // try again + return TRY_AGAIN; // try again } else if (FAILED(hr)) { ReportError("MFT: Failed to process input", hr); - return -1; + return INPUT_ERROR; } // FAILED(hr) } else { hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); @@ -262,7 +262,7 @@ int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample } } - return 0; + return INPUT_OK; } std::tuple> ReceiveSample(IMFTransform* transform, diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index da39319c9..f50264113 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -17,7 +17,8 @@ #include "adts.h" -enum MFOutputState { FATAL_ERROR = -1, OK = 0, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA }; +enum MFOutputState { FATAL_ERROR, OK, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA }; +enum MFInputState { INPUT_ERROR, INPUT_OK, TRY_AGAIN }; // utility functions template @@ -36,7 +37,8 @@ void ReportError(std::string msg, HRESULT hr); bool MFCoInit(); bool MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); void MFDeInit(IMFTransform* transform); -unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); +unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, + LONGLONG duration = 0); bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, UINT8* user_data, UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); @@ -44,7 +46,7 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag); bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format = MFAudioFormat_PCM); void MFFlush(IMFTransform* transform); -int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); +MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); std::tuple> ReceiveSample(IMFTransform* transform, DWORD out_stream_id); int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len); From 633f02b9b292c47685fd637f9a5d112ce1fda788 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Mon, 28 Jan 2019 23:36:24 -0700 Subject: [PATCH 21/34] audio_core: hle: mf: avoid name conflicts --- src/audio_core/hle/wmf_decoder.cpp | 2 +- src/audio_core/hle/wmf_decoder_utils.cpp | 2 +- src/audio_core/hle/wmf_decoder_utils.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index eb04ee34b..cd5cc77d6 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -215,7 +215,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ // 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 == TRY_AGAIN) { + if (input_status == NOT_ACCEPTED) { // try again continue; } diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index a74e1bf64..7f809e679 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -250,7 +250,7 @@ MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* if (in_sample) { hr = transform->ProcessInput(in_stream_id, in_sample, 0); if (hr == MF_E_NOTACCEPTING) { - return TRY_AGAIN; // try again + return NOT_ACCEPTED; // try again } else if (FAILED(hr)) { ReportError("MFT: Failed to process input", hr); return INPUT_ERROR; diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index f50264113..2b9c54543 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -18,7 +18,7 @@ #include "adts.h" enum MFOutputState { FATAL_ERROR, OK, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA }; -enum MFInputState { INPUT_ERROR, INPUT_OK, TRY_AGAIN }; +enum MFInputState { INPUT_ERROR, INPUT_OK, NOT_ACCEPTED }; // utility functions template From 26b3b417886e5896e402031a61e07f6aa3b5d1e7 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sun, 3 Feb 2019 17:29:59 -0700 Subject: [PATCH 22/34] audio_core: hle: mf: wrap enum in class --- src/audio_core/hle/wmf_decoder.cpp | 28 ++++++++++++------------ src/audio_core/hle/wmf_decoder_utils.cpp | 20 ++++++++--------- src/audio_core/hle/wmf_decoder_utils.h | 8 +++---- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index cd5cc77d6..a2025a85e 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -105,7 +105,7 @@ void WMFDecoder::Impl::Clear() { MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams) { - MFOutputState output_status = OK; + MFOutputState output_status = MFOutputState::OK; char* output_buffer = nullptr; DWORD output_len = 0; DWORD tmp = 0; @@ -117,7 +117,7 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, auto [output_status, output] = ReceiveSample(transform.get(), out_stream_id); // 0 -> okay; 3 -> okay but more data available (buffer too small) - if (output_status == OK || output_status == HAVE_MORE_DATA) { + if (output_status == MFOutputState::OK || output_status == MFOutputState::HaveMoreData) { CopySampleToBuffer(output.get(), (void**)&output_buffer, &output_len); // the following was taken from ffmpeg version of the decoder @@ -137,26 +137,26 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, } // in case of "ok" only, just return quickly - if (output_status == OK) - return OK; + if (output_status == MFOutputState::OK) + return MFOutputState::OK; // for status = 2, reset MF - if (output_status == NEED_RECONFIG) { + if (output_status == MFOutputState::NeedReconfig) { Clear(); - return FATAL_ERROR; + return MFOutputState::FatalError; } // for status = 3, try again with new buffer - if (output_status == HAVE_MORE_DATA) + if (output_status == MFOutputState::HaveMoreData) continue; - if (output_status == NEED_MORE_INPUT) // according to MS document, this is not an error (?!) - return NEED_MORE_INPUT; + if (output_status == MFOutputState::NeedMoreInput) // according to MS document, this is not an error (?!) + return MFOutputState::NeedMoreInput; - return FATAL_ERROR; // return on other status + return MFOutputState::FatalError; // return on other status } - return FATAL_ERROR; + return MFOutputState::FatalError; } std::optional WMFDecoder::Impl::Decode(const BinaryRequest& request) { @@ -184,7 +184,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ unique_mfptr sample; ADTSData adts_header; char* aac_tag = (char*)calloc(1, 14); - MFInputState input_status = INPUT_OK; + MFInputState input_status = MFInputState::OK; if (DetectMediaType((char*)data, request.size, &adts_header, &aac_tag) != 0) { LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); @@ -211,11 +211,11 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ while (true) { input_status = SendSample(transform.get(), in_stream_id, sample.get()); - if (DecodingLoop(adts_header, out_streams) == FATAL_ERROR) { + if (DecodingLoop(adts_header, out_streams) == 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 == NOT_ACCEPTED) { + if (input_status == MFInputState::NotAccepted) { // try again continue; } diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 7f809e679..26d1905a7 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -250,10 +250,10 @@ MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* if (in_sample) { hr = transform->ProcessInput(in_stream_id, in_sample, 0); if (hr == MF_E_NOTACCEPTING) { - return NOT_ACCEPTED; // try again + return MFInputState::NotAccepted; // try again } else if (FAILED(hr)) { ReportError("MFT: Failed to process input", hr); - return INPUT_ERROR; + return MFInputState::FatalError; } // FAILED(hr) } else { hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); @@ -262,7 +262,7 @@ MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* } } - return INPUT_OK; + return MFInputState::OK; } std::tuple> ReceiveSample(IMFTransform* transform, @@ -278,7 +278,7 @@ std::tuple> ReceiveSample(IMFTransform* t if (FAILED(hr)) { ReportError("MFT: Failed to get stream info", hr); - return std::make_tuple(FATAL_ERROR, std::move(sample)); + 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); @@ -290,7 +290,7 @@ std::tuple> ReceiveSample(IMFTransform* t 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(FATAL_ERROR, std::move(sample)); + return std::make_tuple(MFOutputState::FatalError, std::move(sample)); } } @@ -305,12 +305,12 @@ std::tuple> ReceiveSample(IMFTransform* t if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { // Most likely reasons: data corrupted; your actions not expected by MFT - return std::make_tuple(NEED_MORE_INPUT, std::move(sample)); + 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(NEED_RECONFIG, std::move(sample)); + return std::make_tuple(MFOutputState::NeedReconfig, std::move(sample)); } break; @@ -318,15 +318,15 @@ std::tuple> ReceiveSample(IMFTransform* t if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { // this status is also unreliable but whatever - return std::make_tuple(HAVE_MORE_DATA, std::move(sample)); + return std::make_tuple(MFOutputState::HaveMoreData, std::move(sample)); } if (out_buffers.pSample == nullptr) { ReportError("MFT: decoding failure", hr); - return std::make_tuple(FATAL_ERROR, std::move(sample)); + return std::make_tuple(MFOutputState::FatalError, std::move(sample)); } - return std::make_tuple(OK, std::move(sample)); + return std::make_tuple(MFOutputState::OK, std::move(sample)); } int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 2b9c54543..07ed76662 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -6,7 +6,6 @@ // AAC decoder related APIs are only available with WIN7+ #define WINVER _WIN32_WINNT_WIN7 -#include #include #include #include @@ -17,10 +16,10 @@ #include "adts.h" -enum MFOutputState { FATAL_ERROR, OK, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA }; -enum MFInputState { INPUT_ERROR, INPUT_OK, NOT_ACCEPTED }; +enum class MFOutputState { FatalError, OK, NeedMoreInput, NeedReconfig, HaveMoreData }; +enum class MFInputState { FatalError, OK, NotAccepted }; -// utility functions +// utility functions / templates template struct MFRelease { void operator()(T* pointer) const { @@ -28,6 +27,7 @@ struct MFRelease { }; }; +// wrapper facilities for dealing with pointers template using unique_mfptr = std::unique_ptr>; From 25fa10327ba46d15e6d796591a16fca8a52ea2eb Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sun, 3 Feb 2019 22:42:18 -0700 Subject: [PATCH 23/34] audio_core: hle: mf: use object proxy --- src/audio_core/hle/wmf_decoder.cpp | 10 ++----- src/audio_core/hle/wmf_decoder_utils.cpp | 36 ++++++++++-------------- src/audio_core/hle/wmf_decoder_utils.h | 23 +++++++++++++++ 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index a2025a85e..7440049f4 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -70,15 +70,13 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r } BinaryResponse response; - IMFTransform* tmp = nullptr; std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; - if (!MFDecoderInit(&tmp)) { + if (!MFDecoderInit(Amp(transform))) { LOG_CRITICAL(Audio_DSP, "Can't init decoder"); return response; } - transform.reset(tmp); HRESULT hr = transform->GetStreamIDs(1, &in_stream_id, 1, &out_stream_id); if (hr == E_NOTIMPL) { @@ -108,9 +106,6 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, MFOutputState output_status = MFOutputState::OK; char* output_buffer = nullptr; DWORD output_len = 0; - DWORD tmp = 0; - // IMFSample* output_tmp = nullptr; - IMFMediaBuffer* mdbuf = nullptr; unique_mfptr output; while (true) { @@ -150,7 +145,8 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, if (output_status == MFOutputState::HaveMoreData) continue; - if (output_status == MFOutputState::NeedMoreInput) // according to MS document, this is not an error (?!) + // according to MS document, this is not an error (?!) + if (output_status == MFOutputState::NeedMoreInput) return MFOutputState::NeedMoreInput; return MFOutputState::FatalError; // return on other status diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 26d1905a7..8ecfae1af 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -15,6 +15,7 @@ void ReportError(std::string msg, HRESULT hr) { 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), (LPSTR)&err, 0, nullptr); if (err != nullptr) { LOG_CRITICAL(Audio_DSP, "{}: {}", msg, err); @@ -79,24 +80,20 @@ void MFDeInit(IMFTransform* transform) { unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { HRESULT hr = S_OK; - IMFMediaBuffer* buf_tmp = nullptr; unique_mfptr buf; - IMFSample* sample_tmp = nullptr; unique_mfptr sample; - hr = MFCreateSample(&sample_tmp); + hr = MFCreateSample(Amp(sample)); if (FAILED(hr)) { ReportError("Unable to allocate a sample", hr); return nullptr; } - sample.reset(sample_tmp); // Yes, the argument for alignment is the actual alignment - 1 - hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf_tmp); + hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, Amp(buf)); if (FAILED(hr)) { ReportError("Unable to allocate a memory buffer for sample", hr); return nullptr; } - buf.reset(buf_tmp); if (data) { BYTE* buffer; // lock the MediaBuffer @@ -116,6 +113,7 @@ unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment, LON 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); } @@ -125,11 +123,11 @@ unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment, LON bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, UINT8* user_data, UINT32 user_data_len, GUID audio_format) { HRESULT hr = S_OK; - IMFMediaType* t; + unique_mfptr 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(&t); + hr = MFCreateMediaType(Amp(t)); if (FAILED(hr)) { ReportError("Unable to create an empty MediaType", hr); return false; @@ -146,7 +144,7 @@ bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSD 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, 0); + hr = transform->SetInputType(in_stream_id, t.get(), 0); if (FAILED(hr)) { ReportError("failed to select input types for MFT", hr); return false; @@ -158,15 +156,13 @@ bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSD bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format) { HRESULT hr = S_OK; UINT32 tmp; - IMFMediaType* type; - unique_mfptr t; + unique_mfptr type; - // If you know what you need and what you are doing, you can specify the condition instead of + // 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, &type); - t.reset(type); + hr = transform->GetOutputAvailableType(out_stream_id, i, Amp(type)); if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) { return true; } @@ -175,19 +171,19 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi return false; } - hr = t->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp); + hr = type->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp); if (FAILED(hr)) continue; // select PCM-16 format if (tmp == 32) { - hr = t->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1); + 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, t.get(), 0); + hr = transform->SetOutputType(out_stream_id, type.get(), 0); if (FAILED(hr)) { ReportError("failed to select output types for MFT", hr); return false; @@ -331,7 +327,6 @@ std::tuple> ReceiveSample(IMFTransform* t int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { unique_mfptr buffer; - IMFMediaBuffer* tmp; HRESULT hr = S_OK; BYTE* data; @@ -341,14 +336,13 @@ int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { return -1; } - hr = sample->ConvertToContiguousBuffer(&tmp); + hr = sample->ConvertToContiguousBuffer(Amp(buffer)); if (FAILED(hr)) { ReportError("Failed to get sample buffer", hr); return -1; } - buffer.reset(tmp); - hr = tmp->Lock(&data, nullptr, nullptr); + hr = buffer->Lock(&data, nullptr, nullptr); if (FAILED(hr)) { ReportError("Failed to lock the buffer", hr); return -1; diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 07ed76662..79514c19c 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -31,6 +31,29 @@ struct MFRelease { template using unique_mfptr = std::unique_ptr>; +template +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; +}; + +template +auto Amp(SmartPtr& smart_ptr) { + return AmpImpl(smart_ptr); +} + +// convient function for formatting error messages void ReportError(std::string msg, HRESULT hr); // exported functions From 6332e570696762cefe8020e279fedd887b5bc5e3 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Tue, 5 Feb 2019 21:13:40 -0700 Subject: [PATCH 24/34] audio_core: hle: mf: return values as std::optional --- src/audio_core/hle/adts.h | 2 +- src/audio_core/hle/adts_reader.cpp | 27 +++++++++++---------- src/audio_core/hle/wmf_decoder.cpp | 13 ++++------ src/audio_core/hle/wmf_decoder_utils.cpp | 30 ++++++++++++++++-------- src/audio_core/hle/wmf_decoder_utils.h | 6 +++-- 5 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index d662c221c..fd11555a9 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -16,7 +16,7 @@ struct ADTSData { u32 samplerate; }; -u32 ParseADTS(char* buffer, ADTSData* out); +ADTSData ParseADTS(char* 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 diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index 58201d162..42ac4afa2 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -8,36 +8,39 @@ constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 3 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; -u32 ParseADTS(char* buffer, ADTSData* out) { +ADTSData ParseADTS(char* buffer) { u32 tmp = 0; + ADTSData out; // sync word 0xfff tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); - if ((tmp & 0xffff) != 0xfff0) - return 0; - out->MPEG2 = (buffer[1] >> 3) & 0x1; + if ((tmp & 0xffff) != 0xfff0) { + out.length = 0; + return out; + } + out.MPEG2 = (buffer[1] >> 3) & 0x1; // bit 17 to 18 - out->profile = (buffer[2] >> 6) + 1; + out.profile = (buffer[2] >> 6) + 1; // bit 19 to 22 tmp = (buffer[2] >> 2) & 0xf; - out->samplerate_idx = tmp; - out->samplerate = (tmp > 15) ? 0 : freq_table[tmp]; + out.samplerate_idx = tmp; + out.samplerate = (tmp > 15) ? 0 : freq_table[tmp]; // bit 24 to 26 tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3); - out->channel_idx = tmp; - out->channels = (tmp > 7) ? 0 : channel_table[tmp]; + out.channel_idx = tmp; + out.channels = (tmp > 7) ? 0 : channel_table[tmp]; // bit 55 to 56 - out->framecount = (buffer[6] & 0x3) + 1; + out.framecount = (buffer[6] & 0x3) + 1; // bit 31 to 43 tmp = (buffer[3] & 0x3) << 11; tmp |= (buffer[4] << 3) & 0x7f8; tmp |= (buffer[5] >> 5) & 0x7; - out->length = tmp; + out.length = tmp; - return tmp; + return out; } // last two bytes of MF AAC decoder user data diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 7440049f4..2d448a216 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -104,8 +104,7 @@ void WMFDecoder::Impl::Clear() { MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams) { MFOutputState output_status = MFOutputState::OK; - char* output_buffer = nullptr; - DWORD output_len = 0; + std::optional> output_buffer; unique_mfptr output; while (true) { @@ -113,22 +112,18 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, // 0 -> okay; 3 -> okay but more data available (buffer too small) if (output_status == MFOutputState::OK || output_status == MFOutputState::HaveMoreData) { - CopySampleToBuffer(output.get(), (void**)&output_buffer, &output_len); + output_buffer = CopySampleToBuffer(output.get()); // the following was taken from ffmpeg version of the decoder f32 val_f32; - for (size_t i = 0; i < output_len;) { + for (size_t i = 0; i < output_buffer->size(); i++) { for (std::size_t channel = 0; channel < adts_header.channels; channel++) { - std::memcpy(&val_f32, output_buffer + i, sizeof(val_f32)); + val_f32 = output_buffer->at(i); s16 val = static_cast(0x7FFF * val_f32); out_streams[channel].push_back(val & 0xFF); out_streams[channel].push_back(val >> 8); - i += sizeof(val_f32); } } - - if (output_buffer) - free(output_buffer); } // in case of "ok" only, just return quickly diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 8ecfae1af..8f0147a2c 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -25,6 +25,12 @@ void ReportError(std::string msg, HRESULT hr) { bool MFCoInit() { 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); + return false; + } // lite startup is faster and all what we need is included hr = MFStartup(MF_VERSION, MFSTARTUP_LITE); @@ -216,8 +222,8 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; uint16_t tag = 0; - uint32_t result = ParseADTS(buffer, &tmp); - if (result == 0) { + tmp = ParseADTS(buffer); + if (tmp.length == 0) { return -1; } @@ -325,33 +331,37 @@ std::tuple> ReceiveSample(IMFTransform* t return std::make_tuple(MFOutputState::OK, std::move(sample)); } -int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) { +std::optional> CopySampleToBuffer(IMFSample* sample) { unique_mfptr buffer; HRESULT hr = S_OK; + std::optional> output; + std::vector output_buffer; BYTE* data; + DWORD len = 0; - hr = sample->GetTotalLength(len); + hr = sample->GetTotalLength(&len); if (FAILED(hr)) { ReportError("Failed to get the length of sample buffer", hr); - return -1; + return std::nullopt; } hr = sample->ConvertToContiguousBuffer(Amp(buffer)); if (FAILED(hr)) { ReportError("Failed to get sample buffer", hr); - return -1; + return std::nullopt; } hr = buffer->Lock(&data, nullptr, nullptr); if (FAILED(hr)) { ReportError("Failed to lock the buffer", hr); - return -1; + return std::nullopt; } - *output = malloc(*len); - std::memcpy(*output, data, *len); + 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 0; + return output; } diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 79514c19c..187ec68d7 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -6,8 +6,10 @@ // AAC decoder related APIs are only available with WIN7+ #define WINVER _WIN32_WINNT_WIN7 +#include #include #include +#include #include #include #include @@ -45,7 +47,7 @@ public: private: SmartPtr& smart_ptr; - RawPtr raw_ptr; + RawPtr raw_ptr = nullptr; }; template @@ -72,4 +74,4 @@ void MFFlush(IMFTransform* transform); MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); std::tuple> ReceiveSample(IMFTransform* transform, DWORD out_stream_id); -int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len); +std::optional> CopySampleToBuffer(IMFSample* sample); From c91f5029ffa817a99860d2406152df8b6288c8ca Mon Sep 17 00:00:00 2001 From: liushuyu Date: Fri, 8 Feb 2019 21:22:14 -0700 Subject: [PATCH 25/34] audio_core: hle: mf: fix a regression... ... where the sample copying logic is incorrect due to the wrong usage of `std::array` --- src/audio_core/hle/adts.h | 4 ++-- src/audio_core/hle/adts_reader.cpp | 4 ++-- src/audio_core/hle/wmf_decoder.cpp | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index fd11555a9..11c736986 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -16,8 +16,8 @@ struct ADTSData { u32 samplerate; }; -ADTSData ParseADTS(char* buffer); +ADTSData ParseADTS(const char* 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(ADTSData input); +u16 MFGetAACTag(const ADTSData input); diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index 42ac4afa2..93118f887 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -8,7 +8,7 @@ constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 3 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; -ADTSData ParseADTS(char* buffer) { +ADTSData ParseADTS(const char* buffer) { u32 tmp = 0; ADTSData out; @@ -50,7 +50,7 @@ ADTSData ParseADTS(char* buffer) { // Frame length flag (1 bit) // Depends on core coder (1 bit) // Extension flag (1 bit) -u16 MFGetAACTag(ADTSData input) { +u16 MFGetAACTag(const ADTSData input) { u16 tag = 0; tag |= input.profile << 11; diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 2d448a216..06d458899 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -116,12 +116,14 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, // the following was taken from ffmpeg version of the decoder f32 val_f32; - for (size_t i = 0; i < output_buffer->size(); i++) { + for (size_t i = 0; i < output_buffer->size(); ) { for (std::size_t channel = 0; channel < adts_header.channels; channel++) { val_f32 = output_buffer->at(i); s16 val = static_cast(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++; } } } From 168f2ee79a325c8a125eaeac41f28b8d8bd68f3d Mon Sep 17 00:00:00 2001 From: liushuyu Date: Fri, 8 Feb 2019 21:42:50 -0700 Subject: [PATCH 26/34] audio_core: mf: make initialize function return smart pointer --- src/audio_core/hle/wmf_decoder.cpp | 5 +++-- src/audio_core/hle/wmf_decoder_utils.cpp | 17 ++++++++++------- src/audio_core/hle/wmf_decoder_utils.h | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 06d458899..cb981513a 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -72,8 +72,9 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r BinaryResponse response; std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; + transform = MFDecoderInit(); - if (!MFDecoderInit(Amp(transform))) { + if (transform == nullptr) { LOG_CRITICAL(Audio_DSP, "Can't init decoder"); return response; } @@ -116,7 +117,7 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, // the following was taken from ffmpeg version of the decoder f32 val_f32; - for (size_t i = 0; i < output_buffer->size(); ) { + for (size_t i = 0; i < output_buffer->size();) { for (std::size_t channel = 0; channel < adts_header.channels; channel++) { val_f32 = output_buffer->at(i); s16 val = static_cast(0x7FFF * val_f32); diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 8f0147a2c..dbdaa1d0e 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -45,11 +45,12 @@ bool MFCoInit() { return true; } -bool MFDecoderInit(IMFTransform** transform, GUID audio_format) { +unique_mfptr MFDecoderInit(GUID audio_format) { HRESULT hr = S_OK; MFT_REGISTER_TYPE_INFO reg = {0}; GUID category = MFT_CATEGORY_AUDIO_DECODER; IMFActivate** activate; + unique_mfptr transform; UINT32 num_activate; reg.guidMajorType = MFMediaType_Audio; @@ -61,22 +62,24 @@ bool MFDecoderInit(IMFTransform** transform, GUID audio_format) { if (FAILED(hr) || num_activate < 1) { ReportError("Failed to enumerate decoders", hr); CoTaskMemFree(activate); - return false; + 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, (void**)transform); + hr = activate[n]->ActivateObject( + IID_IMFTransform, + reinterpret_cast(static_cast(Amp(transform)))); if (FAILED(hr)) - *transform = nullptr; + transform = nullptr; activate[n]->Release(); } - if (*transform == nullptr) { + if (transform == nullptr) { ReportError("Failed to initialize MFT", hr); CoTaskMemFree(activate); - return false; + return nullptr; } CoTaskMemFree(activate); - return true; + return std::move(transform); } void MFDeInit(IMFTransform* transform) { diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 187ec68d7..2c4ae029a 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -60,7 +60,7 @@ void ReportError(std::string msg, HRESULT hr); // exported functions bool MFCoInit(); -bool MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC); +unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); void MFDeInit(IMFTransform* transform); unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); From ab1f47ed15718d3c4fd59981b35082d6e8075000 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Fri, 8 Feb 2019 22:23:40 -0700 Subject: [PATCH 27/34] audio_core: hle: mf: make DetectMediaType return a struct --- src/audio_core/hle/wmf_decoder.cpp | 16 ++++++++-------- src/audio_core/hle/wmf_decoder_utils.cpp | 15 ++++++++------- src/audio_core/hle/wmf_decoder_utils.h | 8 +++++++- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index cb981513a..3b4087bf7 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -163,7 +163,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ if (!initalized) { LOG_DEBUG(Audio_DSP, "Decoder not initalized"); - // This is a hack to continue games that are not compiled with the aac codec + // This is a hack to continue games when decoder failed to initialize return response; } @@ -176,21 +176,21 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ std::array, 2> out_streams; unique_mfptr sample; - ADTSData adts_header; - char* aac_tag = (char*)calloc(1, 14); MFInputState input_status = MFInputState::OK; + std::optional adts_meta = DetectMediaType((char*)data, request.size); - if (DetectMediaType((char*)data, request.size, &adts_header, &aac_tag) != 0) { + if (!adts_meta) { LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); return response; } - response.num_channels = adts_header.channels; + response.num_channels = adts_meta->ADTSHeader.channels; if (!selected) { LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}", - adts_header.channels, adts_header.samplerate); - SelectInputMediaType(transform.get(), in_stream_id, adts_header, (UINT8*)aac_tag, 14); + 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 @@ -205,7 +205,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ while (true) { input_status = SendSample(transform.get(), in_stream_id, sample.get()); - if (DecodingLoop(adts_header, out_streams) == MFOutputState::FatalError) { + if (DecodingLoop(adts_meta->ADTSHeader, out_streams) == 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 diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index dbdaa1d0e..c0c9c5744 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -209,17 +209,18 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi return false; } -int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) { +std::optional DetectMediaType(char* buffer, size_t len) { if (len < 7) { - return -1; + return std::nullopt; } 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 this will lead to messier code + // 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}; @@ -227,15 +228,15 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) tmp = ParseADTS(buffer); if (tmp.length == 0) { - return -1; + return std::nullopt; } tag = MFGetAACTag(tmp); aac_tmp[12] |= (tag & 0xff00) >> 8; aac_tmp[13] |= (tag & 0x00ff); - std::memcpy(*aac_tag, aac_tmp, 14); - std::memcpy(output, &tmp, sizeof(ADTSData)); - return 0; + std::memcpy(&(result.ADTSHeader), &tmp, sizeof(ADTSData)); + std::memcpy(&(result.AACTag), aac_tmp, 14); + return result; } void MFFlush(IMFTransform* transform) { diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 2c4ae029a..a5e5d0154 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -58,6 +58,12 @@ auto Amp(SmartPtr& 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 { + ADTSData ADTSHeader; + u8 AACTag[14]; +}; + // exported functions bool MFCoInit(); unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); @@ -67,7 +73,7 @@ unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, UINT8* user_data, UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); -int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag); +std::optional DetectMediaType(char* buffer, size_t len); bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format = MFAudioFormat_PCM); void MFFlush(IMFTransform* transform); From 6281660844255f9680a04bdaacd5b9753764bb06 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 9 Feb 2019 11:59:00 -0700 Subject: [PATCH 28/34] audio_core: hle: mf: address yet another batch of reviews --- src/audio_core/hle/adts.h | 2 +- src/audio_core/hle/adts_reader.cpp | 2 +- src/audio_core/hle/wmf_decoder.cpp | 22 +++++++++-------- src/audio_core/hle/wmf_decoder_utils.cpp | 30 +++++++++++++----------- src/audio_core/hle/wmf_decoder_utils.h | 14 ++++++++--- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h index 11c736986..9aba09fc3 100644 --- a/src/audio_core/hle/adts.h +++ b/src/audio_core/hle/adts.h @@ -20,4 +20,4 @@ ADTSData ParseADTS(const char* 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); +u16 MFGetAACTag(const ADTSData& input); diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp index 93118f887..e70c32a3b 100644 --- a/src/audio_core/hle/adts_reader.cpp +++ b/src/audio_core/hle/adts_reader.cpp @@ -50,7 +50,7 @@ ADTSData ParseADTS(const char* buffer) { // Frame length flag (1 bit) // Depends on core coder (1 bit) // Extension flag (1 bit) -u16 MFGetAACTag(const ADTSData input) { +u16 MFGetAACTag(const ADTSData& input) { u16 tag = 0; tag |= input.profile << 11; diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 3b4087bf7..44a250c33 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -22,7 +22,7 @@ private: MFOutputState DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams); - bool initalized = false; + bool initialized = false; bool selected = false; Memory::MemorySystem& memory; @@ -36,7 +36,9 @@ WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { MFCoInit(); } -WMFDecoder::Impl::~Impl() = default; +WMFDecoder::Impl::~Impl() { + Clear(); +} std::optional WMFDecoder::Impl::ProcessRequest(const BinaryRequest& request) { if (request.codec != DecoderCodec::AAC) { @@ -65,7 +67,7 @@ std::optional WMFDecoder::Impl::ProcessRequest(const BinaryReque } std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& request) { - if (initalized) { + if (initialized) { Clear(); } @@ -89,16 +91,16 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r return response; } - initalized = true; + initialized = true; return response; } void WMFDecoder::Impl::Clear() { - if (initalized) { + if (initialized) { MFFlush(transform.get()); - MFDeInit(transform.get()); + MFDestroy(); } - initalized = false; + initialized = false; selected = false; } @@ -117,7 +119,7 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, // the following was taken from ffmpeg version of the decoder f32 val_f32; - for (size_t i = 0; i < output_buffer->size();) { + for (std::size_t i = 0; i < output_buffer->size();) { for (std::size_t channel = 0; channel < adts_header.channels; channel++) { val_f32 = output_buffer->at(i); s16 val = static_cast(0x7FFF * val_f32); @@ -161,8 +163,8 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ response.num_channels = 2; response.num_samples = 1024; - if (!initalized) { - LOG_DEBUG(Audio_DSP, "Decoder not initalized"); + if (!initialized) { + LOG_DEBUG(Audio_DSP, "Decoder not initialized"); // This is a hack to continue games when decoder failed to initialize return response; } diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index c0c9c5744..7dec97abf 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -2,6 +2,7 @@ // 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" // utility functions @@ -9,16 +10,17 @@ void ReportError(std::string msg, HRESULT hr) { if (SUCCEEDED(hr)) { return; } - LPSTR err; - FormatMessage(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), (LPSTR)&err, 0, nullptr); + 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, err); + LOG_CRITICAL(Audio_DSP, "{}: {}", msg, Common::UTF16ToUTF8(err)); + LocalFree(err); } LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr); } @@ -82,8 +84,8 @@ unique_mfptr MFDecoderInit(GUID audio_format) { return std::move(transform); } -void MFDeInit(IMFTransform* transform) { - MFShutdownObject(transform); +void MFDestroy() { + MFShutdown(); CoUninitialize(); } @@ -126,11 +128,11 @@ unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment, LON ReportError("Unable to set sample duration, but continuing anyway", hr); } - return std::move(sample); + return sample; } bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, - UINT8* user_data, UINT32 user_data_len, GUID audio_format) { + const UINT8* user_data, UINT32 user_data_len, GUID audio_format) { HRESULT hr = S_OK; unique_mfptr t; @@ -209,7 +211,7 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi return false; } -std::optional DetectMediaType(char* buffer, size_t len) { +std::optional DetectMediaType(char* buffer, std::size_t len) { if (len < 7) { return std::nullopt; } diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index a5e5d0154..ac114edc9 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -29,6 +29,14 @@ struct MFRelease { }; }; +template <> +struct MFRelease { + void operator()(IMFTransform* pointer) const { + MFShutdownObject(pointer); + pointer->Release(); + }; +}; + // wrapper facilities for dealing with pointers template using unique_mfptr = std::unique_ptr>; @@ -67,13 +75,13 @@ struct ADTSMeta { // exported functions bool MFCoInit(); unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); -void MFDeInit(IMFTransform* transform); +void MFDestroy(); unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, - UINT8* user_data, UINT32 user_data_len, + const UINT8* user_data, UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); -std::optional DetectMediaType(char* buffer, size_t len); +std::optional DetectMediaType(char* buffer, std::size_t len); bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format = MFAudioFormat_PCM); void MFFlush(IMFTransform* transform); From 01e0902fa42d35e78faacd15dd68ee40aea66d56 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 9 Feb 2019 13:40:09 -0700 Subject: [PATCH 29/34] audio_core: hle: mf: fix a memory accessing issue --- src/audio_core/hle/wmf_decoder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 44a250c33..c3c8d6f15 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -98,6 +98,9 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r void WMFDecoder::Impl::Clear() { if (initialized) { MFFlush(transform.get()); + // delete the transform object before shutting down MF + // otherwise access violation will occur + transform.reset(); MFDestroy(); } initialized = false; From f0e041e27a849e5ec2091d347ac9055ec79eff45 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 9 Feb 2019 16:49:48 -0700 Subject: [PATCH 30/34] audio_core: hle: mf: correctly handle stream change --- src/audio_core/hle/wmf_decoder.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index c3c8d6f15..4d649349a 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -32,9 +32,7 @@ private: DWORD out_stream_id = 0; }; -WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { - MFCoInit(); -} +WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) {} WMFDecoder::Impl::~Impl() { Clear(); @@ -67,8 +65,8 @@ std::optional WMFDecoder::Impl::ProcessRequest(const BinaryReque } std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& request) { - if (initialized) { - Clear(); + if (!initialized) { + MFCoInit(); } BinaryResponse response; @@ -141,7 +139,7 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, // for status = 2, reset MF if (output_status == MFOutputState::NeedReconfig) { Clear(); - return MFOutputState::FatalError; + return MFOutputState::NeedReconfig; } // for status = 3, try again with new buffer @@ -182,6 +180,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ std::array, 2> out_streams; unique_mfptr sample; MFInputState input_status = MFInputState::OK; + MFOutputState output_status = MFOutputState::OK; std::optional adts_meta = DetectMediaType((char*)data, request.size); if (!adts_meta) { @@ -209,8 +208,9 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ while (true) { input_status = SendSample(transform.get(), in_stream_id, sample.get()); + output_status = DecodingLoop(adts_meta->ADTSHeader, out_streams); - if (DecodingLoop(adts_meta->ADTSHeader, out_streams) == MFOutputState::FatalError) { + 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 @@ -221,6 +221,11 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ LOG_ERROR(Audio_DSP, "Errors occurred when receiving output"); return response; + } else if (output_status == MFOutputState::NeedReconfig) { + // re-initialize the whole thing to adapt to new parameters + this->Initalize(request); + // decode again + return this->Decode(request); } break; // jump out of the loop if at least we don't have obvious issues From 6737fabb3c924dbe23d838842dbf29b77f3279db Mon Sep 17 00:00:00 2001 From: B3N30 Date: Mon, 11 Feb 2019 22:49:27 +0100 Subject: [PATCH 31/34] update ffmpeg path to new version --- .travis/macos/upload.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis/macos/upload.sh b/.travis/macos/upload.sh index 26fa9c15c..9a81ac989 100755 --- a/.travis/macos/upload.sh +++ b/.travis/macos/upload.sh @@ -21,10 +21,10 @@ $(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/citra-qt.app" -executable= dylibbundler -b -x "${REV_NAME}/citra" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/" # TODO(merry): Figure out why these libraries are not automatically processed -install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib "${REV_NAME}/citra-qt.app/Contents/Frameworks/libavcodec.58.dylib" -install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib "${REV_NAME}/citra-qt.app/Contents/Frameworks/libswresample.3.dylib" -install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_1/lib/libavutil.56.dylib @executable_path/libs/libavutil.56.dylib "${REV_NAME}/libs/libavcodec.58.dylib" -install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_1/lib/libavutil.56.dylib @executable_path/libs/libavutil.56.dylib "${REV_NAME}/libs/libswresample.3.dylib" +install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_6/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib "${REV_NAME}/citra-qt.app/Contents/Frameworks/libavcodec.58.dylib" +install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_6/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib "${REV_NAME}/citra-qt.app/Contents/Frameworks/libswresample.3.dylib" +install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_6/lib/libavutil.56.dylib @executable_path/libs/libavutil.56.dylib "${REV_NAME}/libs/libavcodec.58.dylib" +install_name_tool -change /usr/local/Cellar/ffmpeg/4.1_6/lib/libavutil.56.dylib @executable_path/libs/libavutil.56.dylib "${REV_NAME}/libs/libswresample.3.dylib" install_name_tool -change /usr/local/Cellar/libvorbis/1.3.6/lib/libvorbis.0.dylib @executable_path/libs/libavutil.56.dylib "${REV_NAME}/libs/libvorbisenc.2.dylib" # Make the citra-qt.app application launch a debugging terminal. From 6178cc08b74055e8c9f821a9805c96dda4566ea4 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Wed, 13 Feb 2019 13:49:41 -0700 Subject: [PATCH 32/34] audio_core: hle: mf: conform to RAII as possible --- src/audio_core/hle/wmf_decoder.cpp | 68 +++++++++++++----------- src/audio_core/hle/wmf_decoder_utils.cpp | 27 ---------- src/audio_core/hle/wmf_decoder_utils.h | 2 - 3 files changed, 38 insertions(+), 59 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 4d649349a..8599b206d 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -16,14 +16,12 @@ public: private: std::optional Initalize(const BinaryRequest& request); - void Clear(); - std::optional Decode(const BinaryRequest& request); MFOutputState DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams); - bool initialized = false; - bool selected = false; + bool transform_initialized = false; + bool format_selected = false; Memory::MemorySystem& memory; @@ -32,10 +30,35 @@ private: DWORD out_stream_id = 0; }; -WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) {} +WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { + 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); + } + + // lite startup is faster and all what we need is included + hr = 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); + } + + LOG_INFO(Audio_DSP, "Media Foundation activated"); +} WMFDecoder::Impl::~Impl() { - Clear(); + if (transform_initialized) { + MFFlush(transform.get()); + // delete the transform object before shutting down MF + // otherwise access violation will occur + transform.reset(); + MFShutdown(); + CoUninitialize(); + } + transform_initialized = false; + format_selected = false; } std::optional WMFDecoder::Impl::ProcessRequest(const BinaryRequest& request) { @@ -65,17 +88,13 @@ std::optional WMFDecoder::Impl::ProcessRequest(const BinaryReque } std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& request) { - if (!initialized) { - MFCoInit(); - } - BinaryResponse response; std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; transform = MFDecoderInit(); if (transform == nullptr) { - LOG_CRITICAL(Audio_DSP, "Can't init decoder"); + LOG_CRITICAL(Audio_DSP, "Can't initialize decoder"); return response; } @@ -89,22 +108,11 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r return response; } - initialized = true; + transform_initialized = true; + format_selected = false; // select format again if application request initialize the DSP return response; } -void WMFDecoder::Impl::Clear() { - if (initialized) { - MFFlush(transform.get()); - // delete the transform object before shutting down MF - // otherwise access violation will occur - transform.reset(); - MFDestroy(); - } - initialized = false; - selected = false; -} - MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, std::array, 2>& out_streams) { MFOutputState output_status = MFOutputState::OK; @@ -138,7 +146,7 @@ MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, // for status = 2, reset MF if (output_status == MFOutputState::NeedReconfig) { - Clear(); + format_selected = false; return MFOutputState::NeedReconfig; } @@ -164,7 +172,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ response.num_channels = 2; response.num_samples = 1024; - if (!initialized) { + if (!transform_initialized) { LOG_DEBUG(Audio_DSP, "Decoder not initialized"); // This is a hack to continue games when decoder failed to initialize return response; @@ -190,7 +198,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ response.num_channels = adts_meta->ADTSHeader.channels; - if (!selected) { + 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, @@ -200,7 +208,7 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ // 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); - selected = true; + format_selected = true; } sample = CreateSample((void*)data, request.size, 1, 0); @@ -222,8 +230,8 @@ std::optional WMFDecoder::Impl::Decode(const BinaryRequest& requ LOG_ERROR(Audio_DSP, "Errors occurred when receiving output"); return response; } else if (output_status == MFOutputState::NeedReconfig) { - // re-initialize the whole thing to adapt to new parameters - this->Initalize(request); + // flush the transform + MFFlush(transform.get()); // decode again return this->Decode(request); } diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 7dec97abf..e4055bfb2 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -25,28 +25,6 @@ void ReportError(std::string msg, HRESULT hr) { LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr); } -bool MFCoInit() { - 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); - return false; - } - - // lite startup is faster and all what we need is included - hr = 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); - return false; - } - - LOG_INFO(Audio_DSP, "Media Foundation activated"); - - return true; -} - unique_mfptr MFDecoderInit(GUID audio_format) { HRESULT hr = S_OK; MFT_REGISTER_TYPE_INFO reg = {0}; @@ -84,11 +62,6 @@ unique_mfptr MFDecoderInit(GUID audio_format) { return std::move(transform); } -void MFDestroy() { - MFShutdown(); - CoUninitialize(); -} - unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { HRESULT hr = S_OK; unique_mfptr buf; diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index ac114edc9..f9a75af79 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -73,9 +73,7 @@ struct ADTSMeta { }; // exported functions -bool MFCoInit(); unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); -void MFDestroy(); unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, From 671ac441e95113bb63bed6d2e40b6316e95a3430 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Wed, 13 Feb 2019 14:32:14 -0700 Subject: [PATCH 33/34] audio_core: hle: mf: move transform initializer to ctor --- src/audio_core/hle/wmf_decoder.cpp | 43 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index 8599b206d..e78f24ca1 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -46,6 +46,24 @@ WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { } 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; } WMFDecoder::Impl::~Impl() { @@ -54,11 +72,9 @@ WMFDecoder::Impl::~Impl() { // delete the transform object before shutting down MF // otherwise access violation will occur transform.reset(); - MFShutdown(); - CoUninitialize(); } - transform_initialized = false; - format_selected = false; + MFShutdown(); + CoUninitialize(); } std::optional WMFDecoder::Impl::ProcessRequest(const BinaryRequest& request) { @@ -91,25 +107,8 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r BinaryResponse response; std::memcpy(&response, &request, sizeof(response)); response.unknown1 = 0x0; - transform = MFDecoderInit(); - if (transform == nullptr) { - LOG_CRITICAL(Audio_DSP, "Can't initialize decoder"); - return response; - } - - HRESULT 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 response; - } - - transform_initialized = true; - format_selected = false; // select format again if application request initialize the DSP + format_selected = false; // select format again if application request initialize the DSP return response; } From a4ba35fe3afdf9d7ce8d8162b695c2066744a805 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Wed, 13 Feb 2019 15:15:11 -0700 Subject: [PATCH 34/34] audio_core: hle: mf: lint --- src/audio_core/hle/wmf_decoder_utils.cpp | 7 +++++-- src/audio_core/hle/wmf_decoder_utils.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index e4055bfb2..21dd8a950 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -52,6 +52,8 @@ unique_mfptr MFDecoderInit(GUID audio_format) { if (FAILED(hr)) transform = nullptr; activate[n]->Release(); + if (SUCCEEDED(hr)) + break; } if (transform == nullptr) { ReportError("Failed to initialize MFT", hr); @@ -59,10 +61,11 @@ unique_mfptr MFDecoderInit(GUID audio_format) { return nullptr; } CoTaskMemFree(activate); - return std::move(transform); + return transform; } -unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) { +unique_mfptr CreateSample(const void* data, DWORD len, DWORD alignment, + LONGLONG duration) { HRESULT hr = S_OK; unique_mfptr buf; unique_mfptr sample; diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index f9a75af79..26e1217a2 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -74,7 +74,7 @@ struct ADTSMeta { // exported functions unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); -unique_mfptr CreateSample(void* data, DWORD len, DWORD alignment = 1, +unique_mfptr CreateSample(const void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, const UINT8* user_data, UINT32 user_data_len,