camera: Windows support, through the Media Foundation API!

This commit is contained in:
Ryan C. Gordon 2024-01-31 15:07:07 -05:00
parent 3dca8a03da
commit 7191a97fe3
11 changed files with 998 additions and 44 deletions

View File

@ -397,6 +397,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
<ClCompile Include="..\..\src\camera\mediafoundation\SDL_camera_mediafoundation.c" />
<ClCompile Include="..\..\src\camera\SDL_camera.c" />
<ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
<ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />

View File

@ -184,6 +184,9 @@
<Filter Include="camera\dummy">
<UniqueIdentifier>{0000fc2700d453b3c8d79fe81e1c0000}</UniqueIdentifier>
</Filter>
<Filter Include="camera\mediafoundation">
<UniqueIdentifier>{0000fbfe2d21e4f451142e7d0e870000}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\SDL3\SDL_begin_code.h">
@ -856,6 +859,9 @@
<ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c">
<Filter>camera\dummy</Filter>
</ClCompile>
<ClCompile Include="..\..\src\camera\mediafoundation\SDL_camera_mediafoundation.c">
<Filter>camera\mediafoundation</Filter>
</ClCompile>
<ClCompile Include="..\..\src\camera\SDL_camera.c">
<Filter>camera</Filter>
</ClCompile>

View File

