From b395efe804463c3fe1b694058a1d954ad8dd1533 Mon Sep 17 00:00:00 2001 From: James Rowe Date: Sun, 8 Dec 2019 13:54:27 -0700 Subject: [PATCH] Dynamically load Media Foundation --- src/audio_core/CMakeLists.txt | 4 +- src/audio_core/hle/wmf_decoder.cpp | 17 +++- src/audio_core/hle/wmf_decoder_utils.cpp | 112 +++++++++++++++++++++++ src/audio_core/hle/wmf_decoder_utils.h | 38 ++++++++ 4 files changed, 166 insertions(+), 5 deletions(-) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 4e0c9f4de4..f2b3e1f3b8 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -45,7 +45,9 @@ if(ENABLE_MF) hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h ) - target_link_libraries(audio_core PRIVATE mf.lib mfplat.lib mfuuid.lib) + # We dynamically load the required symbols from mf.dll and mfplat.dll but mfuuid is not a dll + # just a static library of GUIDS so include that one directly. + target_link_libraries(audio_core PRIVATE mfuuid.lib) target_compile_definitions(audio_core PUBLIC HAVE_MF) elseif(ENABLE_FFMPEG_AUDIO_DECODER) target_sources(audio_core PRIVATE diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp index a1507991b3..801ae6ee66 100644 --- a/src/audio_core/hle/wmf_decoder.cpp +++ b/src/audio_core/hle/wmf_decoder.cpp @@ -7,6 +7,8 @@ namespace AudioCore::HLE { +using namespace MFDecoder; + class WMFDecoder::Impl { public: explicit Impl(Memory::MemorySystem& memory); @@ -31,6 +33,13 @@ private: }; WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { + // Attempt to load the symbols for mf.dll + if (!InitMFDLL()) { + LOG_CRITICAL(Audio_DSP, + "Unable to load mf.dll. AAC audio through media foundation unavailable"); + return; + } + HRESULT hr = S_OK; hr = CoInitialize(NULL); // S_FALSE will be returned when COM has already been initialized @@ -39,7 +48,7 @@ WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { } // lite startup is faster and all what we need is included - hr = MFStartup(MF_VERSION, MFSTARTUP_LITE); + hr = MFDecoder::MFStartup(MF_VERSION, MFSTARTUP_LITE); if (hr != S_OK) { // Do you know you can't initialize MF in test mode or safe mode? ReportError("Failed to initialize Media Foundation", hr); @@ -73,7 +82,7 @@ WMFDecoder::Impl::~Impl() { // otherwise access violation will occur transform.reset(); } - MFShutdown(); + MFDecoder::MFShutdown(); CoUninitialize(); } @@ -112,8 +121,8 @@ std::optional WMFDecoder::Impl::Initalize(const BinaryRequest& r return response; } -MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, - std::array, 2>& out_streams) { +MFDecoder::MFOutputState WMFDecoder::Impl::DecodingLoop( + ADTSData adts_header, std::array, 2>& out_streams) { MFOutputState output_status = MFOutputState::OK; std::optional> output_buffer; unique_mfptr output; diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp index 21dd8a9503..bcaf0b2251 100644 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ b/src/audio_core/hle/wmf_decoder_utils.cpp @@ -5,6 +5,8 @@ #include "common/string_util.h" #include "wmf_decoder_utils.h" +namespace MFDecoder { + // utility functions void ReportError(std::string msg, HRESULT hr) { if (SUCCEEDED(hr)) { @@ -26,6 +28,7 @@ void ReportError(std::string msg, HRESULT hr) { } unique_mfptr MFDecoderInit(GUID audio_format) { + HRESULT hr = S_OK; MFT_REGISTER_TYPE_INFO reg = {0}; GUID category = MFT_CATEGORY_AUDIO_DECODER; @@ -347,3 +350,112 @@ std::optional> CopySampleToBuffer(IMFSample* sample) { buffer->Unlock(); return output; } + +namespace { + +struct LibraryDeleter { + using pointer = HMODULE; + void operator()(HMODULE h) const { + if (h != nullptr) + FreeLibrary(h); + } +}; + +std::unique_ptr mf_dll{nullptr}; +std::unique_ptr mfplat_dll{nullptr}; + +} // namespace + +bool InitMFDLL() { + + mf_dll.reset(LoadLibrary(TEXT("mf.dll"))); + if (!mf_dll) { + DWORD error_message_id = GetLastError(); + LPSTR message_buffer = nullptr; + size_t size = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&message_buffer), 0, nullptr); + + std::string message(message_buffer, size); + + LocalFree(message_buffer); + LOG_ERROR(Audio_DSP, "Could not load mf.dll: {}", message); + return false; + } + + mfplat_dll.reset(LoadLibrary(TEXT("mfplat.dll"))); + if (!mfplat_dll) { + DWORD error_message_id = GetLastError(); + LPSTR message_buffer = nullptr; + size_t size = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&message_buffer), 0, nullptr); + + std::string message(message_buffer, size); + + LocalFree(message_buffer); + LOG_ERROR(Audio_DSP, "Could not load mfplat.dll: {}", message); + return false; + } + + MFStartup = Symbol(mfplat_dll.get(), "MFStartup"); + if (!MFStartup) { + LOG_ERROR(Audio_DSP, "Cannot load function MFStartup"); + return false; + } + + MFShutdown = Symbol(mfplat_dll.get(), "MFShutdown"); + if (!MFShutdown) { + LOG_ERROR(Audio_DSP, "Cannot load function MFShutdown"); + return false; + } + + MFShutdownObject = Symbol(mf_dll.get(), "MFShutdownObject"); + if (!MFShutdownObject) { + LOG_ERROR(Audio_DSP, "Cannot load function MFShutdownObject"); + return false; + } + + MFCreateAlignedMemoryBuffer = Symbol( + mfplat_dll.get(), "MFCreateAlignedMemoryBuffer"); + if (!MFCreateAlignedMemoryBuffer) { + LOG_ERROR(Audio_DSP, "Cannot load function MFCreateAlignedMemoryBuffer"); + return false; + } + + MFCreateSample = Symbol(mfplat_dll.get(), "MFCreateSample"); + if (!MFCreateSample) { + LOG_ERROR(Audio_DSP, "Cannot load function MFCreateSample"); + return false; + } + + MFTEnumEx = + Symbol(mfplat_dll.get(), "MFTEnumEx"); + if (!MFTEnumEx) { + LOG_ERROR(Audio_DSP, "Cannot load function MFTEnumEx"); + return false; + } + + MFCreateMediaType = Symbol(mfplat_dll.get(), "MFCreateMediaType"); + if (!MFCreateMediaType) { + LOG_ERROR(Audio_DSP, "Cannot load function MFCreateMediaType"); + return false; + } +} + +Symbol MFStartup; +Symbol MFShutdown; +Symbol MFShutdownObject; +Symbol MFCreateAlignedMemoryBuffer; +Symbol MFCreateSample; +Symbol + MFTEnumEx; +Symbol MFCreateMediaType; + +} // namespace MFDecoder diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h index 26e1217a2d..cdbde5f1f0 100644 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ b/src/audio_core/hle/wmf_decoder_utils.h @@ -18,6 +18,39 @@ #include "adts.h" +namespace MFDecoder { + +template +struct Symbol { + Symbol() = default; + Symbol(HMODULE dll, const char* name) { + if (dll) { + ptr_symbol = reinterpret_cast(GetProcAddress(dll, name)); + } + } + + operator T*() const { + return ptr_symbol; + } + + explicit operator bool() const { + return ptr_symbol != nullptr; + } + + T* ptr_symbol = nullptr; +}; + +// Runtime load the MF symbols to prevent mf.dll not found errors on citra load +extern Symbol MFStartup; +extern Symbol MFShutdown; +extern Symbol MFShutdownObject; +extern Symbol MFCreateAlignedMemoryBuffer; +extern Symbol MFCreateSample; +extern Symbol + MFTEnumEx; +extern Symbol MFCreateMediaType; + enum class MFOutputState { FatalError, OK, NeedMoreInput, NeedReconfig, HaveMoreData }; enum class MFInputState { FatalError, OK, NotAccepted }; @@ -73,6 +106,9 @@ struct ADTSMeta { }; // exported functions + +/// Loads the symbols from mf.dll at runtime. Returns false if the symbols can't be loaded +bool InitMFDLL(); unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); unique_mfptr CreateSample(const void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0); @@ -87,3 +123,5 @@ MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* std::tuple> ReceiveSample(IMFTransform* transform, DWORD out_stream_id); std::optional> CopySampleToBuffer(IMFSample* sample); + +} // namespace MFDecoder