summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/camera/mediafoundation/SDL_camera_mediafoundation.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/camera/mediafoundation/SDL_camera_mediafoundation.c')
-rw-r--r--contrib/SDL-3.2.8/src/camera/mediafoundation/SDL_camera_mediafoundation.c1143
1 files changed, 1143 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/camera/mediafoundation/SDL_camera_mediafoundation.c b/contrib/SDL-3.2.8/src/camera/mediafoundation/SDL_camera_mediafoundation.c
new file mode 100644
index 0000000..d9d627d
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/camera/mediafoundation/SDL_camera_mediafoundation.c
@@ -0,0 +1,1143 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23// the Windows Media Foundation API
24
25#ifdef SDL_CAMERA_DRIVER_MEDIAFOUNDATION
26
27#define COBJMACROS
28
29// this seems to be a bug in mfidl.h, just define this to avoid the problem section.
30#define __IMFVideoProcessorControl3_INTERFACE_DEFINED__
31
32#include "../../core/windows/SDL_windows.h"
33
34#include <mfapi.h>
35#include <mfidl.h>
36#include <mfreadwrite.h>
37
38#include "../SDL_syscamera.h"
39#include "../SDL_camera_c.h"
40
41static const IID SDL_IID_IMFMediaSource = { 0x279a808d, 0xaec7, 0x40c8, { 0x9c, 0x6b, 0xa6, 0xb4, 0x92, 0xc7, 0x8a, 0x66 } };
42static const IID SDL_IID_IMF2DBuffer = { 0x7dc9d5f9, 0x9ed9, 0x44ec, { 0x9b, 0xbf, 0x06, 0x00, 0xbb, 0x58, 0x9f, 0xbb } };
43static const IID SDL_IID_IMF2DBuffer2 = { 0x33ae5ea6, 0x4316, 0x436f, { 0x8d, 0xdd, 0xd7, 0x3d, 0x22, 0xf8, 0x29, 0xec } };
44static const GUID SDL_MF_MT_DEFAULT_STRIDE = { 0x644b4e48, 0x1e02, 0x4516, { 0xb0, 0xeb, 0xc0, 0x1c, 0xa9, 0xd4, 0x9a, 0xc6 } };
45static const GUID SDL_MF_MT_MAJOR_TYPE = { 0x48eba18e, 0xf8c9, 0x4687, { 0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f } };
46static const GUID SDL_MF_MT_SUBTYPE = { 0xf7e34c9a, 0x42e8, 0x4714, { 0xb7, 0x4b, 0xcb, 0x29, 0xd7, 0x2c, 0x35, 0xe5 } };
47static const GUID SDL_MF_MT_VIDEO_NOMINAL_RANGE = { 0xc21b8ee5, 0xb956, 0x4071, { 0x8d, 0xaf, 0x32, 0x5e, 0xdf, 0x5c, 0xab, 0x11 } };
48static const GUID SDL_MF_MT_VIDEO_PRIMARIES = { 0xdbfbe4d7, 0x0740, 0x4ee0, { 0x81, 0x92, 0x85, 0x0a, 0xb0, 0xe2, 0x19, 0x35 } };
49static const GUID SDL_MF_MT_TRANSFER_FUNCTION = { 0x5fb0fce9, 0xbe5c, 0x4935, { 0xa8, 0x11, 0xec, 0x83, 0x8f, 0x8e, 0xed, 0x93 } };
50static const GUID SDL_MF_MT_YUV_MATRIX = { 0x3e23d450, 0x2c75, 0x4d25, { 0xa0, 0x0e, 0xb9, 0x16, 0x70, 0xd1, 0x23, 0x27 } };
51static const GUID SDL_MF_MT_VIDEO_CHROMA_SITING = { 0x65df2370, 0xc773, 0x4c33, { 0xaa, 0x64, 0x84, 0x3e, 0x06, 0x8e, 0xfb, 0x0c } };
52static const GUID SDL_MF_MT_FRAME_SIZE = { 0x1652c33d, 0xd6b2, 0x4012, { 0xb8, 0x34, 0x72, 0x03, 0x08, 0x49, 0xa3, 0x7d } };
53static const GUID SDL_MF_MT_FRAME_RATE = { 0xc459a2e8, 0x3d2c, 0x4e44, { 0xb1, 0x32, 0xfe, 0xe5, 0x15, 0x6c, 0x7b, 0xb0 } };
54static const GUID SDL_MFMediaType_Video = { 0x73646976, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } };
55static const IID SDL_MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME = { 0x60d0e559, 0x52f8, 0x4fa2, { 0xbb, 0xce, 0xac, 0xdb, 0x34, 0xa8, 0xec, 0x1 } };
56static const IID SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE = { 0xc60ac5fe, 0x252a, 0x478f, { 0xa0, 0xef, 0xbc, 0x8f, 0xa5, 0xf7, 0xca, 0xd3 } };
57static const IID SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK = { 0x58f0aad8, 0x22bf, 0x4f8a, { 0xbb, 0x3d, 0xd2, 0xc4, 0x97, 0x8c, 0x6e, 0x2f } };
58static const IID SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID = { 0x8ac3587a, 0x4ae7, 0x42d8, { 0x99, 0xe0, 0x0a, 0x60, 0x13, 0xee, 0xf9, 0x0f } };
59
60#ifdef __GNUC__
61#pragma GCC diagnostic push
62#pragma GCC diagnostic ignored "-Wmultichar"
63#endif
64
65#define SDL_DEFINE_MEDIATYPE_GUID(name, fmt) static const GUID SDL_##name = { fmt, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }
66SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB555, 24);
67SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB565, 23);
68SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB24, 20);
69SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB32, 22);
70SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_ARGB32, 21);
71SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_A2R10G10B10, 31);
72SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YV12, FCC('YV12'));
73SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_IYUV, FCC('IYUV'));
74SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YUY2, FCC('YUY2'));
75SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_UYVY, FCC('UYVY'));
76SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YVYU, FCC('YVYU'));
77SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV12, FCC('NV12'));
78SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV21, FCC('NV21'));
79SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_MJPG, FCC('MJPG'));
80#undef SDL_DEFINE_MEDIATYPE_GUID
81
82#ifdef __GNUC__
83#pragma GCC diagnostic pop
84#endif
85
86static const struct
87{
88 const GUID *guid;
89 SDL_PixelFormat format;
90 SDL_Colorspace colorspace;
91} fmtmappings[] = {
92 // This is not every possible format, just popular ones that SDL can reasonably handle.
93 // (and we should probably trim this list more.)
94 { &SDL_MFVideoFormat_RGB555, SDL_PIXELFORMAT_XRGB1555, SDL_COLORSPACE_SRGB },
95 { &SDL_MFVideoFormat_RGB565, SDL_PIXELFORMAT_RGB565, SDL_COLORSPACE_SRGB },
96 { &SDL_MFVideoFormat_RGB24, SDL_PIXELFORMAT_RGB24, SDL_COLORSPACE_SRGB },
97 { &SDL_MFVideoFormat_RGB32, SDL_PIXELFORMAT_XRGB8888, SDL_COLORSPACE_SRGB },
98 { &SDL_MFVideoFormat_ARGB32, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB },
99 { &SDL_MFVideoFormat_A2R10G10B10, SDL_PIXELFORMAT_ARGB2101010, SDL_COLORSPACE_SRGB },
100 { &SDL_MFVideoFormat_YV12, SDL_PIXELFORMAT_YV12, SDL_COLORSPACE_BT709_LIMITED },
101 { &SDL_MFVideoFormat_IYUV, SDL_PIXELFORMAT_IYUV, SDL_COLORSPACE_BT709_LIMITED },
102 { &SDL_MFVideoFormat_YUY2, SDL_PIXELFORMAT_YUY2, SDL_COLORSPACE_BT709_LIMITED },
103 { &SDL_MFVideoFormat_UYVY, SDL_PIXELFORMAT_UYVY, SDL_COLORSPACE_BT709_LIMITED },
104 { &SDL_MFVideoFormat_YVYU, SDL_PIXELFORMAT_YVYU, SDL_COLORSPACE_BT709_LIMITED },
105 { &SDL_MFVideoFormat_NV12, SDL_PIXELFORMAT_NV12, SDL_COLORSPACE_BT709_LIMITED },
106 { &SDL_MFVideoFormat_NV21, SDL_PIXELFORMAT_NV21, SDL_COLORSPACE_BT709_LIMITED },
107 { &SDL_MFVideoFormat_MJPG, SDL_PIXELFORMAT_MJPG, SDL_COLORSPACE_SRGB }
108};
109
110static SDL_Colorspace GetMediaTypeColorspace(IMFMediaType *mediatype, SDL_Colorspace default_colorspace)
111{
112 SDL_Colorspace colorspace = default_colorspace;
113
114 if (SDL_COLORSPACETYPE(colorspace) == SDL_COLOR_TYPE_YCBCR) {
115 HRESULT ret;
116 UINT32 range = 0, primaries = 0, transfer = 0, matrix = 0, chroma = 0;
117
118 ret = IMFMediaType_GetUINT32(mediatype, &SDL_MF_MT_VIDEO_NOMINAL_RANGE, &range);
119 if (SUCCEEDED(ret)) {
120 switch (range) {
121 case MFNominalRange_0_255:
122 range = SDL_COLOR_RANGE_FULL;
123 break;
124 case MFNominalRange_16_235:
125 range = SDL_COLOR_RANGE_LIMITED;
126 break;
127 default:
128 range = (UINT32)SDL_COLORSPACERANGE(default_colorspace);
129 break;
130 }
131 } else {
132 range = (UINT32)SDL_COLORSPACERANGE(default_colorspace);
133 }
134
135 ret = IMFMediaType_GetUINT32(mediatype, &SDL_MF_MT_VIDEO_PRIMARIES, &primaries);
136 if (SUCCEEDED(ret)) {
137 switch (primaries) {
138 case MFVideoPrimaries_BT709:
139 primaries = SDL_COLOR_PRIMARIES_BT709;
140 break;
141 case MFVideoPrimaries_BT470_2_SysM:
142 primaries = SDL_COLOR_PRIMARIES_BT470M;
143 break;
144 case MFVideoPrimaries_BT470_2_SysBG:
145 primaries = SDL_COLOR_PRIMARIES_BT470BG;
146 break;
147 case MFVideoPrimaries_SMPTE170M:
148 primaries = SDL_COLOR_PRIMARIES_BT601;
149 break;
150 case MFVideoPrimaries_SMPTE240M:
151 primaries = SDL_COLOR_PRIMARIES_SMPTE240;
152 break;
153 case MFVideoPrimaries_EBU3213:
154 primaries = SDL_COLOR_PRIMARIES_EBU3213;
155 break;
156 case MFVideoPrimaries_BT2020:
157 primaries = SDL_COLOR_PRIMARIES_BT2020;
158 break;
159 case MFVideoPrimaries_XYZ:
160 primaries = SDL_COLOR_PRIMARIES_XYZ;
161 break;
162 case MFVideoPrimaries_DCI_P3:
163 primaries = SDL_COLOR_PRIMARIES_SMPTE432;
164 break;
165 default:
166 primaries = (UINT32)SDL_COLORSPACEPRIMARIES(default_colorspace);
167 break;
168 }
169 } else {
170 primaries = (UINT32)SDL_COLORSPACEPRIMARIES(default_colorspace);
171 }
172
173 ret = IMFMediaType_GetUINT32(mediatype, &SDL_MF_MT_TRANSFER_FUNCTION, &transfer);
174 if (SUCCEEDED(ret)) {
175 switch (transfer) {
176 case MFVideoTransFunc_10:
177 transfer = SDL_TRANSFER_CHARACTERISTICS_LINEAR;
178 break;
179 case MFVideoTransFunc_22:
180 transfer = SDL_TRANSFER_CHARACTERISTICS_GAMMA22;
181 break;
182 case MFVideoTransFunc_709:
183 transfer = SDL_TRANSFER_CHARACTERISTICS_BT709;
184 break;
185 case MFVideoTransFunc_240M:
186 transfer = SDL_TRANSFER_CHARACTERISTICS_SMPTE240;
187 break;
188 case MFVideoTransFunc_sRGB:
189 transfer = SDL_TRANSFER_CHARACTERISTICS_SRGB;
190 break;
191 case MFVideoTransFunc_28:
192 transfer = SDL_TRANSFER_CHARACTERISTICS_GAMMA28;
193 break;
194 case MFVideoTransFunc_Log_100:
195 transfer = SDL_TRANSFER_CHARACTERISTICS_LOG100;
196 break;
197 case MFVideoTransFunc_2084:
198 transfer = SDL_TRANSFER_CHARACTERISTICS_PQ;
199 break;
200 case MFVideoTransFunc_HLG:
201 transfer = SDL_TRANSFER_CHARACTERISTICS_HLG;
202 break;
203 case 18 /* MFVideoTransFunc_BT1361_ECG */:
204 transfer = SDL_TRANSFER_CHARACTERISTICS_BT1361;
205 break;
206 case 19 /* MFVideoTransFunc_SMPTE428 */:
207 transfer = SDL_TRANSFER_CHARACTERISTICS_SMPTE428;
208 break;
209 default:
210 transfer = (UINT32)SDL_COLORSPACETRANSFER(default_colorspace);
211 break;
212 }
213 } else {
214 transfer = (UINT32)SDL_COLORSPACETRANSFER(default_colorspace);
215 }
216
217 ret = IMFMediaType_GetUINT32(mediatype, &SDL_MF_MT_YUV_MATRIX, &matrix);
218 if (SUCCEEDED(ret)) {
219 switch (matrix) {
220 case MFVideoTransferMatrix_BT709:
221 matrix = SDL_MATRIX_COEFFICIENTS_BT709;
222 break;
223 case MFVideoTransferMatrix_BT601:
224 matrix = SDL_MATRIX_COEFFICIENTS_BT601;
225 break;
226 case MFVideoTransferMatrix_SMPTE240M:
227 matrix = SDL_MATRIX_COEFFICIENTS_SMPTE240;
228 break;
229 case MFVideoTransferMatrix_BT2020_10:
230 matrix = SDL_MATRIX_COEFFICIENTS_BT2020_NCL;
231 break;
232 case 6 /* MFVideoTransferMatrix_Identity */:
233 matrix = SDL_MATRIX_COEFFICIENTS_IDENTITY;
234 break;
235 case 7 /* MFVideoTransferMatrix_FCC47 */:
236 matrix = SDL_MATRIX_COEFFICIENTS_FCC;
237 break;
238 case 8 /* MFVideoTransferMatrix_YCgCo */:
239 matrix = SDL_MATRIX_COEFFICIENTS_YCGCO;
240 break;
241 case 9 /* MFVideoTransferMatrix_SMPTE2085 */:
242 matrix = SDL_MATRIX_COEFFICIENTS_SMPTE2085;
243 break;
244 case 10 /* MFVideoTransferMatrix_Chroma */:
245 matrix = SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL;
246 break;
247 case 11 /* MFVideoTransferMatrix_Chroma_const */:
248 matrix = SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL;
249 break;
250 case 12 /* MFVideoTransferMatrix_ICtCp */:
251 matrix = SDL_MATRIX_COEFFICIENTS_ICTCP;
252 break;
253 default:
254 matrix = (UINT32)SDL_COLORSPACEMATRIX(default_colorspace);
255 break;
256 }
257 } else {
258 matrix = (UINT32)SDL_COLORSPACEMATRIX(default_colorspace);
259 }
260
261 ret = IMFMediaType_GetUINT32(mediatype, &SDL_MF_MT_VIDEO_CHROMA_SITING, &chroma);
262 if (SUCCEEDED(ret)) {
263 switch (chroma) {
264 case MFVideoChromaSubsampling_MPEG2:
265 chroma = SDL_CHROMA_LOCATION_LEFT;
266 break;
267 case MFVideoChromaSubsampling_MPEG1:
268 chroma = SDL_CHROMA_LOCATION_CENTER;
269 break;
270 case MFVideoChromaSubsampling_DV_PAL:
271 chroma = SDL_CHROMA_LOCATION_TOPLEFT;
272 break;
273 default:
274 chroma = (UINT32)SDL_COLORSPACECHROMA(default_colorspace);
275 break;
276 }
277 } else {
278 chroma = (UINT32)SDL_COLORSPACECHROMA(default_colorspace);
279 }
280
281 colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, range, primaries, transfer, matrix, chroma);
282 }
283 return colorspace;
284}
285
286static void MediaTypeToSDLFmt(IMFMediaType *mediatype, SDL_PixelFormat *format, SDL_Colorspace *colorspace)
287{
288 HRESULT ret;
289 GUID type;
290
291 ret = IMFMediaType_GetGUID(mediatype, &SDL_MF_MT_SUBTYPE, &type);
292 if (SUCCEEDED(ret)) {
293 for (size_t i = 0; i < SDL_arraysize(fmtmappings); i++) {
294 if (WIN_IsEqualGUID(&type, fmtmappings[i].guid)) {
295 *format = fmtmappings[i].format;
296 *colorspace = GetMediaTypeColorspace(mediatype, fmtmappings[i].colorspace);
297 return;
298 }
299 }
300 }
301#if DEBUG_CAMERA
302 SDL_Log("Unknown media type: 0x%x (%c%c%c%c)", type.Data1,
303 (char)(Uint8)(type.Data1 >> 0),
304 (char)(Uint8)(type.Data1 >> 8),
305 (char)(Uint8)(type.Data1 >> 16),
306 (char)(Uint8)(type.Data1 >> 24));
307#endif
308 *format = SDL_PIXELFORMAT_UNKNOWN;
309 *colorspace = SDL_COLORSPACE_UNKNOWN;
310}
311
312static const GUID *SDLFmtToMFVidFmtGuid(SDL_PixelFormat format)
313{
314 for (size_t i = 0; i < SDL_arraysize(fmtmappings); i++) {
315 if (fmtmappings[i].format == format) {
316 return fmtmappings[i].guid;
317 }
318 }
319 return NULL;
320}
321
322
323// handle to Media Foundation libs--Vista and later!--for access to the Media Foundation API.
324
325// mf.dll ...
326static HMODULE libmf = NULL;
327typedef HRESULT(WINAPI *pfnMFEnumDeviceSources)(IMFAttributes *,IMFActivate ***,UINT32 *);
328typedef HRESULT(WINAPI *pfnMFCreateDeviceSource)(IMFAttributes *, IMFMediaSource **);
329static pfnMFEnumDeviceSources pMFEnumDeviceSources = NULL;
330static pfnMFCreateDeviceSource pMFCreateDeviceSource = NULL;
331
332// mfplat.dll ...
333static HMODULE libmfplat = NULL;
334typedef HRESULT(WINAPI *pfnMFStartup)(ULONG, DWORD);
335typedef HRESULT(WINAPI *pfnMFShutdown)(void);
336typedef HRESULT(WINAPI *pfnMFCreateAttributes)(IMFAttributes **, UINT32);
337typedef HRESULT(WINAPI *pfnMFCreateMediaType)(IMFMediaType **);
338typedef HRESULT(WINAPI *pfnMFGetStrideForBitmapInfoHeader)(DWORD, DWORD, LONG *);
339
340static pfnMFStartup pMFStartup = NULL;
341static pfnMFShutdown pMFShutdown = NULL;
342static pfnMFCreateAttributes pMFCreateAttributes = NULL;
343static pfnMFCreateMediaType pMFCreateMediaType = NULL;
344static pfnMFGetStrideForBitmapInfoHeader pMFGetStrideForBitmapInfoHeader = NULL;
345
346// mfreadwrite.dll ...
347static HMODULE libmfreadwrite = NULL;
348typedef HRESULT(WINAPI *pfnMFCreateSourceReaderFromMediaSource)(IMFMediaSource *, IMFAttributes *, IMFSourceReader **);
349static pfnMFCreateSourceReaderFromMediaSource pMFCreateSourceReaderFromMediaSource = NULL;
350
351
352typedef struct SDL_PrivateCameraData
353{
354 IMFSourceReader *srcreader;
355 IMFSample *current_sample;
356 int pitch;
357} SDL_PrivateCameraData;
358
359static bool MEDIAFOUNDATION_WaitDevice(SDL_Camera *device)
360{
361 SDL_assert(device->hidden->current_sample == NULL);
362
363 IMFSourceReader *srcreader = device->hidden->srcreader;
364 IMFSample *sample = NULL;
365
366 while (!SDL_GetAtomicInt(&device->shutdown)) {
367 DWORD stream_flags = 0;
368 const HRESULT ret = IMFSourceReader_ReadSample(srcreader, (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &stream_flags, NULL, &sample);
369 if (FAILED(ret)) {
370 return false; // ruh roh.
371 }
372
373 // we currently ignore stream_flags format changes, but my _hope_ is that IMFSourceReader is handling this and
374 // will continue to give us the explicitly-specified format we requested when opening the device, though, and
375 // we don't have to manually deal with it.
376
377 if (sample != NULL) {
378 break;
379 } else if (stream_flags & (MF_SOURCE_READERF_ERROR | MF_SOURCE_READERF_ENDOFSTREAM)) {
380 return false; // apparently this camera has gone down. :/
381 }
382
383 // otherwise, there was some minor burp, probably; just try again.
384 }
385
386 device->hidden->current_sample = sample;
387
388 return true;
389}
390
391
392#ifdef KEEP_ACQUIRED_BUFFERS_LOCKED
393
394#define PROP_SURFACE_IMFOBJS_POINTER "SDL.camera.mediafoundation.imfobjs"
395
396typedef struct SDL_IMFObjects
397{
398 IMF2DBuffer2 *buffer2d2;
399 IMF2DBuffer *buffer2d;
400 IMFMediaBuffer *buffer;
401 IMFSample *sample;
402} SDL_IMFObjects;
403
404static void SDLCALL CleanupIMF2DBuffer2(void *userdata, void *value)
405{
406 SDL_IMFObjects *objs = (SDL_IMFObjects *)value;
407 IMF2DBuffer2_Unlock2D(objs->buffer2d2);
408 IMF2DBuffer2_Release(objs->buffer2d2);
409 IMFMediaBuffer_Release(objs->buffer);
410 IMFSample_Release(objs->sample);
411 SDL_free(objs);
412}
413
414static void SDLCALL CleanupIMF2DBuffer(void *userdata, void *value)
415{
416 SDL_IMFObjects *objs = (SDL_IMFObjects *)value;
417 IMF2DBuffer_Unlock2D(objs->buffer2d);
418 IMF2DBuffer_Release(objs->buffer2d);
419 IMFMediaBuffer_Release(objs->buffer);
420 IMFSample_Release(objs->sample);
421 SDL_free(objs);
422}
423
424static void SDLCALL CleanupIMFMediaBuffer(void *userdata, void *value)
425{
426 SDL_IMFObjects *objs = (SDL_IMFObjects *)value;
427 IMFMediaBuffer_Unlock(objs->buffer);
428 IMFMediaBuffer_Release(objs->buffer);
429 IMFSample_Release(objs->sample);
430 SDL_free(objs);
431}
432
433static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS)
434{
435 SDL_assert(device->hidden->current_sample != NULL);
436
437 SDL_CameraFrameResult result = SDL_CAMERA_FRAME_READY;
438 HRESULT ret;
439 LONGLONG timestamp100NS = 0;
440 SDL_IMFObjects *objs = (SDL_IMFObjects *) SDL_calloc(1, sizeof (SDL_IMFObjects));
441
442 if (objs == NULL) {
443 return SDL_CAMERA_FRAME_ERROR;
444 }
445
446 objs->sample = device->hidden->current_sample;
447 device->hidden->current_sample = NULL;
448
449 const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame);
450 if (!surfprops) {
451 result = SDL_CAMERA_FRAME_ERROR;
452 } else {
453 ret = IMFSample_GetSampleTime(objs->sample, &timestamp100NS);
454 if (FAILED(ret)) {
455 result = SDL_CAMERA_FRAME_ERROR;
456 }
457
458 *timestampNS = timestamp100NS * 100; // the timestamps are in 100-nanosecond increments; move to full nanoseconds.
459 }
460
461 ret = (result == SDL_CAMERA_FRAME_ERROR) ? E_FAIL : IMFSample_ConvertToContiguousBuffer(objs->sample, &objs->buffer); // IMFSample_GetBufferByIndex(objs->sample, 0, &objs->buffer);
462
463 if (FAILED(ret)) {
464 SDL_free(objs);
465 result = SDL_CAMERA_FRAME_ERROR;
466 } else {
467 BYTE *pixels = NULL;
468 LONG pitch = 0;
469 DWORD buflen = 0;
470
471 if (SUCCEEDED(IMFMediaBuffer_QueryInterface(objs->buffer, &SDL_IID_IMF2DBuffer2, (void **)&objs->buffer2d2))) {
472 BYTE *bufstart = NULL;
473 ret = IMF2DBuffer2_Lock2DSize(objs->buffer2d2, MF2DBuffer_LockFlags_Read, &pixels, &pitch, &bufstart, &buflen);
474 if (FAILED(ret)) {
475 result = SDL_CAMERA_FRAME_ERROR;
476 CleanupIMF2DBuffer2(NULL, objs);
477 } else {
478 if (frame->format == SDL_PIXELFORMAT_MJPG) {
479 pitch = (LONG)buflen;
480 }
481 if (pitch < 0) { // image rows are reversed.
482 pixels += -pitch * (frame->h - 1);
483 }
484 frame->pixels = pixels;
485 frame->pitch = (int)pitch;
486 if (!SDL_SetPointerPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMF2DBuffer2, NULL)) {
487 result = SDL_CAMERA_FRAME_ERROR;
488 }
489 }
490 } else if (frame->format != SDL_PIXELFORMAT_MJPG &&
491 SUCCEEDED(IMFMediaBuffer_QueryInterface(objs->buffer, &SDL_IID_IMF2DBuffer, (void **)&objs->buffer2d))) {
492 ret = IMF2DBuffer_Lock2D(objs->buffer2d, &pixels, &pitch);
493 if (FAILED(ret)) {
494 CleanupIMF2DBuffer(NULL, objs);
495 result = SDL_CAMERA_FRAME_ERROR;
496 } else {
497 if (pitch < 0) { // image rows are reversed.
498 pixels += -pitch * (frame->h - 1);
499 }
500 frame->pixels = pixels;
501 frame->pitch = (int)pitch;
502 if (!SDL_SetPointerPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMF2DBuffer, NULL)) {
503 result = SDL_CAMERA_FRAME_ERROR;
504 }
505 }
506 } else {
507 DWORD maxlen = 0;
508 ret = IMFMediaBuffer_Lock(objs->buffer, &pixels, &maxlen, &buflen);
509 if (FAILED(ret)) {
510 CleanupIMFMediaBuffer(NULL, objs);
511 result = SDL_CAMERA_FRAME_ERROR;
512 } else {
513 if (frame->format == SDL_PIXELFORMAT_MJPG) {
514 pitch = (LONG)buflen;
515 } else {
516 pitch = (LONG)device->hidden->pitch;
517 }
518 if (pitch < 0) { // image rows are reversed.
519 pixels += -pitch * (frame->h - 1);
520 }
521 frame->pixels = pixels;
522 frame->pitch = (int)pitch;
523 if (!SDL_SetPointerPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMFMediaBuffer, NULL)) {
524 result = SDL_CAMERA_FRAME_ERROR;
525 }
526 }
527 }
528 }
529
530 if (result != SDL_CAMERA_FRAME_READY) {
531 *timestampNS = 0;
532 }
533
534 return result;
535}
536
537static void MEDIAFOUNDATION_ReleaseFrame(SDL_Camera *device, SDL_Surface *frame)
538{
539 const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame);
540 if (surfprops) {
541 // this will release the IMFBuffer and IMFSample objects for this frame.
542 SDL_ClearProperty(surfprops, PROP_SURFACE_IMFOBJS_POINTER);
543 }
544}
545
546#else
547
548static SDL_CameraFrameResult MEDIAFOUNDATION_CopyFrame(SDL_Surface *frame, const BYTE *pixels, LONG pitch, DWORD buflen)
549{
550 frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
551 if (!frame->pixels) {
552 return SDL_CAMERA_FRAME_ERROR;
553 }
554
555 const BYTE *start = pixels;
556 if (pitch < 0) { // image rows are reversed.
557 start += -pitch * (frame->h - 1);
558 }
559 SDL_memcpy(frame->pixels, start, buflen);
560 frame->pitch = (int)pitch;
561
562 return SDL_CAMERA_FRAME_READY;
563}
564
565static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS)
566{
567 SDL_assert(device->hidden->current_sample != NULL);
568
569 SDL_CameraFrameResult result = SDL_CAMERA_FRAME_READY;
570 HRESULT ret;
571 LONGLONG timestamp100NS = 0;
572
573 IMFSample *sample = device->hidden->current_sample;
574 device->hidden->current_sample = NULL;
575
576 const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame);
577 if (!surfprops) {
578 result = SDL_CAMERA_FRAME_ERROR;
579 } else {
580 ret = IMFSample_GetSampleTime(sample, &timestamp100NS);
581 if (FAILED(ret)) {
582 result = SDL_CAMERA_FRAME_ERROR;
583 }
584
585 *timestampNS = timestamp100NS * 100; // the timestamps are in 100-nanosecond increments; move to full nanoseconds.
586 }
587
588 IMFMediaBuffer *buffer = NULL;
589 ret = (result < 0) ? E_FAIL : IMFSample_ConvertToContiguousBuffer(sample, &buffer); // IMFSample_GetBufferByIndex(sample, 0, &buffer);
590
591 if (FAILED(ret)) {
592 result = SDL_CAMERA_FRAME_ERROR;
593 } else {
594 IMF2DBuffer *buffer2d = NULL;
595 IMF2DBuffer2 *buffer2d2 = NULL;
596 BYTE *pixels = NULL;
597 LONG pitch = 0;
598 DWORD buflen = 0;
599
600 if (SUCCEEDED(IMFMediaBuffer_QueryInterface(buffer, &SDL_IID_IMF2DBuffer2, (void **)&buffer2d2))) {
601 BYTE *bufstart = NULL;
602 ret = IMF2DBuffer2_Lock2DSize(buffer2d2, MF2DBuffer_LockFlags_Read, &pixels, &pitch, &bufstart, &buflen);
603 if (FAILED(ret)) {
604 result = SDL_CAMERA_FRAME_ERROR;
605 } else {
606 if (frame->format == SDL_PIXELFORMAT_MJPG) {
607 pitch = (LONG)buflen;
608 }
609 result = MEDIAFOUNDATION_CopyFrame(frame, pixels, pitch, buflen);
610 IMF2DBuffer2_Unlock2D(buffer2d2);
611 }
612 IMF2DBuffer2_Release(buffer2d2);
613 } else if (frame->format != SDL_PIXELFORMAT_MJPG &&
614 SUCCEEDED(IMFMediaBuffer_QueryInterface(buffer, &SDL_IID_IMF2DBuffer, (void **)&buffer2d))) {
615 ret = IMF2DBuffer_Lock2D(buffer2d, &pixels, &pitch);
616 if (FAILED(ret)) {
617 result = SDL_CAMERA_FRAME_ERROR;
618 } else {
619 buflen = SDL_abs((int)pitch) * frame->h;
620 result = MEDIAFOUNDATION_CopyFrame(frame, pixels, pitch, buflen);
621 IMF2DBuffer_Unlock2D(buffer2d);
622 }
623 IMF2DBuffer_Release(buffer2d);
624 } else {
625 DWORD maxlen = 0;
626 ret = IMFMediaBuffer_Lock(buffer, &pixels, &maxlen, &buflen);
627 if (FAILED(ret)) {
628 result = SDL_CAMERA_FRAME_ERROR;
629 } else {
630 if (frame->format == SDL_PIXELFORMAT_MJPG) {
631 pitch = (LONG)buflen;
632 } else {
633 pitch = (LONG)device->hidden->pitch;
634 }
635 result = MEDIAFOUNDATION_CopyFrame(frame, pixels, pitch, buflen);
636 IMFMediaBuffer_Unlock(buffer);
637 }
638 }
639 IMFMediaBuffer_Release(buffer);
640 }
641
642 IMFSample_Release(sample);
643
644 if (result != SDL_CAMERA_FRAME_READY) {
645 *timestampNS = 0;
646 }
647
648 return result;
649}
650
651static void MEDIAFOUNDATION_ReleaseFrame(SDL_Camera *device, SDL_Surface *frame)
652{
653 SDL_aligned_free(frame->pixels);
654}
655
656#endif
657
658static void MEDIAFOUNDATION_CloseDevice(SDL_Camera *device)
659{
660 if (device && device->hidden) {
661 if (device->hidden->srcreader) {
662 IMFSourceReader_Release(device->hidden->srcreader);
663 }
664 if (device->hidden->current_sample) {
665 IMFSample_Release(device->hidden->current_sample);
666 }
667 SDL_free(device->hidden);
668 device->hidden = NULL;
669 }
670}
671
672// this function is from https://learn.microsoft.com/en-us/windows/win32/medfound/uncompressed-video-buffers
673static HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
674{
675 LONG lStride = 0;
676
677 // Try to get the default stride from the media type.
678 HRESULT ret = IMFMediaType_GetUINT32(pType, &SDL_MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
679 if (FAILED(ret)) {
680 // Attribute not set. Try to calculate the default stride.
681
682 GUID subtype = GUID_NULL;
683 UINT32 width = 0;
684 // UINT32 height = 0;
685 UINT64 val = 0;
686
687 // Get the subtype and the image size.
688 ret = IMFMediaType_GetGUID(pType, &SDL_MF_MT_SUBTYPE, &subtype);
689 if (FAILED(ret)) {
690 goto done;
691 }
692
693 ret = IMFMediaType_GetUINT64(pType, &SDL_MF_MT_FRAME_SIZE, &val);
694 if (FAILED(ret)) {
695 goto done;
696 }
697
698 width = (UINT32) (val >> 32);
699 // height = (UINT32) val;
700
701 ret = pMFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride);
702 if (FAILED(ret)) {
703 goto done;
704 }
705
706 // Set the attribute for later reference.
707 IMFMediaType_SetUINT32(pType, &SDL_MF_MT_DEFAULT_STRIDE, (UINT32) lStride);
708 }
709
710 if (SUCCEEDED(ret)) {
711 *plStride = lStride;
712 }
713
714done:
715 return ret;
716}
717
718
719static bool MEDIAFOUNDATION_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
720{
721 const char *utf8symlink = (const char *) device->handle;
722 IMFAttributes *attrs = NULL;
723 LPWSTR wstrsymlink = NULL;
724 IMFMediaSource *source = NULL;
725 IMFMediaType *mediatype = NULL;
726 IMFSourceReader *srcreader = NULL;
727#if 0
728 DWORD num_streams = 0;
729#endif
730 LONG lstride = 0;
731 //PROPVARIANT var;
732 HRESULT ret;
733
734 #if 0
735 IMFStreamDescriptor *streamdesc = NULL;
736 IMFPresentationDescriptor *presentdesc = NULL;
737 IMFMediaTypeHandler *handler = NULL;
738 #endif
739
740 #if DEBUG_CAMERA
741 SDL_Log("CAMERA: opening device with symlink of '%s'", utf8symlink);
742 #endif
743
744 wstrsymlink = WIN_UTF8ToString(utf8symlink);
745 if (!wstrsymlink) {
746 goto failed;
747 }
748
749 #define CHECK_HRESULT(what, r) if (FAILED(r)) { WIN_SetErrorFromHRESULT(what " failed", r); goto failed; }
750
751 ret = pMFCreateAttributes(&attrs, 1);
752 CHECK_HRESULT("MFCreateAttributes", ret);
753
754 ret = IMFAttributes_SetGUID(attrs, &SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
755 CHECK_HRESULT("IMFAttributes_SetGUID(srctype)", ret);
756
757 ret = IMFAttributes_SetString(attrs, &SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, wstrsymlink);
758 CHECK_HRESULT("IMFAttributes_SetString(symlink)", ret);
759
760 ret = pMFCreateDeviceSource(attrs, &source);
761 CHECK_HRESULT("MFCreateDeviceSource", ret);
762
763 IMFAttributes_Release(attrs);
764 SDL_free(wstrsymlink);
765 attrs = NULL;
766 wstrsymlink = NULL;
767
768 // !!! FIXME: I think it'd be nice to do this without an IMFSourceReader,
769 // since it's just utility code that has to handle more complex media streams
770 // than we're dealing with, but this will do for now. The docs are slightly
771 // insistent that you should use one, though...Maybe it's extremely hard
772 // to handle directly at the IMFMediaSource layer...?
773 ret = pMFCreateSourceReaderFromMediaSource(source, NULL, &srcreader);
774 CHECK_HRESULT("MFCreateSourceReaderFromMediaSource", ret);
775
776 // !!! FIXME: do we actually have to find the media type object in the source reader or can we just roll our own like this?
777 ret = pMFCreateMediaType(&mediatype);
778 CHECK_HRESULT("MFCreateMediaType", ret);
779
780 ret = IMFMediaType_SetGUID(mediatype, &SDL_MF_MT_MAJOR_TYPE, &SDL_MFMediaType_Video);
781 CHECK_HRESULT("IMFMediaType_SetGUID(major_type)", ret);
782
783 ret = IMFMediaType_SetGUID(mediatype, &SDL_MF_MT_SUBTYPE, SDLFmtToMFVidFmtGuid(spec->format));
784 CHECK_HRESULT("IMFMediaType_SetGUID(subtype)", ret);
785
786 ret = IMFMediaType_SetUINT64(mediatype, &SDL_MF_MT_FRAME_SIZE, (((UINT64)spec->width) << 32) | ((UINT64)spec->height));
787 CHECK_HRESULT("MFSetAttributeSize(frame_size)", ret);
788
789 ret = IMFMediaType_SetUINT64(mediatype, &SDL_MF_MT_FRAME_RATE, (((UINT64)spec->framerate_numerator) << 32) | ((UINT64)spec->framerate_denominator));
790 CHECK_HRESULT("MFSetAttributeRatio(frame_rate)", ret);
791
792 ret = IMFSourceReader_SetCurrentMediaType(srcreader, (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, mediatype);
793 CHECK_HRESULT("IMFSourceReader_SetCurrentMediaType", ret);
794
795 #if 0 // this (untested thing) is what we would do to get started with a IMFMediaSource that _doesn't_ use IMFSourceReader...
796 ret = IMFMediaSource_CreatePresentationDescriptor(source, &presentdesc);
797 CHECK_HRESULT("IMFMediaSource_CreatePresentationDescriptor", ret);
798
799 ret = IMFPresentationDescriptor_GetStreamDescriptorCount(presentdesc, &num_streams);
800 CHECK_HRESULT("IMFPresentationDescriptor_GetStreamDescriptorCount", ret);
801
802 for (DWORD i = 0; i < num_streams; i++) {
803 BOOL selected = FALSE;
804 ret = IMFPresentationDescriptor_GetStreamDescriptorByIndex(presentdesc, i, &selected, &streamdesc);
805 CHECK_HRESULT("IMFPresentationDescriptor_GetStreamDescriptorByIndex", ret);
806
807 if (selected) {
808 ret = IMFStreamDescriptor_GetMediaTypeHandler(streamdesc, &handler);
809 CHECK_HRESULT("IMFStreamDescriptor_GetMediaTypeHandler", ret);
810 IMFMediaTypeHandler_SetCurrentMediaType(handler, mediatype);
811 IMFMediaTypeHandler_Release(handler);
812 handler = NULL;
813 }
814
815 IMFStreamDescriptor_Release(streamdesc);
816 streamdesc = NULL;
817 }
818
819 PropVariantInit(&var);
820 var.vt = VT_EMPTY;
821 ret = IMFMediaSource_Start(source, presentdesc, NULL, &var);
822 PropVariantClear(&var);
823 CHECK_HRESULT("IMFMediaSource_Start", ret);
824
825 IMFPresentationDescriptor_Release(presentdesc);
826 presentdesc = NULL;
827 #endif
828
829 ret = GetDefaultStride(mediatype, &lstride);
830 CHECK_HRESULT("GetDefaultStride", ret);
831
832 IMFMediaType_Release(mediatype);
833 mediatype = NULL;
834
835 device->hidden = (SDL_PrivateCameraData *) SDL_calloc(1, sizeof (SDL_PrivateCameraData));
836 if (!device->hidden) {
837 goto failed;
838 }
839
840 device->hidden->pitch = (int) lstride;
841 device->hidden->srcreader = srcreader;
842 IMFMediaSource_Release(source); // srcreader is holding a reference to this.
843
844 // There is no user permission prompt for camera access (I think?)
845 SDL_CameraPermissionOutcome(device, true);
846
847 #undef CHECK_HRESULT
848
849 return true;
850
851failed:
852
853 if (srcreader) {
854 IMFSourceReader_Release(srcreader);
855 }
856
857 #if 0
858 if (handler) {
859 IMFMediaTypeHandler_Release(handler);
860 }
861
862 if (streamdesc) {
863 IMFStreamDescriptor_Release(streamdesc);
864 }
865
866 if (presentdesc) {
867 IMFPresentationDescriptor_Release(presentdesc);
868 }
869 #endif
870
871 if (source) {
872 IMFMediaSource_Shutdown(source);
873 IMFMediaSource_Release(source);
874 }
875
876 if (mediatype) {
877 IMFMediaType_Release(mediatype);
878 }
879
880 if (attrs) {
881 IMFAttributes_Release(attrs);
882 }
883 SDL_free(wstrsymlink);
884
885 return false;
886}
887
888static void MEDIAFOUNDATION_FreeDeviceHandle(SDL_Camera *device)
889{
890 if (device) {
891 SDL_free(device->handle); // the device's symlink string.
892 }
893}
894
895static char *QueryActivationObjectString(IMFActivate *activation, const GUID *pguid)
896{
897 LPWSTR wstr = NULL;
898 UINT32 wlen = 0;
899 HRESULT ret = IMFActivate_GetAllocatedString(activation, pguid, &wstr, &wlen);
900 if (FAILED(ret)) {
901 return NULL;
902 }
903
904 char *utf8str = WIN_StringToUTF8(wstr);
905 CoTaskMemFree(wstr);
906 return utf8str;
907}
908
909static void GatherCameraSpecs(IMFMediaSource *source, CameraFormatAddData *add_data)
910{
911 HRESULT ret;
912
913 // this has like a thousand steps. :/
914
915 SDL_zerop(add_data);
916
917 IMFPresentationDescriptor *presentdesc = NULL;
918 ret = IMFMediaSource_CreatePresentationDescriptor(source, &presentdesc);
919 if (FAILED(ret) || !presentdesc) {
920 return;
921 }
922
923 DWORD num_streams = 0;
924 ret = IMFPresentationDescriptor_GetStreamDescriptorCount(presentdesc, &num_streams);
925 if (FAILED(ret)) {
926 num_streams = 0;
927 }
928
929 for (DWORD i = 0; i < num_streams; i++) {
930 IMFStreamDescriptor *streamdesc = NULL;
931 BOOL selected = FALSE;
932 ret = IMFPresentationDescriptor_GetStreamDescriptorByIndex(presentdesc, i, &selected, &streamdesc);
933 if (FAILED(ret) || !streamdesc) {
934 continue;
935 }
936
937 if (selected) {
938 IMFMediaTypeHandler *handler = NULL;
939 ret = IMFStreamDescriptor_GetMediaTypeHandler(streamdesc, &handler);
940 if (SUCCEEDED(ret) && handler) {
941 DWORD num_mediatype = 0;
942 ret = IMFMediaTypeHandler_GetMediaTypeCount(handler, &num_mediatype);
943 if (FAILED(ret)) {
944 num_mediatype = 0;
945 }
946
947 for (DWORD j = 0; j < num_mediatype; j++) {
948 IMFMediaType *mediatype = NULL;
949 ret = IMFMediaTypeHandler_GetMediaTypeByIndex(handler, j, &mediatype);
950 if (SUCCEEDED(ret) && mediatype) {
951 GUID type;
952 ret = IMFMediaType_GetGUID(mediatype, &SDL_MF_MT_MAJOR_TYPE, &type);
953 if (SUCCEEDED(ret) && WIN_IsEqualGUID(&type, &SDL_MFMediaType_Video)) {
954 SDL_PixelFormat sdlfmt = SDL_PIXELFORMAT_UNKNOWN;
955 SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN;
956 MediaTypeToSDLFmt(mediatype, &sdlfmt, &colorspace);
957 if (sdlfmt != SDL_PIXELFORMAT_UNKNOWN) {
958 UINT64 val = 0;
959 UINT32 w = 0, h = 0;
960 ret = IMFMediaType_GetUINT64(mediatype, &SDL_MF_MT_FRAME_SIZE, &val);
961 w = (UINT32)(val >> 32);
962 h = (UINT32)val;
963 if (SUCCEEDED(ret) && w && h) {
964 UINT32 framerate_numerator = 0, framerate_denominator = 0;
965 ret = IMFMediaType_GetUINT64(mediatype, &SDL_MF_MT_FRAME_RATE, &val);
966 framerate_numerator = (UINT32)(val >> 32);
967 framerate_denominator = (UINT32)val;
968 if (SUCCEEDED(ret) && framerate_numerator && framerate_denominator) {
969 SDL_AddCameraFormat(add_data, sdlfmt, colorspace, (int) w, (int) h, (int)framerate_numerator, (int)framerate_denominator);
970 }
971 }
972 }
973 }
974 IMFMediaType_Release(mediatype);
975 }
976 }
977 IMFMediaTypeHandler_Release(handler);
978 }
979 }
980 IMFStreamDescriptor_Release(streamdesc);
981 }
982
983 IMFPresentationDescriptor_Release(presentdesc);
984}
985
986static bool FindMediaFoundationCameraBySymlink(SDL_Camera *device, void *userdata)
987{
988 return (SDL_strcmp((const char *) device->handle, (const char *) userdata) == 0);
989}
990
991static void MaybeAddDevice(IMFActivate *activation)
992{
993 char *symlink = QueryActivationObjectString(activation, &SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK);
994
995 if (SDL_FindPhysicalCameraByCallback(FindMediaFoundationCameraBySymlink, symlink)) {
996 SDL_free(symlink);
997 return; // already have this one.
998 }
999
1000 char *name = QueryActivationObjectString(activation, &SDL_MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
1001 if (name && symlink) {
1002 IMFMediaSource *source = NULL;
1003 // "activating" here only creates an object, it doesn't open the actual camera hardware or start recording.
1004 HRESULT ret = IMFActivate_ActivateObject(activation, &SDL_IID_IMFMediaSource, (void**)&source);
1005 if (SUCCEEDED(ret) && source) {
1006 CameraFormatAddData add_data;
1007 GatherCameraSpecs(source, &add_data);
1008 if (add_data.num_specs > 0) {
1009 SDL_AddCamera(name, SDL_CAMERA_POSITION_UNKNOWN, add_data.num_specs, add_data.specs, symlink);
1010 }
1011 SDL_free(add_data.specs);
1012 IMFActivate_ShutdownObject(activation);
1013 IMFMediaSource_Release(source);
1014 }
1015 }
1016
1017 SDL_free(name);
1018}
1019
1020static void MEDIAFOUNDATION_DetectDevices(void)
1021{
1022 // !!! FIXME: use CM_Register_Notification (Win8+) to get device notifications.
1023 // !!! FIXME: Earlier versions can use RegisterDeviceNotification, but I'm not bothering: no hotplug for you!
1024 HRESULT ret;
1025
1026 IMFAttributes *attrs = NULL;
1027 ret = pMFCreateAttributes(&attrs, 1);
1028 if (FAILED(ret)) {
1029 return; // oh well, no cameras for you.
1030 }
1031
1032 ret = IMFAttributes_SetGUID(attrs, &SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &SDL_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
1033 if (FAILED(ret)) {
1034 IMFAttributes_Release(attrs);
1035 return; // oh well, no cameras for you.
1036 }
1037
1038 IMFActivate **activations = NULL;
1039 UINT32 total = 0;
1040 ret = pMFEnumDeviceSources(attrs, &activations, &total);
1041 IMFAttributes_Release(attrs);
1042 if (FAILED(ret)) {
1043 return; // oh well, no cameras for you.
1044 }
1045
1046 for (UINT32 i = 0; i < total; i++) {
1047 MaybeAddDevice(activations[i]);
1048 IMFActivate_Release(activations[i]);
1049 }
1050
1051 CoTaskMemFree(activations);
1052}
1053
1054static void MEDIAFOUNDATION_Deinitialize(void)
1055{
1056 pMFShutdown();
1057
1058 FreeLibrary(libmfreadwrite);
1059 libmfreadwrite = NULL;
1060 FreeLibrary(libmfplat);
1061 libmfplat = NULL;
1062 FreeLibrary(libmf);
1063 libmf = NULL;
1064
1065 pMFEnumDeviceSources = NULL;
1066 pMFCreateDeviceSource = NULL;
1067 pMFStartup = NULL;
1068 pMFShutdown = NULL;
1069 pMFCreateAttributes = NULL;
1070 pMFCreateMediaType = NULL;
1071 pMFCreateSourceReaderFromMediaSource = NULL;
1072 pMFGetStrideForBitmapInfoHeader = NULL;
1073}
1074
1075static bool MEDIAFOUNDATION_Init(SDL_CameraDriverImpl *impl)
1076{
1077 // !!! FIXME: slide this off into a subroutine
1078 HMODULE mf = LoadLibrary(TEXT("Mf.dll")); // this library is available in Vista and later, but also can be on XP with service packs and Windows
1079 if (!mf) {
1080 return false;
1081 }
1082
1083 HMODULE mfplat = LoadLibrary(TEXT("Mfplat.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now!
1084 if (!mfplat) {
1085 FreeLibrary(mf);
1086 return false;
1087 }
1088
1089 HMODULE mfreadwrite = LoadLibrary(TEXT("Mfreadwrite.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now!
1090 if (!mfreadwrite) {
1091 FreeLibrary(mfplat);
1092 FreeLibrary(mf);
1093 return false;
1094 }
1095
1096 bool okay = true;
1097 #define LOADSYM(lib, fn) if (okay) { p##fn = (pfn##fn) GetProcAddress(lib, #fn); if (!p##fn) { okay = false; } }
1098 LOADSYM(mf, MFEnumDeviceSources);
1099 LOADSYM(mf, MFCreateDeviceSource);
1100 LOADSYM(mfplat, MFStartup);
1101 LOADSYM(mfplat, MFShutdown);
1102 LOADSYM(mfplat, MFCreateAttributes);
1103 LOADSYM(mfplat, MFCreateMediaType);
1104 LOADSYM(mfplat, MFGetStrideForBitmapInfoHeader);
1105 LOADSYM(mfreadwrite, MFCreateSourceReaderFromMediaSource);
1106 #undef LOADSYM
1107
1108 if (okay) {
1109 const HRESULT ret = pMFStartup(MF_VERSION, MFSTARTUP_LITE);
1110 if (FAILED(ret)) {
1111 okay = false;
1112 }
1113 }
1114
1115 if (!okay) {
1116 FreeLibrary(mfreadwrite);
1117 FreeLibrary(mfplat);
1118 FreeLibrary(mf);
1119 return false;
1120 }
1121
1122 libmf = mf;
1123 libmfplat = mfplat;
1124 libmfreadwrite = mfreadwrite;
1125
1126 impl->DetectDevices = MEDIAFOUNDATION_DetectDevices;
1127 impl->OpenDevice = MEDIAFOUNDATION_OpenDevice;
1128 impl->CloseDevice = MEDIAFOUNDATION_CloseDevice;
1129 impl->WaitDevice = MEDIAFOUNDATION_WaitDevice;
1130 impl->AcquireFrame = MEDIAFOUNDATION_AcquireFrame;
1131 impl->ReleaseFrame = MEDIAFOUNDATION_ReleaseFrame;
1132 impl->FreeDeviceHandle = MEDIAFOUNDATION_FreeDeviceHandle;
1133 impl->Deinitialize = MEDIAFOUNDATION_Deinitialize;
1134
1135 return true;
1136}
1137
1138CameraBootStrap MEDIAFOUNDATION_bootstrap = {
1139 "mediafoundation", "SDL Windows Media Foundation camera driver", MEDIAFOUNDATION_Init, false
1140};
1141
1142#endif // SDL_CAMERA_DRIVER_MEDIAFOUNDATION
1143