Merge pull request #5020 from jroweboy/dynamic-mf
Runtime Load MediaFoundation dlls on Windows
This commit is contained in:
commit
ddb7ead3e4
9 changed files with 213 additions and 6 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,9 +93,13 @@ 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) {
|
||||||
if (request.codec != DecoderCodec::AAC) {
|
if (request.codec != DecoderCodec::AAC) {
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue