Atlas - SDL_camera_mediafoundation.c
Home / ext / SDL / src / camera / mediafoundation Lines: 1 | Size: 43632 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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, float *rotation) 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, ×tamp100NS); 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, float *rotation) 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, ×tamp100NS); 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_UTF8ToStringW(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_StringToUTF8W(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 1144[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.