Files
spice2x.github.io/src/spice2x/games/iidx/mf_wrappers.cpp
T
bicarus 88a210e82e iidx: camera hook improvements (#841)
## Link to GitHub Issue or related Pull Request, if one exists
#201

## Description of change

- Adds MJPEG camera support using Media Foundation decoding.
- Moves camera capture to asynchronous Source Reader callbacks.
- Supports native NV12 and YUY2 capture, preferring target-sized native
formats over MJPEG when possible.
- Improves capture and rendering performance through callback-paced
reads, bulk frame uploads, optimized row-based flips, and lazy
allocation of flip textures.
- Makes runtime media-type changes interrupt pending reads and safely
flush and reconfigure the Source Reader.
- Improves automatic mode selection based on aspect ratio, proximity to
1280x720, frame rate, and capture format.
- Hardens camera shutdown, flush handling, media-type changes, and
repeated capture failures.

## Testing
Tested with two cameras in tdj
2026-07-27 08:42:46 -07:00

132 lines
4.9 KiB
C++

#include "mf_wrappers.h"
#include "util/libutils.h"
#include "util/logging.h"
namespace games::iidx {
static bool INITIALIZED = false;
static HMODULE mf_dll = nullptr;
static HMODULE mfreadwrite_dll = nullptr;
static HMODULE mfplat_dll = nullptr;
typedef HRESULT (__stdcall * MFCreateAttributes_t)(
_Out_ IMFAttributes** ppMFAttributes,
_In_ UINT32 cInitialSize
);
typedef HRESULT (__stdcall * MFCreateMediaType_t)(
_Out_ IMFMediaType** ppMFType
);
typedef HRESULT (__stdcall * MFEnumDeviceSources_t)(
_In_ IMFAttributes* pAttributes,
_Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate,
_Out_ UINT32* pcSourceActivate
);
typedef HRESULT (__stdcall * MFCreateSourceReaderFromMediaSource_t)(
_In_ IMFMediaSource *pMediaSource,
_In_opt_ IMFAttributes *pAttributes,
_Out_ IMFSourceReader **ppSourceReader
);
typedef HRESULT (__stdcall * MFGetService_t)(
IUnknown* punkObject,
REFGUID guidService,
REFIID riid,
_Outptr_ LPVOID* ppvObject
);
static MFCreateAttributes_t MFCreateAttributes = nullptr;
static MFCreateMediaType_t MFCreateMediaType = nullptr;
static MFEnumDeviceSources_t MFEnumDeviceSources = nullptr;
static MFCreateSourceReaderFromMediaSource_t MFCreateSourceReaderFromMediaSource = nullptr;
static MFGetService_t MFGetService = nullptr;
void init_mf_library() {
// why was all of this needed?
//
// when iidx camhook was initially implemented, we linked to mf.lib, mfreadwrite.lib, and mfplat.lib
// this made Unity-based really unhappy, causing them to skip over the logic that loads mf library
// causing videos to not play ("Initializing Microsoft Media Foundation failed." in the cmd prompt)
//
// as a result, the static linking to mf libs were removed, and we are now doing the mess that is this file
if (INITIALIZED) {
return;
}
INITIALIZED = true;
log_misc("mf_wrappers", "creating delay-loaded wrappers for MF routines - BEGIN");
mf_dll = libutils::load_library("mf.dll", true);
mfreadwrite_dll = libutils::load_library("mfreadwrite.dll", true);
mfplat_dll = libutils::load_library("mfplat.dll", true);
MFCreateAttributes = (MFCreateAttributes_t)
libutils::get_proc(mfplat_dll, "MFCreateAttributes");
if (!MFCreateAttributes) {
log_fatal("mf_wrappers", "MFCreateAttributes failed to hook");
}
MFCreateMediaType = (MFCreateMediaType_t)
libutils::get_proc(mfplat_dll, "MFCreateMediaType");
if (!MFCreateMediaType) {
log_fatal("mf_wrappers", "MFCreateMediaType failed to hook");
}
MFEnumDeviceSources = (MFEnumDeviceSources_t)
libutils::get_proc(mf_dll, "MFEnumDeviceSources");
if (!MFEnumDeviceSources) {
log_fatal("mf_wrappers", "MFEnumDeviceSources failed to hook");
}
MFCreateSourceReaderFromMediaSource = (MFCreateSourceReaderFromMediaSource_t)
libutils::get_proc(mfreadwrite_dll, "MFCreateSourceReaderFromMediaSource");
if (!MFCreateSourceReaderFromMediaSource) {
log_fatal("mf_wrappers", "MFCreateSourceReaderFromMediaSource failed to hook");
}
MFGetService = (MFGetService_t)libutils::get_proc(mf_dll, "MFGetService");
if (!MFGetService) {
log_fatal("mf_wrappers", "MFGetService failed to hook");
}
log_misc("mf_wrappers", "creating delay-loaded wrappers for MF routines - DONE");
}
HRESULT WrappedMFCreateAttributes (
_Out_ IMFAttributes** ppMFAttributes,
_In_ UINT32 cInitialSize) {
return MFCreateAttributes(ppMFAttributes, cInitialSize);
}
HRESULT WrappedMFCreateMediaType (
_Out_ IMFMediaType** ppMFType) {
return MFCreateMediaType(ppMFType);
}
HRESULT WrappedMFEnumDeviceSources (
_In_ IMFAttributes* pAttributes,
_Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate,
_Out_ UINT32* pcSourceActivate) {
return MFEnumDeviceSources(pAttributes, pppSourceActivate, pcSourceActivate);
}
HRESULT WrappedMFCreateSourceReaderFromMediaSource (
_In_ IMFMediaSource *pMediaSource,
_In_opt_ IMFAttributes *pAttributes,
_Out_ IMFSourceReader **ppSourceReader) {
return MFCreateSourceReaderFromMediaSource(pMediaSource, pAttributes, ppSourceReader);
}
HRESULT WrappedMFGetService (
IUnknown* punkObject,
REFGUID guidService,
REFIID riid,
_Outptr_ LPVOID* ppvObject) {
return MFGetService(punkObject, guidService, riid, ppvObject);
}
}