Merge pull request #5020 from jroweboy/dynamic-mf

Runtime Load MediaFoundation dlls on Windows
This commit is contained in:
James Rowe 2019-12-14 10:13:54 -07:00 committed by GitHub
commit ddb7ead3e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 213 additions and 6 deletions

View File

@ -45,7 +45,9 @@ if(ENABLE_MF)
hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.cpp
hle/wmf_decoder_utils.h 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) target_compile_definitions(audio_core PUBLIC HAVE_MF)
elseif(ENABLE_FFMPEG_AUDIO_DECODER) elseif(ENABLE_FFMPEG_AUDIO_DECODER)
target_sources(audio_core PRIVATE target_sources(audio_core PRIVATE

View File

@ -56,6 +56,9 @@ class DecoderBase {
public: public:
virtual ~DecoderBase(); virtual ~DecoderBase();
virtual std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) = 0; virtual std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) = 0;
/// Return true if this Decoder can be loaded. Return false if the system cannot create the
/// decoder
virtual bool IsValid() const = 0;
}; };
class NullDecoder final : public DecoderBase { class NullDecoder final : public DecoderBase {
@ -63,6 +66,9 @@ public:
NullDecoder(); NullDecoder();
~NullDecoder() override; ~NullDecoder() override;
std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) override; std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) override;
bool IsValid() const override {
return true;
}
}; };
} // namespace AudioCore::HLE } // namespace AudioCore::HLE

View File

@ -12,6 +12,9 @@ public:
explicit Impl(Memory::MemorySystem& memory); explicit Impl(Memory::MemorySystem& memory);
~Impl(); ~Impl();
std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request); std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request);
bool IsValid() const {
return initalized;
}
private: private:
std::optional<BinaryResponse> Initalize(const BinaryRequest& request); std::optional<BinaryResponse> Initalize(const BinaryRequest& request);
@ -261,4 +264,8 @@ std::optional<BinaryResponse> FFMPEGDecoder::ProcessRequest(const BinaryRequest&
return impl->ProcessRequest(request); return impl->ProcessRequest(request);
} }
bool FFMPEGDecoder::IsValid() const {
return impl->IsValid();
}
} // namespace AudioCore::HLE } // namespace AudioCore::HLE

View File

@ -13,6 +13,7 @@ public:
explicit FFMPEGDecoder(Memory::MemorySystem& memory); explicit FFMPEGDecoder(Memory::MemorySystem& memory);
~FFMPEGDecoder() override; ~FFMPEGDecoder() override;
std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) override; std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) override;
bool IsValid() const override;
private: private:
class Impl; class Impl;

View File

@ -87,15 +87,27 @@ DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory) : parent(paren
source.SetMemory(memory); source.SetMemory(memory);
} }
#ifdef HAVE_MF #if defined(HAVE_MF) && defined(HAVE_FFMPEG)
decoder = std::make_unique<HLE::WMFDecoder>(memory); decoder = std::make_unique<HLE::WMFDecoder>(memory);
#elif HAVE_FFMPEG if (!decoder->IsValid()) {
LOG_WARNING(Audio_DSP, "Unable to load MediaFoundation. Attempting to load FFMPEG instead");
decoder = std::make_unique<HLE::FFMPEGDecoder>(memory);
}
#elif defined(HAVE_MF)
decoder = std::make_unique<HLE::WMFDecoder>(memory);
#elif defined(HAVE_FFMPEG)
decoder = std::make_unique<HLE::FFMPEGDecoder>(memory); decoder = std::make_unique<HLE::FFMPEGDecoder>(memory);
#else #else
LOG_WARNING(Audio_DSP, "No decoder found, this could lead to missing audio"); LOG_WARNING(Audio_DSP, "No decoder found, this could lead to missing audio");
decoder = std::make_unique<HLE::NullDecoder>(); decoder = std::make_unique<HLE::NullDecoder>();
#endif // HAVE_MF #endif // HAVE_MF
if (!decoder->IsValid()) {
LOG_WARNING(Audio_DSP,
"Unable to load any decoders, this could cause missing audio in some games");
decoder = std::make_unique<HLE::NullDecoder>();
}
Core::Timing& timing = Core::System::GetInstance().CoreTiming(); Core::Timing& timing = Core::System::GetInstance().CoreTiming();
tick_event = tick_event =
timing.RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) { timing.RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, s64 cycles_late) {

View File

@ -7,11 +7,16 @@
namespace AudioCore::HLE { namespace AudioCore::HLE {
using namespace MFDecoder;
class WMFDecoder::Impl { class WMFDecoder::Impl {
public: public:
explicit Impl(Memory::MemorySystem& memory); explicit Impl(Memory::MemorySystem& memory);
~Impl(); ~Impl();
std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request); std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request);
bool IsValid() const {
return is_valid;
}
private: private:
std::optional<BinaryResponse> Initalize(const BinaryRequest& request); std::optional<BinaryResponse> Initalize(const BinaryRequest& request);
@ -28,21 +33,35 @@ private:
unique_mfptr<IMFTransform> transform; unique_mfptr<IMFTransform> transform;
DWORD in_stream_id = 0; DWORD in_stream_id = 0;
DWORD out_stream_id = 0; DWORD out_stream_id = 0;
bool is_valid = false;
bool mf_started = false;
bool coinited = false;
}; };
WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { 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; HRESULT hr = S_OK;
hr = CoInitialize(NULL); hr = CoInitialize(NULL);
// S_FALSE will be returned when COM has already been initialized // S_FALSE will be returned when COM has already been initialized
if (hr != S_OK && hr != S_FALSE) { if (hr != S_OK && hr != S_FALSE) {
ReportError("Failed to start COM components", hr); ReportError("Failed to start COM components", hr);
} else {
coinited = true;
} }
// lite startup is faster and all what we need is included // 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) { if (hr != S_OK) {
// Do you know you can't initialize MF in test mode or safe mode? // Do you know you can't initialize MF in test mode or safe mode?
ReportError("Failed to initialize Media Foundation", hr); ReportError("Failed to initialize Media Foundation", hr);
} else {
mf_started = true;
} }
LOG_INFO(Audio_DSP, "Media Foundation activated"); LOG_INFO(Audio_DSP, "Media Foundation activated");
@ -64,6 +83,7 @@ WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) {
return; return;
} }
transform_initialized = true; transform_initialized = true;
is_valid = true;
} }
WMFDecoder::Impl::~Impl() { WMFDecoder::Impl::~Impl() {
@ -73,8 +93,12 @@ WMFDecoder::Impl::~Impl() {
// otherwise access violation will occur // otherwise access violation will occur
transform.reset(); transform.reset();
} }
MFShutdown(); if (mf_started) {
MFDecoder::MFShutdown();
}
if (coinited) {
CoUninitialize(); CoUninitialize();
}
} }
std::optional<BinaryResponse> WMFDecoder::Impl::ProcessRequest(const BinaryRequest& request) { std::optional<BinaryResponse> WMFDecoder::Impl::ProcessRequest(const BinaryRequest& request) {
@ -271,4 +295,8 @@ std::optional<BinaryResponse> WMFDecoder::ProcessRequest(const BinaryRequest& re
return impl->ProcessRequest(request); return impl->ProcessRequest(request);
} }
bool WMFDecoder::IsValid() const {
return impl->IsValid();
}
} // namespace AudioCore::HLE } // namespace AudioCore::HLE

View File

@ -13,6 +13,7 @@ public:
explicit WMFDecoder(Memory::MemorySystem& memory); explicit WMFDecoder(Memory::MemorySystem& memory);
~WMFDecoder() override; ~WMFDecoder() override;
std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) override; std::optional<BinaryResponse> ProcessRequest(const BinaryRequest& request) override;
bool IsValid() const override;
private: private:
class Impl; class Impl;

View File

@ -5,6 +5,8 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "wmf_decoder_utils.h" #include "wmf_decoder_utils.h"
namespace MFDecoder {
// utility functions // utility functions
void ReportError(std::string msg, HRESULT hr) { void ReportError(std::string msg, HRESULT hr) {
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
@ -26,6 +28,7 @@ void ReportError(std::string msg, HRESULT hr) {
} }
unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format) { unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format) {
HRESULT hr = S_OK; HRESULT hr = S_OK;
MFT_REGISTER_TYPE_INFO reg = {0}; MFT_REGISTER_TYPE_INFO reg = {0};
GUID category = MFT_CATEGORY_AUDIO_DECODER; GUID category = MFT_CATEGORY_AUDIO_DECODER;
@ -347,3 +350,112 @@ std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample) {
buffer->Unlock(); buffer->Unlock();
return output; return output;
} }
namespace {
struct LibraryDeleter {
using pointer = HMODULE;
void operator()(HMODULE h) const {
if (h != nullptr)
FreeLibrary(h);
}
};
std::unique_ptr<HMODULE, LibraryDeleter> mf_dll{nullptr};
std::unique_ptr<HMODULE, LibraryDeleter> mfplat_dll{nullptr};
} // namespace
bool InitMFDLL() {
mf_dll.reset(LoadLibrary(TEXT("mf.dll")));
if (!mf_dll) {
DWORD error_message_id = GetLastError();
LPSTR message_buffer = nullptr;
size_t size =
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr);
std::string message(message_buffer, size);
LocalFree(message_buffer);
LOG_ERROR(Audio_DSP, "Could not load mf.dll: {}", message);
return false;
}
mfplat_dll.reset(LoadLibrary(TEXT("mfplat.dll")));
if (!mfplat_dll) {
DWORD error_message_id = GetLastError();
LPSTR message_buffer = nullptr;
size_t size =
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr);
std::string message(message_buffer, size);
LocalFree(message_buffer);
LOG_ERROR(Audio_DSP, "Could not load mfplat.dll: {}", message);
return false;
}
MFStartup = Symbol<HRESULT(ULONG, DWORD)>(mfplat_dll.get(), "MFStartup");
if (!MFStartup) {
LOG_ERROR(Audio_DSP, "Cannot load function MFStartup");
return false;
}
MFShutdown = Symbol<HRESULT(void)>(mfplat_dll.get(), "MFShutdown");
if (!MFShutdown) {
LOG_ERROR(Audio_DSP, "Cannot load function MFShutdown");
return false;
}
MFShutdownObject = Symbol<HRESULT(IUnknown*)>(mf_dll.get(), "MFShutdownObject");
if (!MFShutdownObject) {
LOG_ERROR(Audio_DSP, "Cannot load function MFShutdownObject");
return false;
}
MFCreateAlignedMemoryBuffer = Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)>(
mfplat_dll.get(), "MFCreateAlignedMemoryBuffer");
if (!MFCreateAlignedMemoryBuffer) {
LOG_ERROR(Audio_DSP, "Cannot load function MFCreateAlignedMemoryBuffer");
return false;
}
MFCreateSample = Symbol<HRESULT(IMFSample**)>(mfplat_dll.get(), "MFCreateSample");
if (!MFCreateSample) {
LOG_ERROR(Audio_DSP, "Cannot load function MFCreateSample");
return false;
}
MFTEnumEx =
Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*,
IMFActivate***, UINT32*)>(mfplat_dll.get(), "MFTEnumEx");
if (!MFTEnumEx) {
LOG_ERROR(Audio_DSP, "Cannot load function MFTEnumEx");
return false;
}
MFCreateMediaType = Symbol<HRESULT(IMFMediaType**)>(mfplat_dll.get(), "MFCreateMediaType");
if (!MFCreateMediaType) {
LOG_ERROR(Audio_DSP, "Cannot load function MFCreateMediaType");
return false;
}
}
Symbol<HRESULT(ULONG, DWORD)> MFStartup;
Symbol<HRESULT(void)> MFShutdown;
Symbol<HRESULT(IUnknown*)> MFShutdownObject;
Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)> MFCreateAlignedMemoryBuffer;
Symbol<HRESULT(IMFSample**)> MFCreateSample;
Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*,
IMFActivate***, UINT32*)>
MFTEnumEx;
Symbol<HRESULT(IMFMediaType**)> MFCreateMediaType;
} // namespace MFDecoder

View File

@ -18,6 +18,39 @@
#include "adts.h" #include "adts.h"
namespace MFDecoder {
template <typename T>
struct Symbol {
Symbol() = default;
Symbol(HMODULE dll, const char* name) {
if (dll) {
ptr_symbol = reinterpret_cast<T*>(GetProcAddress(dll, name));
}
}
operator T*() const {
return ptr_symbol;
}
explicit operator bool() const {
return ptr_symbol != nullptr;
}
T* ptr_symbol = nullptr;
};
// Runtime load the MF symbols to prevent mf.dll not found errors on citra load
extern Symbol<HRESULT(ULONG, DWORD)> MFStartup;
extern Symbol<HRESULT(void)> MFShutdown;
extern Symbol<HRESULT(IUnknown*)> MFShutdownObject;
extern Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)> MFCreateAlignedMemoryBuffer;
extern Symbol<HRESULT(IMFSample**)> MFCreateSample;
extern Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*,
IMFActivate***, UINT32*)>
MFTEnumEx;
extern Symbol<HRESULT(IMFMediaType**)> MFCreateMediaType;
enum class MFOutputState { FatalError, OK, NeedMoreInput, NeedReconfig, HaveMoreData }; enum class MFOutputState { FatalError, OK, NeedMoreInput, NeedReconfig, HaveMoreData };
enum class MFInputState { FatalError, OK, NotAccepted }; enum class MFInputState { FatalError, OK, NotAccepted };
@ -73,6 +106,9 @@ struct ADTSMeta {
}; };
// exported functions // exported functions
/// Loads the symbols from mf.dll at runtime. Returns false if the symbols can't be loaded
bool InitMFDLL();
unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC);
unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1, unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1,
LONGLONG duration = 0); LONGLONG duration = 0);
@ -87,3 +123,5 @@ MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample*
std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* transform, std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* transform,
DWORD out_stream_id); DWORD out_stream_id);
std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample); std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample);
} // namespace MFDecoder