@ -499,6 +499,7 @@
F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */; };
F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */; };
FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); };
00009F560664255CCB6C0000 /* SDL_camera_mediafoundation.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -1027,6 +1028,7 @@
F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; };
FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_camera_mediafoundation.c; path = SDL_camera_mediafoundation.c; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -1068,6 +1070,7 @@
0000035D38C3899C7EFD0000 /* SDL_camera.c */,
00009003C7148E1126CA0000 /* SDL_camera_c.h */,
00005D3EB902478835E20000 /* SDL_syscamera.h */,
0000926F2501CA0BDD650000 /* mediafoundation */,
);
path = camera;
sourceTree = "<group>";
@ -2175,6 +2178,14 @@
path = resources;
sourceTree = "<group>";
};
0000926F2501CA0BDD650000 /* mediafoundation */ = {
isa = PBXGroup;
children = (
0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */,
);
path = mediafoundation;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -2754,6 +2765,7 @@
000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */,
00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */,
00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */,
00009F560664255CCB6C0000 /* SDL_camera_mediafoundation.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -472,7 +472,8 @@
#cmakedefine SDL_CAMERA_DRIVER_V4L2 @SDL_CAMERA_DRIVER_V4L2@
#cmakedefine SDL_CAMERA_DRIVER_COREMEDIA @SDL_CAMERA_DRIVER_COREMEDIA@
#cmakedefine SDL_CAMERA_DRIVER_ANDROID @SDL_CAMERA_DRIVER_ANDROID@
#cmakedefine SDL_CAMERA_DRIVER_EMSCRIPTEN @SDL_CAMERA_DRIVER_EMSCRIPTEND@
#cmakedefine SDL_CAMERA_DRIVER_EMSCRIPTEN @SDL_CAMERA_DRIVER_EMSCRIPTEN@
#cmakedefine SDL_CAMERA_DRIVER_MEDIAFOUNDATION @SDL_CAMERA_DRIVER_MEDIAFOUNDATION@
/* Enable misc subsystem */
#cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@

View File

@ -311,7 +311,8 @@ typedef unsigned int uintptr_t;
/* Enable filesystem support */
#define SDL_FILESYSTEM_WINDOWS 1
/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */
/* Enable the camera driver */
#define SDL_CAMERA_DRIVER_MEDIAFOUNDATION 1
#define SDL_CAMERA_DRIVER_DUMMY 1
#endif /* SDL_build_config_windows_h_ */

View File

@ -43,6 +43,9 @@ static const CameraBootStrap *const bootstrap[] = {
#ifdef SDL_CAMERA_DRIVER_EMSCRIPTEN
&EMSCRIPTENCAMERA_bootstrap,
#endif
#ifdef SDL_CAMERA_DRIVER_MEDIAFOUNDATION
&MEDIAFOUNDATION_bootstrap,
#endif
#ifdef SDL_CAMERA_DRIVER_DUMMY
&DUMMYCAMERA_bootstrap,
#endif
@ -70,6 +73,32 @@ const char *SDL_GetCurrentCameraDriver(void)
return camera_driver.name;
}
int SDL_AddCameraFormat(CameraFormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator)
{
SDL_assert(data != NULL);
if (data->allocated_specs <= data->num_specs) {
const int newalloc = data->allocated_specs ? (data->allocated_specs * 2) : 16;
void *ptr = SDL_realloc(data->specs, sizeof (SDL_CameraSpec) * newalloc);
if (!ptr) {
return -1;
}
data->specs = (SDL_CameraSpec *) ptr;
data->allocated_specs = newalloc;
}
SDL_CameraSpec *spec = &data->specs[data->num_specs];
spec->format = fmt;
spec->width = w;
spec->height = h;
spec->interval_numerator = interval_numerator;
spec->interval_denominator = interval_denominator;
data->num_specs++;
return 0;
}
static void ClosePhysicalCameraDevice(SDL_CameraDevice *device)
{
if (!device) {
@ -610,10 +639,13 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device)
if (rc == 1) { // new frame acquired!
#if DEBUG_CAMERA
SDL_Log("CAMERA: New frame available!");
SDL_Log("CAMERA: New frame available! pixels=%p pitch=%d", device->acquire_surface->pixels, device->acquire_surface->pitch);
#endif
if (device->drop_frames > 0) {
#if DEBUG_CAMERA
SDL_Log("CAMERA: Dropping an initial frame");
#endif
device->drop_frames--;
camera_driver.impl.ReleaseFrame(device, device->acquire_surface);
device->acquire_surface->pixels = NULL;
@ -662,9 +694,15 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device)
} else if (acquired) { // we have a new frame, scale/convert if necessary and queue it for the app!
SDL_assert(slist != NULL);
if (!device->needs_scaling && !device->needs_conversion) { // no conversion needed? Just move the pointer/pitch into the output surface.
#if DEBUG_CAMERA
SDL_Log("CAMERA: Frame is going through without conversion!");
#endif
output_surface->pixels = acquired->pixels;
output_surface->pitch = acquired->pitch;
} else { // convert/scale into a different surface.
#if DEBUG_CAMERA
SDL_Log("CAMERA: Frame is getting converted!");
#endif
SDL_Surface *srcsurf = acquired;
if (device->needs_scaling == -1) { // downscaling? Do it first. -1: downscale, 0: no scaling, 1: upscale
SDL_Surface *dstsurf = device->needs_conversion ? device->conversion_surface : output_surface;

View File

@ -58,6 +58,16 @@ extern void SDL_CameraThreadSetup(SDL_CameraDevice *device);
extern SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device);
extern void SDL_CameraThreadShutdown(SDL_CameraDevice *device);
// common utility functionality to gather up camera specs. Not required!
typedef struct CameraFormatAddData
{
SDL_CameraSpec *specs;
int num_specs;
int allocated_specs;
} CameraFormatAddData;
int SDL_AddCameraFormat(CameraFormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator);
typedef struct SurfaceList
{
SDL_Surface *surface;
@ -189,5 +199,6 @@ extern CameraBootStrap V4L2_bootstrap;
extern CameraBootStrap COREMEDIA_bootstrap;
extern CameraBootStrap ANDROIDCAMERA_bootstrap;
extern CameraBootStrap EMSCRIPTENCAMERA_bootstrap;
extern CameraBootStrap MEDIAFOUNDATION_bootstrap;
#endif // SDL_syscamera_h_

View File

@ -77,4 +77,4 @@ CameraBootStrap DUMMYCAMERA_bootstrap = {
"dummy", "SDL dummy camera driver", DUMMYCAMERA_Init, SDL_TRUE
};
#endif
#endif // SDL_CAMERA_DRIVER_DUMMY

View File

@ -79,8 +79,6 @@ static int EMSCRIPTENCAMERA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *
static void EMSCRIPTENCAMERA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame)
{
SDL_free(frame->pixels);
frame->pixels = NULL;
frame->pitch = 0;
}
static void EMSCRIPTENCAMERA_CloseDevice(SDL_CameraDevice *device)

View File

@ -0,0 +1,918 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
// the Windows Media Foundation API
#ifdef SDL_CAMERA_DRIVER_MEDIAFOUNDATION
#define COBJMACROS
// this seems to be a bug in mfidl.h, just define this to avoid the problem section.
#define __IMFVideoProcessorControl3_INTERFACE_DEFINED__
#include "../../core/windows/SDL_windows.h"
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include "../SDL_syscamera.h"
#include "../SDL_camera_c.h"
static const IID SDL_IID_IMFMediaSource = { 0x279a808d, 0xaec7, 0x40c8, { 0x9c, 0x6b, 0xa6, 0xb4, 0x92, 0xc7, 0x8a, 0x66 } };
static const IID SDL_IID_IMF2DBuffer = { 0x7dc9d5f9, 0x9ed9, 0x44ec, { 0x9b, 0xbf, 0x06, 0x00, 0xbb, 0x58, 0x9f, 0xbb } };
static const IID SDL_IID_IMF2DBuffer2 = { 0x33ae5ea6, 0x4316, 0x436f, { 0x8d, 0xdd, 0xd7, 0x3d, 0x22, 0xf8, 0x29, 0xec } };
static const GUID SDL_MF_MT_DEFAULT_STRIDE = { 0x644b4e48, 0x1e02, 0x4516, { 0xb0, 0xeb, 0xc0, 0x1c, 0xa9, 0xd4, 0x9a, 0xc6 } };
static const GUID SDL_MF_MT_MAJOR_TYPE = { 0x48eba18e, 0xf8c9, 0x4687, { 0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f } };
static const GUID SDL_MF_MT_SUBTYPE = { 0xf7e34c9a, 0x42e8, 0x4714, { 0xb7, 0x4b, 0xcb, 0x29, 0xd7, 0x2c, 0x35, 0xe5 } };
static const GUID SDL_MF_MT_FRAME_SIZE = { 0x1652c33d, 0xd6b2, 0x4012, { 0xb8, 0x34, 0x72, 0x03, 0x08, 0x49, 0xa3, 0x7d } };
static const GUID SDL_MF_MT_FRAME_RATE = { 0xc459a2e8, 0x3d2c, 0x4e44, { 0xb1, 0x32, 0xfe, 0xe5, 0x15, 0x6c, 0x7b, 0xb0 } };
static const GUID SDL_MFMediaType_Video = { 0x73646976, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } };
#define SDL_DEFINE_MEDIATYPE_GUID(name, fmt) static const GUID SDL_##name = { fmt, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB555, 24);
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB565, 23);
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB24, 20);
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB32, 22);
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_ARGB32, 21);
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_A2R10G10B10, 31);
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YV12, FCC('YV12'));
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_IYUV, FCC('IYUV'));
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YUY2, FCC('YUY2'));
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_UYVY, FCC('UYVY'));
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YVYU, FCC('YVYU'));
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV12, FCC('NV12'));
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV21, FCC('NV21'));
#undef SDL_DEFINE_MEDIATYPE_GUID
static const struct { const GUID *guid; const Uint32 sdlfmt; } fmtmappings[] = {
// This is not every possible format, just popular ones that SDL can reasonably handle.
// (and we should probably trim this list more.)
{ &SDL_MFVideoFormat_RGB555, SDL_PIXELFORMAT_XRGB1555 },
{ &SDL_MFVideoFormat_RGB565, SDL_PIXELFORMAT_RGB565 },
{ &SDL_MFVideoFormat_RGB24, SDL_PIXELFORMAT_RGB24 },
{ &SDL_MFVideoFormat_RGB32, SDL_PIXELFORMAT_XRGB8888 },
{ &SDL_MFVideoFormat_ARGB32, SDL_PIXELFORMAT_ARGB8888 },
{ &SDL_MFVideoFormat_A2R10G10B10, SDL_PIXELFORMAT_ARGB2101010 },
{ &SDL_MFVideoFormat_YV12, SDL_PIXELFORMAT_YV12 },
{ &SDL_MFVideoFormat_IYUV, SDL_PIXELFORMAT_IYUV },
{ &SDL_MFVideoFormat_YUY2, SDL_PIXELFORMAT_YUY2 },
{ &SDL_MFVideoFormat_UYVY, SDL_PIXELFORMAT_UYVY },
{ &SDL_MFVideoFormat_YVYU, SDL_PIXELFORMAT_YVYU },
{ &SDL_MFVideoFormat_NV12, SDL_PIXELFORMAT_NV12 },
{ &SDL_MFVideoFormat_NV21, SDL_PIXELFORMAT_NV21 }
};
static Uint32 MFVidFmtGuidToSDLFmt(const GUID *guid) {
for (size_t i = 0; i < SDL_arraysize(fmtmappings); i++) {
if (WIN_IsEqualGUID(guid, fmtmappings[i].guid)) {
return fmtmappings[i].sdlfmt;
}
}
return SDL_PIXELFORMAT_UNKNOWN;
}
static const GUID *SDLFmtToMFVidFmtGuid(Uint32 sdlfmt) {
for (size_t i = 0; i < SDL_arraysize(fmtmappings); i++) {
if (fmtmappings[i].sdlfmt == sdlfmt) {
return fmtmappings[i].guid;
}
}
return NULL;
}
// handle to Media Foundation libs--Vista and later!--for access to the Media Foundation API.
// mf.dll ...
static HMODULE libmf = NULL;
typedef HRESULT(WINAPI *pfnMFEnumDeviceSources)(IMFAttributes *,IMFActivate ***,UINT32 *);
typedef HRESULT(WINAPI *pfnMFCreateDeviceSource)(IMFAttributes *, IMFMediaSource **);
static pfnMFEnumDeviceSources pMFEnumDeviceSources = NULL;
static pfnMFCreateDeviceSource pMFCreateDeviceSource = NULL;
// mfplat.dll ...
static HMODULE libmfplat = NULL;
typedef HRESULT(WINAPI *pfnMFStartup)(ULONG, DWORD);
typedef HRESULT(WINAPI *pfnMFShutdown)(void);
typedef HRESULT(WINAPI *pfnMFCreateAttributes)(IMFAttributes **, UINT32);
typedef HRESULT(WINAPI *pfnMFCreateMediaType)(IMFMediaType **);
typedef HRESULT(WINAPI *pfnMFGetStrideForBitmapInfoHeader)(DWORD, DWORD, LONG *);
static pfnMFStartup pMFStartup = NULL;
static pfnMFShutdown pMFShutdown = NULL;
static pfnMFCreateAttributes pMFCreateAttributes = NULL;
static pfnMFCreateMediaType pMFCreateMediaType = NULL;
static pfnMFGetStrideForBitmapInfoHeader pMFGetStrideForBitmapInfoHeader = NULL;
// mfreadwrite.dll ...
static HMODULE libmfreadwrite = NULL;
typedef HRESULT(WINAPI *pfnMFCreateSourceReaderFromMediaSource)(IMFMediaSource *, IMFAttributes *, IMFSourceReader **);
static pfnMFCreateSourceReaderFromMediaSource pMFCreateSourceReaderFromMediaSource = NULL;
typedef struct SDL_PrivateCameraData
{
IMFSourceReader *srcreader;
IMFSample *current_sample;
int pitch;
} SDL_PrivateCameraData;
static int MEDIAFOUNDATION_WaitDevice(SDL_CameraDevice *device)
{
SDL_assert(device->hidden->current_sample == NULL);
IMFSourceReader *srcreader = device->hidden->srcreader;
IMFSample *sample = NULL;
while (!SDL_AtomicGet(&device->shutdown)) {
DWORD stream_flags = 0;
const HRESULT ret = IMFSourceReader_ReadSample(srcreader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &stream_flags, NULL, &sample);
if (FAILED(ret)) {
return -1; // ruh roh.
}
// we currently ignore stream_flags format changes, but my _hope_ is that IMFSourceReader is handling this and
// will continue to give us the explictly-specified format we requested when opening the device, though, and
// we don't have to manually deal with it.
if (sample != NULL) {
break;
} else if (stream_flags & (MF_SOURCE_READERF_ERROR | MF_SOURCE_READERF_ENDOFSTREAM)) {
return -1; // apparently this camera has gone down. :/
}
// otherwise, there was some minor burp, probably; just try again.
}
device->hidden->current_sample = sample;
return 0;
}
#if KEEP_ACQUIRED_BUFFERS_LOCKED
#define PROP_SURFACE_IMFOBJS_POINTER "SDL.camera.mediafoundation.imfobjs"
typedef struct SDL_IMFObjects
{
IMF2DBuffer2 *buffer2d2;
IMF2DBuffer *buffer2d;
IMFMediaBuffer *buffer;
IMFSample *sample;
} SDL_IMFObjects;
static void SDLCALL CleanupIMF2DBuffer2(void *userdata, void *value)
{
SDL_IMFObjects *objs = (SDL_IMFObjects *)value;
IMF2DBuffer2_Unlock2D(objs->buffer2d2);
IMF2DBuffer2_Release(objs->buffer2d2);
IMFMediaBuffer_Release(objs->buffer);
IMFSample_Release(objs->sample);
SDL_free(objs);
}
static void SDLCALL CleanupIMF2DBuffer(void *userdata, void *value)
{
SDL_IMFObjects *objs = (SDL_IMFObjects *)value;
IMF2DBuffer_Unlock2D(objs->buffer2d);
IMF2DBuffer_Release(objs->buffer2d);
IMFMediaBuffer_Release(objs->buffer);
IMFSample_Release(objs->sample);
SDL_free(objs);
}
static void SDLCALL CleanupIMFMediaBuffer(void *userdata, void *value)
{
SDL_IMFObjects *objs = (SDL_IMFObjects *)value;
IMFMediaBuffer_Unlock(objs->buffer);
IMFMediaBuffer_Release(objs->buffer);
IMFSample_Release(objs->sample);
SDL_free(objs);
}
static int MEDIAFOUNDATION_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS)
{
SDL_assert(device->hidden->current_sample != NULL);
int retval = 1;
HRESULT ret;
LONGLONG timestamp100NS = 0;
SDL_IMFObjects *objs = (SDL_IMFObjects *) SDL_calloc(1, sizeof (SDL_IMFObjects));
if (objs == NULL) {
return -1;
}
objs->sample = device->hidden->current_sample;
device->hidden->current_sample = NULL;
const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame);
if (!surfprops) {
retval = -1;
} else {
ret = IMFSample_GetSampleTime(objs->sample, &timestamp100NS);
if (FAILED(ret)) {
retval = -1;
}
*timestampNS = timestamp100NS * 100; // the timestamps are in 100-nanosecond increments; move to full nanoseconds.
}
ret = (retval < 0) ? E_FAIL : IMFSample_ConvertToContiguousBuffer(objs->sample, &objs->buffer); /*IMFSample_GetBufferByIndex(objs->sample, 0, &objs->buffer);*/
if (FAILED(ret)) {
SDL_free(objs);
retval = -1;
} else {
BYTE *pixels = NULL;
LONG pitch = 0;
if (SUCCEEDED(IMFMediaBuffer_QueryInterface(objs->buffer, &SDL_IID_IMF2DBuffer2, (void **)&objs->buffer2d2))) {
BYTE *bufstart = NULL;
DWORD buflen = 0;
ret = IMF2DBuffer2_Lock2DSize(objs->buffer2d2, MF2DBuffer_LockFlags_Read, &pixels, &pitch, &bufstart, &buflen);
if (FAILED(ret)) {
retval = -1;
CleanupIMF2DBuffer2(NULL, objs);
} else {
frame->pixels = pixels;
frame->pitch = (int) pitch;
if (SDL_SetPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMF2DBuffer2, NULL) == -1) {
CleanupIMF2DBuffer2(NULL, objs);
retval = -1;
}
}
} else if (SUCCEEDED(IMFMediaBuffer_QueryInterface(objs->buffer, &SDL_IID_IMF2DBuffer, (void **)&objs->buffer2d))) {
ret = IMF2DBuffer_Lock2D(objs->buffer2d, &pixels, &pitch);
if (FAILED(ret)) {
CleanupIMF2DBuffer(NULL, objs);
retval = -1;
} else {
frame->pixels = pixels;
frame->pitch = (int) pitch;
if (SDL_SetPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMF2DBuffer, NULL) == -1) {
CleanupIMF2DBuffer(NULL, objs);
retval = -1;
}
}
} else {
DWORD maxlen = 0, currentlen = 0;
ret = IMFMediaBuffer_Lock(objs->buffer, &pixels, &maxlen, &currentlen);
if (FAILED(ret)) {
CleanupIMFMediaBuffer(NULL, objs);
retval = -1;
} else {
pitch = (LONG) device->hidden->pitch;
if (pitch < 0) { // image rows are reversed.
pixels += -pitch * (frame->h - 1);
}
frame->pixels = pixels;
frame->pitch = (int) pitch;
if (SDL_SetPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMFMediaBuffer, NULL) == -1) {
CleanupIMFMediaBuffer(NULL, objs);
retval = -1;
}
}
}
}
if (retval < 0) {
*timestampNS = 0;
}
return retval;
}
static void MEDIAFOUNDATION_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame)
{
const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame);
if (surfprops) {
// this will release the IMFBuffer and IMFSample objects for this frame.
SDL_ClearProperty(surfprops, PROP_SURFACE_IMFOBJS_POINTER);
}
}
#else
static int MEDIAFOUNDATION_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS)
{
SDL_assert(device->hidden->current_sample != NULL);
int retval = 1;
HRESULT ret;
LONGLONG timestamp100NS = 0;
IMFSample *sample = device->hidden->current_sample;
device->hidden->current_sample = NULL;
const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame);
if (!surfprops) {
retval = -1;
} else {
ret = IMFSample_GetSampleTime(sample, &timestamp100NS);
if (FAILED(ret)) {
retval = -1;
}
*timestampNS = timestamp100NS * 100; // the timestamps are in 100-nanosecond increments; move to full nanoseconds.
}
IMFMediaBuffer *buffer = NULL;
ret = (retval < 0) ? E_FAIL : IMFSample_ConvertToContiguousBuffer(sample, &buffer); /*IMFSample_GetBufferByIndex(sample, 0, &buffer);*/
if (FAILED(ret)) {
retval = -1;
} else {
IMF2DBuffer *buffer2d = NULL;
IMF2DBuffer2 *buffer2d2 = NULL;
BYTE *pixels = NULL;
LONG pitch = 0;
if (SUCCEEDED(IMFMediaBuffer_QueryInterface(buffer, &SDL_IID_IMF2DBuffer2, (void **)&buffer2d2))) {
BYTE *bufstart = NULL;
DWORD buflen = 0;
ret = IMF2DBuffer2_Lock2DSize(buffer2d2, MF2DBuffer_LockFlags_Read, &pixels, &pitch, &bufstart, &buflen);
if (FAILED(ret)) {
retval = -1;
} else {
frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen);
if (frame->pixels == NULL) {
retval = -1;
} else {
SDL_memcpy(frame->pixels, pixels, buflen);
frame->pitch = (int)pitch;
}
IMF2DBuffer2_Unlock2D(buffer2d2);
}
IMF2DBuffer2_Release(buffer2d2);
} else if (SUCCEEDED(IMFMediaBuffer_QueryInterface(buffer, &SDL_IID_IMF2DBuffer, (void **)&buffer2d))) {
ret = IMF2DBuffer_Lock2D(buffer2d, &pixels, &pitch);
if (FAILED(ret)) {
retval = -1;
} else {
BYTE *bufstart = pixels;
const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h;
if (pitch < 0) { // image rows are reversed.
bufstart += -pitch * (frame->h - 1);
}
frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen);
if (frame->pixels == NULL) {
retval = -1;
} else {
SDL_memcpy(frame->pixels, bufstart, buflen);
frame->pitch = (int)pitch;
}
IMF2DBuffer_Unlock2D(buffer2d);
}
IMF2DBuffer_Release(buffer2d);
} else {
DWORD maxlen = 0, currentlen = 0;
ret = IMFMediaBuffer_Lock(buffer, &pixels, &maxlen, &currentlen);
if (FAILED(ret)) {
retval = -1;
} else {
BYTE *bufstart = pixels;
pitch = (LONG)device->hidden->pitch;
const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h;
if (pitch < 0) { // image rows are reversed.
bufstart += -pitch * (frame->h - 1);
}
frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen);
if (frame->pixels == NULL) {
retval = -1;
} else {
SDL_memcpy(frame->pixels, bufstart, buflen);
frame->pitch = (int)pitch;
}
IMFMediaBuffer_Unlock(buffer);
}
}
IMFMediaBuffer_Release(buffer);
}
IMFSample_Release(sample);
if (retval < 0) {
*timestampNS = 0;
}
return retval;
}
static void MEDIAFOUNDATION_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame)
{
SDL_aligned_free(frame->pixels);
}
#endif
static void MEDIAFOUNDATION_CloseDevice(SDL_CameraDevice *device)
{
if (device && device->hidden) {
if (device->hidden->srcreader) {
IMFSourceReader_Release(device->hidden->srcreader);
}
if (device->hidden->current_sample) {
IMFSample_Release(device->hidden->current_sample);
}
SDL_free(device->hidden);
device->hidden = NULL;
}
}
// this function is from https://learn.microsoft.com/en-us/windows/win32/medfound/uncompressed-video-buffers
static HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
{
LONG lStride = 0;
// Try to get the default stride from the media type.
HRESULT ret = IMFMediaType_GetUINT32(pType, &SDL_MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
if (FAILED(ret)) {
// Attribute not set. Try to calculate the default stride.
GUID subtype = GUID_NULL;
UINT32 width = 0;
UINT32 height = 0;
UINT64 val = 0;
// Get the subtype and the image size.
ret = IMFMediaType_GetGUID(pType, &SDL_MF_MT_SUBTYPE, &subtype);
if (FAILED(ret)) {
goto done;
}
ret = IMFMediaType_GetUINT64(pType, &SDL_MF_MT_FRAME_SIZE, &val);
if (FAILED(ret)) {
goto done;
}
width = (UINT32) (val >> 32);
height = (UINT32) val;
ret = pMFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride);
if (FAILED(ret)) {
goto done;
}
// Set the attribute for later reference.
IMFMediaType_SetUINT32(pType, &SDL_MF_MT_DEFAULT_STRIDE, (UINT32) lStride);
}
if (SUCCEEDED(ret)) {
*plStride = lStride;
}
done:
return ret;
}
static int MEDIAFOUNDATION_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec)
{
const char *utf8symlink = (const char *) device->handle;
IMFAttributes *attrs = NULL;
LPWSTR wstrsymlink = NULL;
IMFMediaSource *source = NULL;
IMFMediaType *mediatype = NULL;
IMFSourceReader *srcreader = NULL;
DWORD num_streams = 0;
LONG lstride = 0;
//PROPVARIANT var;
HRESULT ret;
SDL_Log("MEDIAFOUNDATION spec format: %s", SDL_GetPixelFormatName(spec->format));
#if 0
IMFStreamDescriptor *streamdesc = NULL;
IMFPresentationDescriptor *presentdesc = NULL;
IMFMediaTypeHandler *handler = NULL;
#endif
#if DEBUG_CAMERA
SDL_Log("CAMERA: opening device with symlink of '%s'", utf8symlink);
#endif
wstrsymlink = WIN_UTF8ToString(utf8symlink);
if (!wstrsymlink) {
goto failed;
}
#define CHECK_HRESULT(what, r) if (FAILED(r)) { WIN_SetErrorFromHRESULT(what " failed", r); goto failed; }
ret = pMFCreateAttributes(&attrs, 1);
CHECK_HRESULT("MFCreateAttributes", ret);
ret = IMFAttributes_SetGUID(attrs, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
CHECK_HRESULT("IMFAttributes_SetGUID(srctype)", ret);
ret = IMFAttributes_SetString(attrs, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, wstrsymlink);
CHECK_HRESULT("IMFAttributes_SetString(symlink)", ret);
ret = pMFCreateDeviceSource(attrs, &source);
CHECK_HRESULT("MFCreateDeviceSource", ret);
IMFAttributes_Release(attrs);
SDL_free(wstrsymlink);
attrs = NULL;
wstrsymlink = NULL;
// !!! FIXME: I think it'd be nice to do this without an IMFSourceReader,
// since it's just utility code that has to handle more complex media streams
// than we're dealing with, but this will do for now. The docs are slightly
// insistent that you should use one, though...Maybe it's extremely hard
// to handle directly at the IMFMediaSource layer...?
ret = pMFCreateSourceReaderFromMediaSource(source, NULL, &srcreader);
CHECK_HRESULT("MFCreateSourceReaderFromMediaSource", ret);
// !!! FIXME: do we actually have to find the media type object in the source reader or can we just roll our own like this?
ret = pMFCreateMediaType(&mediatype);
CHECK_HRESULT("MFCreateMediaType", ret);
ret = IMFMediaType_SetGUID(mediatype, &SDL_MF_MT_MAJOR_TYPE, &SDL_MFMediaType_Video);
CHECK_HRESULT("IMFMediaType_SetGUID(major_type)", ret);
ret = IMFMediaType_SetGUID(mediatype, &SDL_MF_MT_SUBTYPE, SDLFmtToMFVidFmtGuid(spec->format));
CHECK_HRESULT("IMFMediaType_SetGUID(subtype)", ret);
ret = IMFMediaType_SetUINT64(mediatype, &SDL_MF_MT_FRAME_SIZE, (((UINT64)spec->width) << 32) | ((UINT64)spec->height));
CHECK_HRESULT("MFSetAttributeSize(frame_size)", ret);
ret = IMFMediaType_SetUINT64(mediatype, &SDL_MF_MT_FRAME_RATE, (((UINT64)spec->interval_numerator) << 32) | ((UINT64)spec->interval_denominator));
CHECK_HRESULT("MFSetAttributeRatio(frame_rate)", ret);
ret = IMFSourceReader_SetCurrentMediaType(srcreader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, mediatype);
CHECK_HRESULT("IMFSourceReader_SetCurrentMediaType", ret);
#if 0 // this (untested thing) is what we would do to get started with a IMFMediaSource that _doesn't_ use IMFSourceReader...
ret = IMFMediaSource_CreatePresentationDescriptor(source, &presentdesc);
CHECK_HRESULT("IMFMediaSource_CreatePresentationDescriptor", ret);
ret = IMFPresentationDescriptor_GetStreamDescriptorCount(presentdesc, &num_streams);
CHECK_HRESULT("IMFPresentationDescriptor_GetStreamDescriptorCount", ret);
for (DWORD i = 0; i < num_streams; i++) {
BOOL selected = FALSE;
ret = IMFPresentationDescriptor_GetStreamDescriptorByIndex(presentdesc, i, &selected, &streamdesc);
CHECK_HRESULT("IMFPresentationDescriptor_GetStreamDescriptorByIndex", ret);
if (selected) {
ret = IMFStreamDescriptor_GetMediaTypeHandler(streamdesc, &handler);
CHECK_HRESULT("IMFStreamDescriptor_GetMediaTypeHandler", ret);
IMFMediaTypeHandler_SetCurrentMediaType(handler, mediatype);
IMFMediaTypeHandler_Release(handler);
handler = NULL;
}
IMFStreamDescriptor_Release(streamdesc);
streamdesc = NULL;
}
PropVariantInit(&var);
var.vt = VT_EMPTY;
ret = IMFMediaSource_Start(source, presentdesc, NULL, &var);
PropVariantClear(&var);
CHECK_HRESULT("IMFMediaSource_Start", ret);
IMFPresentationDescriptor_Release(presentdesc);
presentdesc = NULL;
#endif
ret = GetDefaultStride(mediatype, &lstride);
CHECK_HRESULT("GetDefaultStride", ret);
IMFMediaType_Release(mediatype);
mediatype = NULL;
device->hidden = (SDL_PrivateCameraData *) SDL_calloc(1, sizeof (SDL_PrivateCameraData));
if (!device->hidden) {
goto failed;
}
device->hidden->pitch = (int) lstride;
device->hidden->srcreader = srcreader;
IMFMediaSource_Release(source); // srcreader is holding a reference to this.
// There is no user permission prompt for camera access (I think?)
SDL_CameraDevicePermissionOutcome(device, SDL_TRUE);
#undef CHECK_HRESULT
return 0;
failed:
if (srcreader) {
IMFSourceReader_Release(srcreader);
}
#if 0
if (handler) {
IMFMediaTypeHandler_Release(handler);
}
if (streamdesc) {
IMFStreamDescriptor_Release(streamdesc);
}
if (presentdesc) {
IMFPresentationDescriptor_Release(presentdesc);
}
#endif
if (source) {
IMFMediaSource_Shutdown(source);
IMFMediaSource_Release(source);
}
if (mediatype) {
IMFMediaType_Release(mediatype);
}
if (attrs) {
IMFAttributes_Release(attrs);
}
SDL_free(wstrsymlink);
return -1;
}
static void MEDIAFOUNDATION_FreeDeviceHandle(SDL_CameraDevice *device)
{
if (device) {
SDL_free(device->handle); // the device's symlink string.
}
}
static char *QueryActivationObjectString(IMFActivate *activation, const GUID *pguid)
{
LPWSTR wstr = NULL;
UINT32 wlen = 0;
HRESULT ret = IMFActivate_GetAllocatedString(activation, pguid, &wstr, &wlen);
if (FAILED(ret)) {
return NULL;
}
char *utf8str = WIN_StringToUTF8(wstr);
CoTaskMemFree(wstr);
return utf8str;
}
static void GatherCameraSpecs(IMFMediaSource *source, CameraFormatAddData *add_data)
{
HRESULT ret;
// this has like a thousand steps. :/
SDL_zerop(add_data);
IMFPresentationDescriptor *presentdesc = NULL;
ret = IMFMediaSource_CreatePresentationDescriptor(source, &presentdesc);
if (FAILED(ret) || !presentdesc) {
return;
}
DWORD num_streams = 0;
ret = IMFPresentationDescriptor_GetStreamDescriptorCount(presentdesc, &num_streams);
if (FAILED(ret)) {
num_streams = 0;
}
for (DWORD i = 0; i < num_streams; i++) {
IMFStreamDescriptor *streamdesc = NULL;
BOOL selected = FALSE;
ret = IMFPresentationDescriptor_GetStreamDescriptorByIndex(presentdesc, i, &selected, &streamdesc);
if (FAILED(ret) || !streamdesc) {
continue;
}
if (selected) {
IMFMediaTypeHandler *handler = NULL;
ret = IMFStreamDescriptor_GetMediaTypeHandler(streamdesc, &handler);
if (SUCCEEDED(ret) && handler) {
DWORD num_mediatype = 0;
ret = IMFMediaTypeHandler_GetMediaTypeCount(handler, &num_mediatype);
if (FAILED(ret)) {
num_mediatype = 0;
}
for (DWORD j = 0; j < num_mediatype; j++) {
IMFMediaType *mediatype = NULL;
ret = IMFMediaTypeHandler_GetMediaTypeByIndex(handler, j, &mediatype);
if (SUCCEEDED(ret) && mediatype) {
GUID type;
ret = IMFMediaType_GetGUID(mediatype, &SDL_MF_MT_MAJOR_TYPE, &type);
if (SUCCEEDED(ret) && WIN_IsEqualGUID(&type, &SDL_MFMediaType_Video)) {
ret = IMFMediaType_GetGUID(mediatype, &SDL_MF_MT_SUBTYPE, &type);
if (SUCCEEDED(ret)) {
const Uint32 sdlfmt = MFVidFmtGuidToSDLFmt(&type);
if (sdlfmt != SDL_PIXELFORMAT_UNKNOWN) {
UINT64 val = 0;
UINT32 w = 0, h = 0;
ret = IMFMediaType_GetUINT64(mediatype, &SDL_MF_MT_FRAME_SIZE, &val);
w = (UINT32)(val >> 32);
h = (UINT32)val;
if (SUCCEEDED(ret) && w && h) {
UINT32 interval_numerator = 0, interval_denominator = 0;
ret = IMFMediaType_GetUINT64(mediatype, &SDL_MF_MT_FRAME_RATE, &val);
interval_numerator = (UINT32)(val >> 32);
interval_denominator = (UINT32)val;
if (SUCCEEDED(ret) && interval_numerator && interval_denominator) {
SDL_AddCameraFormat(add_data, sdlfmt, (int) w, (int) h, (int) interval_numerator, (int) interval_denominator); // whew.
}
}
}
}
}
IMFMediaType_Release(mediatype);
}
}
IMFMediaTypeHandler_Release(handler);
}
}
IMFStreamDescriptor_Release(streamdesc);
}
IMFPresentationDescriptor_Release(presentdesc);
}
static SDL_bool FindMediaFoundationCameraDeviceBySymlink(SDL_CameraDevice *device, void *userdata)
{
return (SDL_strcmp((const char *) device->handle, (const char *) userdata) == 0);
}
static void MaybeAddDevice(IMFActivate *activation)
{
char *symlink = QueryActivationObjectString(activation, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK);
if (SDL_FindPhysicalCameraDeviceByCallback(FindMediaFoundationCameraDeviceBySymlink, symlink)) {
SDL_free(symlink);
return; // already have this one.
}
char *name = QueryActivationObjectString(activation, &MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
if (name && symlink) {
IMFMediaSource *source = NULL;
// "activating" here only creates an object, it doesn't open the actual camera hardware or start recording.
HRESULT ret = IMFActivate_ActivateObject(activation, &SDL_IID_IMFMediaSource, &source);
if (SUCCEEDED(ret) && source) {
CameraFormatAddData add_data;
GatherCameraSpecs(source, &add_data);
if (add_data.num_specs > 0) {
SDL_AddCameraDevice(name, add_data.num_specs, add_data.specs, symlink);
}
SDL_free(add_data.specs);
IMFActivate_ShutdownObject(activation);
IMFMediaSource_Release(source);
}
}
SDL_free(name);
}
static void MEDIAFOUNDATION_DetectDevices(void)
{
// !!! FIXME: use CM_Register_Notification (Win8+) to get device notifications.
// !!! FIXME: Earlier versions can use RegisterDeviceNotification, but I'm not bothering: no hotplug for you!
HRESULT ret;
IMFAttributes *attrs = NULL;
ret = pMFCreateAttributes(&attrs, 1);
if (FAILED(ret)) {
return; // oh well, no cameras for you.
}
// !!! FIXME: We need these GUIDs hardcoded in this file.
ret = IMFAttributes_SetGUID(attrs, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (FAILED(ret)) {
IMFAttributes_Release(attrs);
return; // oh well, no cameras for you.
}
IMFActivate **activations = NULL;
UINT32 total = 0;
ret = pMFEnumDeviceSources(attrs, &activations, &total);
IMFAttributes_Release(attrs);
if (FAILED(ret)) {
return; // oh well, no cameras for you.
}
for (UINT32 i = 0; i < total; i++) {
MaybeAddDevice(activations[i]);
IMFActivate_Release(activations[i]);
}
CoTaskMemFree(activations);
}
static void MEDIAFOUNDATION_Deinitialize(void)
{
pMFShutdown();
FreeLibrary(libmfreadwrite);
libmfreadwrite = NULL;
FreeLibrary(libmfplat);
libmfplat = NULL;
FreeLibrary(libmf);
libmf = NULL;
pMFEnumDeviceSources = NULL;
pMFCreateDeviceSource = NULL;
pMFStartup = NULL;
pMFShutdown = NULL;
pMFCreateAttributes = NULL;
pMFCreateMediaType = NULL;
pMFCreateSourceReaderFromMediaSource = NULL;
pMFGetStrideForBitmapInfoHeader = NULL;
}
static SDL_bool MEDIAFOUNDATION_Init(SDL_CameraDriverImpl *impl)
{
// !!! FIXME: slide this off into a subroutine
HANDLE mf = LoadLibrary(TEXT("Mf.dll")); // this library is available in Vista and later, but also can be on XP with service packs and Windows
if (!mf) {
return SDL_FALSE;
}
HANDLE mfplat = LoadLibrary(TEXT("Mfplat.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now!
if (!mfplat) {
FreeLibrary(mf);
return SDL_FALSE;
}
HANDLE mfreadwrite = LoadLibrary(TEXT("Mfreadwrite.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now!
if (!mfreadwrite) {
FreeLibrary(mfplat);
FreeLibrary(mf);
return SDL_FALSE;
}
SDL_bool okay = SDL_TRUE;
#define LOADSYM(lib, fn) if (okay) { p##fn = (pfn##fn) GetProcAddress(lib, #fn); if (!p##fn) { okay = SDL_FALSE; } }
LOADSYM(mf, MFEnumDeviceSources);
LOADSYM(mf, MFCreateDeviceSource);
LOADSYM(mfplat, MFStartup);
LOADSYM(mfplat, MFShutdown);
LOADSYM(mfplat, MFCreateAttributes);
LOADSYM(mfplat, MFCreateMediaType);
LOADSYM(mfplat, MFGetStrideForBitmapInfoHeader);
LOADSYM(mfreadwrite, MFCreateSourceReaderFromMediaSource);
#undef LOADSYM
if (!okay) {
FreeLibrary(mfreadwrite);
FreeLibrary(mfplat);
FreeLibrary(mf);
}
libmf = mf;
libmfplat = mfplat;
libmfreadwrite = mfreadwrite;
const HRESULT ret = pMFStartup(MF_VERSION, MFSTARTUP_LITE);
if (FAILED(ret)) {
FreeLibrary(libmfplat);
libmfplat = NULL;
FreeLibrary(libmf);
libmf = NULL;
return SDL_FALSE;
}
impl->DetectDevices = MEDIAFOUNDATION_DetectDevices;
impl->OpenDevice = MEDIAFOUNDATION_OpenDevice;
impl->CloseDevice = MEDIAFOUNDATION_CloseDevice;
impl->WaitDevice = MEDIAFOUNDATION_WaitDevice;
impl->AcquireFrame = MEDIAFOUNDATION_AcquireFrame;
impl->ReleaseFrame = MEDIAFOUNDATION_ReleaseFrame;
impl->FreeDeviceHandle = MEDIAFOUNDATION_FreeDeviceHandle;
impl->Deinitialize = MEDIAFOUNDATION_Deinitialize;
return SDL_TRUE;
}
CameraBootStrap MEDIAFOUNDATION_bootstrap = {
"mediafoundation", "SDL Windows Media Foundation camera driver", MEDIAFOUNDATION_Init, SDL_FALSE
};
#endif // SDL_CAMERA_DRIVER_MEDIAFOUNDATION

View File

@ -631,39 +631,7 @@ static SDL_bool FindV4L2CameraDeviceByBusInfoCallback(SDL_CameraDevice *device,
return (SDL_strcmp(handle->bus_info, (const char *) userdata) == 0);
}
typedef struct FormatAddData
{
SDL_CameraSpec *specs;
int num_specs;
int allocated_specs;
} FormatAddData;
static int AddCameraCompleteFormat(FormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator)
{
SDL_assert(data != NULL);
if (data->allocated_specs <= data->num_specs) {
const int newalloc = data->allocated_specs ? (data->allocated_specs * 2) : 16;
void *ptr = SDL_realloc(data->specs, sizeof (SDL_CameraSpec) * newalloc);
if (!ptr) {
return -1;
}
data->specs = (SDL_CameraSpec *) ptr;
data->allocated_specs = newalloc;
}
SDL_CameraSpec *spec = &data->specs[data->num_specs];
spec->format = fmt;
spec->width = w;
spec->height = h;
spec->interval_numerator = interval_numerator;
spec->interval_denominator = interval_denominator;
data->num_specs++;
return 0;
}
static int AddCameraFormat(const int fd, FormatAddData *data, Uint32 sdlfmt, Uint32 v4l2fmt, int w, int h)
static int AddCameraFormat(const int fd, CameraFormatAddData *data, Uint32 sdlfmt, Uint32 v4l2fmt, int w, int h)
{
struct v4l2_frmivalenum frmivalenum;
SDL_zero(frmivalenum);
@ -679,7 +647,7 @@ static int AddCameraFormat(const int fd, FormatAddData *data, Uint32 sdlfmt, Uin
const float fps = (float) denominator / (float) numerator;
SDL_Log("CAMERA: * Has discrete frame interval (%d / %d), fps=%f", numerator, denominator, fps);
#endif
if (AddCameraCompleteFormat(data, sdlfmt, w, h, numerator, denominator) == -1) {
if (SDL_AddCameraFormat(data, sdlfmt, w, h, numerator, denominator) == -1) {
return -1; // Probably out of memory; we'll go with what we have, if anything.
}
frmivalenum.index++; // set up for the next one.
@ -691,7 +659,7 @@ static int AddCameraFormat(const int fd, FormatAddData *data, Uint32 sdlfmt, Uin
const float fps = (float) d / (float) n;
SDL_Log("CAMERA: * Has %s frame interval (%d / %d), fps=%f", (frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) ? "stepwise" : "continuous", n, d, fps);
#endif
if (AddCameraCompleteFormat(data, sdlfmt, w, h, n, d) == -1) {
if (SDL_AddCameraFormat(data, sdlfmt, w, h, n, d) == -1) {
return -1; // Probably out of memory; we'll go with what we have, if anything.
}
d += (int) frmivalenum.stepwise.step.denominator;
@ -739,7 +707,7 @@ static void MaybeAddDevice(const char *path)
SDL_Log("CAMERA: V4L2 camera path='%s' bus_info='%s' name='%s'", path, (const char *) vcap.bus_info, vcap.card);
#endif
FormatAddData add_data;
CameraFormatAddData add_data;
SDL_zero(add_data);
struct v4l2_fmtdesc fmtdesc;
@ -911,5 +879,5 @@ CameraBootStrap V4L2_bootstrap = {
"v4l2", "SDL Video4Linux2 camera driver", V4L2_Init, SDL_FALSE
};
#endif // SDL_CAMERA_V4L2
#endif // SDL_CAMERA_DRIVER_V4L2