Atlas - SDL_camera.c
Home / ext / SDL / src / camera Lines: 1 | Size: 59047 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#include "SDL_syscamera.h" 24#include "SDL_camera_c.h" 25#include "../video/SDL_pixels_c.h" 26#include "../video/SDL_surface_c.h" 27#include "../thread/SDL_systhread.h" 28 29 30// A lot of this is a simplified version of SDL_audio.c; if fixing stuff here, 31// maybe check that file, too. 32 33// Available camera drivers 34static const CameraBootStrap *const bootstrap[] = { 35#ifdef SDL_CAMERA_DRIVER_V4L2 36 &V4L2_bootstrap, 37#endif 38#ifdef SDL_CAMERA_DRIVER_PIPEWIRE 39 &PIPEWIRECAMERA_bootstrap, 40#endif 41#ifdef SDL_CAMERA_DRIVER_COREMEDIA 42 &COREMEDIA_bootstrap, 43#endif 44#ifdef SDL_CAMERA_DRIVER_ANDROID 45 &ANDROIDCAMERA_bootstrap, 46#endif 47#ifdef SDL_CAMERA_DRIVER_EMSCRIPTEN 48 &EMSCRIPTENCAMERA_bootstrap, 49#endif 50#ifdef SDL_CAMERA_DRIVER_MEDIAFOUNDATION 51 &MEDIAFOUNDATION_bootstrap, 52#endif 53#ifdef SDL_CAMERA_DRIVER_VITA 54 &VITACAMERA_bootstrap, 55#endif 56#ifdef SDL_CAMERA_DRIVER_DUMMY 57 &DUMMYCAMERA_bootstrap, 58#endif 59 NULL 60}; 61 62static SDL_CameraDriver camera_driver; 63 64 65int SDL_GetNumCameraDrivers(void) 66{ 67 return SDL_arraysize(bootstrap) - 1; 68} 69 70const char *SDL_GetCameraDriver(int index) 71{ 72 CHECK_PARAM(index < 0 || index >= SDL_GetNumCameraDrivers()) { 73 SDL_InvalidParamError("index"); 74 return NULL; 75 } 76 return bootstrap[index]->name; 77} 78 79const char *SDL_GetCurrentCameraDriver(void) 80{ 81 return camera_driver.name; 82} 83 84char *SDL_GetCameraThreadName(SDL_Camera *device, char *buf, size_t buflen) 85{ 86 (void)SDL_snprintf(buf, buflen, "SDLCamera%d", (int) device->instance_id); 87 return buf; 88} 89 90bool SDL_AddCameraFormat(CameraFormatAddData *data, SDL_PixelFormat format, SDL_Colorspace colorspace, int w, int h, int framerate_numerator, int framerate_denominator) 91{ 92 SDL_assert(data != NULL); 93 if (data->allocated_specs <= data->num_specs) { 94 const int newalloc = data->allocated_specs ? (data->allocated_specs * 2) : 16; 95 void *ptr = SDL_realloc(data->specs, sizeof (SDL_CameraSpec) * newalloc); 96 if (!ptr) { 97 return false; 98 } 99 data->specs = (SDL_CameraSpec *) ptr; 100 data->allocated_specs = newalloc; 101 } 102 103 SDL_CameraSpec *spec = &data->specs[data->num_specs]; 104 spec->format = format; 105 spec->colorspace = colorspace; 106 spec->width = w; 107 spec->height = h; 108 spec->framerate_numerator = framerate_numerator; 109 spec->framerate_denominator = framerate_denominator; 110 111 data->num_specs++; 112 113 return true; 114} 115 116 117// Zombie device implementation... 118 119// These get used when a device is disconnected or fails. Apps that ignore the 120// loss notifications will get black frames but otherwise keep functioning. 121static bool ZombieWaitDevice(SDL_Camera *device) 122{ 123 if (!SDL_GetAtomicInt(&device->shutdown)) { 124 // !!! FIXME: this is bad for several reasons (uses double, could be precalculated, doesn't track elapsed time). 125 const double duration = ((double) device->actual_spec.framerate_denominator / ((double) device->actual_spec.framerate_numerator)); 126 SDL_Delay((Uint32) (duration * 1000.0)); 127 } 128 return true; 129} 130 131static size_t GetFrameBufLen(const SDL_CameraSpec *spec) 132{ 133 const size_t w = (const size_t) spec->width; 134 const size_t h = (const size_t) spec->height; 135 const size_t wxh = w * h; 136 const SDL_PixelFormat fmt = spec->format; 137 138 switch (fmt) { 139 // Some YUV formats have a larger Y plane than their U or V planes. 140 case SDL_PIXELFORMAT_YV12: 141 case SDL_PIXELFORMAT_IYUV: 142 case SDL_PIXELFORMAT_NV12: 143 case SDL_PIXELFORMAT_NV21: 144 return wxh + (wxh / 2); 145 146 default: break; 147 } 148 149 // this is correct for most things. 150 return wxh * SDL_BYTESPERPIXEL(fmt); 151} 152 153static SDL_CameraFrameResult ZombieAcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) 154{ 155 const SDL_CameraSpec *spec = &device->actual_spec; 156 157 if (!device->zombie_pixels) { 158 // attempt to allocate and initialize a fake frame of pixels. 159 const size_t buflen = GetFrameBufLen(&device->actual_spec); 160 device->zombie_pixels = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen); 161 if (!device->zombie_pixels) { 162 *timestampNS = 0; 163 return SDL_CAMERA_FRAME_SKIP; // oh well, say there isn't a frame yet, so we'll go back to waiting. Maybe allocation will succeed later...? 164 } 165 166 Uint8 *dst = device->zombie_pixels; 167 switch (spec->format) { 168 // in YUV formats, the U and V values must be 128 to get a black frame. If set to zero, it'll be bright green. 169 case SDL_PIXELFORMAT_YV12: 170 case SDL_PIXELFORMAT_IYUV: 171 case SDL_PIXELFORMAT_NV12: 172 case SDL_PIXELFORMAT_NV21: 173 SDL_memset(dst, 0, spec->width * spec->height); // set Y to zero. 174 SDL_memset(dst + (spec->width * spec->height), 128, (spec->width * spec->height) / 2); // set U and V to 128. 175 break; 176 177 case SDL_PIXELFORMAT_YUY2: 178 case SDL_PIXELFORMAT_YVYU: 179 // Interleaved Y1[U1|V1]Y2[U2|V2]. 180 for (size_t i = 0; i < buflen; i += 4) { 181 dst[i] = 0; 182 dst[i+1] = 128; 183 dst[i+2] = 0; 184 dst[i+3] = 128; 185 } 186 break; 187 188 189 case SDL_PIXELFORMAT_UYVY: 190 // Interleaved [U1|V1]Y1[U2|V2]Y2. 191 for (size_t i = 0; i < buflen; i += 4) { 192 dst[i] = 128; 193 dst[i+1] = 0; 194 dst[i+2] = 128; 195 dst[i+3] = 0; 196 } 197 break; 198 199 default: 200 // just zero everything else, it'll _probably_ be okay. 201 SDL_memset(dst, 0, buflen); 202 break; 203 } 204 } 205 206 207 *timestampNS = SDL_GetTicksNS(); 208 frame->pixels = device->zombie_pixels; 209 210 // SDL (currently) wants the pitch of YUV formats to be the pitch of the (1-byte-per-pixel) Y plane. 211 frame->pitch = spec->width; 212 if (!SDL_ISPIXELFORMAT_FOURCC(spec->format)) { // checking if it's not FOURCC to only do this for non-YUV data is good enough for now. 213 frame->pitch *= SDL_BYTESPERPIXEL(spec->format); 214 } 215 216 #if DEBUG_CAMERA 217 SDL_Log("CAMERA: dev[%p] Acquired Zombie frame, timestamp %llu", device, (unsigned long long) *timestampNS); 218 #endif 219 220 return SDL_CAMERA_FRAME_READY; // frame is available. 221} 222 223static void ZombieReleaseFrame(SDL_Camera *device, SDL_Surface *frame) // Reclaim frame->pixels and frame->pitch! 224{ 225 if (frame->pixels != device->zombie_pixels) { 226 // this was a frame from before the disconnect event; let the backend make an attempt to free it. 227 camera_driver.impl.ReleaseFrame(device, frame); 228 } 229 // we just leave zombie_pixels alone, as we'll reuse it for every new frame until the camera is closed. 230} 231 232 233static void ObtainPhysicalCameraObj(SDL_Camera *device); 234static void ReleaseCamera(SDL_Camera *device); 235 236 237static void ClosePhysicalCamera(SDL_Camera *device) 238{ 239 if (!device || (device->hidden == NULL)) { 240 return; // device is not open. 241 } 242 243 SDL_SetAtomicInt(&device->shutdown, 1); 244 245// !!! FIXME: the close_cond stuff from audio might help the race condition here. 246 247 if (device->thread != NULL) { 248 SDL_WaitThread(device->thread, NULL); 249 device->thread = NULL; 250 } 251 252 ObtainPhysicalCameraObj(device); 253 254 // release frames that are queued up somewhere... 255 if (!device->needs_conversion && !device->needs_scaling) { 256 for (SurfaceList *i = device->filled_output_surfaces.next; i != NULL; i = i->next) { 257 device->ReleaseFrame(device, i->surface); 258 } 259 for (SurfaceList *i = device->app_held_output_surfaces.next; i != NULL; i = i->next) { 260 device->ReleaseFrame(device, i->surface); 261 } 262 } 263 264 camera_driver.impl.CloseDevice(device); 265 device->hidden = NULL; // just in case backend didn't reset this. 266 267 SDL_DestroyProperties(device->props); 268 269 SDL_DestroySurface(device->acquire_surface); 270 device->acquire_surface = NULL; 271 SDL_DestroySurface(device->conversion_surface); 272 device->conversion_surface = NULL; 273 274 for (int i = 0; i < SDL_arraysize(device->output_surfaces); i++) { 275 SDL_DestroySurface(device->output_surfaces[i].surface); 276 } 277 SDL_zeroa(device->output_surfaces); 278 279 SDL_aligned_free(device->zombie_pixels); 280 281 device->permission = SDL_CAMERA_PERMISSION_STATE_PENDING; 282 device->zombie_pixels = NULL; 283 device->filled_output_surfaces.next = NULL; 284 device->empty_output_surfaces.next = NULL; 285 device->app_held_output_surfaces.next = NULL; 286 287 device->base_timestamp = 0; 288 device->adjust_timestamp = 0; 289 290 SDL_zero(device->spec); 291 UnrefPhysicalCamera(device); // we're closed, release a reference. 292 293 ReleaseCamera(device); 294} 295 296// Don't hold the device lock when calling this, as we may destroy the device! 297void UnrefPhysicalCamera(SDL_Camera *device) 298{ 299 if (SDL_AtomicDecRef(&device->refcount)) { 300 // take it out of the device list. This will call DestroyCameraHashItem to clean up the object. 301 SDL_LockRWLockForWriting(camera_driver.device_hash_lock); 302 if (SDL_RemoveFromHashTable(camera_driver.device_hash, (const void *) (uintptr_t) device->instance_id)) { 303 SDL_AddAtomicInt(&camera_driver.device_count, -1); 304 } 305 SDL_UnlockRWLock(camera_driver.device_hash_lock); 306 } 307} 308 309void RefPhysicalCamera(SDL_Camera *device) 310{ 311 SDL_AtomicIncRef(&device->refcount); 312} 313 314static void ObtainPhysicalCameraObj(SDL_Camera *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_ACQUIRE 315{ 316 if (device) { 317 RefPhysicalCamera(device); 318 SDL_LockMutex(device->lock); 319 } 320} 321 322static SDL_Camera *ObtainPhysicalCamera(SDL_CameraID devid) // !!! FIXME: SDL_ACQUIRE 323{ 324 if (!SDL_GetCurrentCameraDriver()) { 325 SDL_SetError("Camera subsystem is not initialized"); 326 return NULL; 327 } 328 329 SDL_Camera *device = NULL; 330 SDL_LockRWLockForReading(camera_driver.device_hash_lock); 331 SDL_FindInHashTable(camera_driver.device_hash, (const void *) (uintptr_t) devid, (const void **) &device); 332 SDL_UnlockRWLock(camera_driver.device_hash_lock); 333 if (!device) { 334 SDL_SetError("Invalid camera device instance ID"); 335 } else { 336 ObtainPhysicalCameraObj(device); 337 } 338 return device; 339} 340 341static void ReleaseCamera(SDL_Camera *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_RELEASE 342{ 343 if (device) { 344 SDL_UnlockMutex(device->lock); 345 UnrefPhysicalCamera(device); 346 } 347} 348 349// we want these sorted by format first, so you can find a block of all 350// resolutions that are supported for a format. The formats are sorted in 351// "best" order, but that's subjective: right now, we prefer planar 352// formats, since they're likely what the cameras prefer to produce 353// anyhow, and they basically send the same information in less space 354// than an RGB-style format. After that, sort by bits-per-pixel. 355 356// we want specs sorted largest to smallest dimensions, larger width taking precedence over larger height. 357static int SDLCALL CameraSpecCmp(const void *vpa, const void *vpb) 358{ 359 const SDL_CameraSpec *a = (const SDL_CameraSpec *) vpa; 360 const SDL_CameraSpec *b = (const SDL_CameraSpec *) vpb; 361 362 // driver shouldn't send specs like this, check here since we're eventually going to sniff the whole array anyhow. 363 SDL_assert(a->format != SDL_PIXELFORMAT_UNKNOWN); 364 SDL_assert(a->width > 0); 365 SDL_assert(a->height > 0); 366 SDL_assert(b->format != SDL_PIXELFORMAT_UNKNOWN); 367 SDL_assert(b->width > 0); 368 SDL_assert(b->height > 0); 369 370 const SDL_PixelFormat afmt = a->format; 371 const SDL_PixelFormat bfmt = b->format; 372 if (SDL_ISPIXELFORMAT_FOURCC(afmt) && !SDL_ISPIXELFORMAT_FOURCC(bfmt)) { 373 return -1; 374 } else if (!SDL_ISPIXELFORMAT_FOURCC(afmt) && SDL_ISPIXELFORMAT_FOURCC(bfmt)) { 375 return 1; 376 } else if (SDL_BITSPERPIXEL(afmt) > SDL_BITSPERPIXEL(bfmt)) { 377 return -1; 378 } else if (SDL_BITSPERPIXEL(bfmt) > SDL_BITSPERPIXEL(afmt)) { 379 return 1; 380 } else if (a->width > b->width) { 381 return -1; 382 } else if (b->width > a->width) { 383 return 1; 384 } else if (a->height > b->height) { 385 return -1; 386 } else if (b->height > a->height) { 387 return 1; 388 } 389 390 // still here? We care about framerate less than format or size, but faster is better than slow. 391 if (a->framerate_numerator && !b->framerate_numerator) { 392 return -1; 393 } else if (!a->framerate_numerator && b->framerate_numerator) { 394 return 1; 395 } 396 397 const float fpsa = ((float)a->framerate_numerator / a->framerate_denominator); 398 const float fpsb = ((float)b->framerate_numerator / b->framerate_denominator); 399 if (fpsa > fpsb) { 400 return -1; 401 } else if (fpsb > fpsa) { 402 return 1; 403 } 404 405 if (SDL_COLORSPACERANGE(a->colorspace) == SDL_COLOR_RANGE_FULL && 406 SDL_COLORSPACERANGE(b->colorspace) != SDL_COLOR_RANGE_FULL) { 407 return -1; 408 } 409 if (SDL_COLORSPACERANGE(a->colorspace) != SDL_COLOR_RANGE_FULL && 410 SDL_COLORSPACERANGE(b->colorspace) == SDL_COLOR_RANGE_FULL) { 411 return 1; 412 } 413 414 return 0; // apparently, they're equal. 415} 416 417// The camera backends call this when a new device is plugged in. 418SDL_Camera *SDL_AddCamera(const char *name, SDL_CameraPosition position, int num_specs, const SDL_CameraSpec *specs, void *handle) 419{ 420 SDL_assert(name != NULL); 421 SDL_assert(num_specs >= 0); 422 SDL_assert((specs != NULL) == (num_specs > 0)); 423 SDL_assert(handle != NULL); 424 425 SDL_LockRWLockForReading(camera_driver.device_hash_lock); 426 const int shutting_down = SDL_GetAtomicInt(&camera_driver.shutting_down); 427 SDL_UnlockRWLock(camera_driver.device_hash_lock); 428 if (shutting_down) { 429 return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment. 430 } 431 432 SDL_Camera *device = (SDL_Camera *)SDL_calloc(1, sizeof(SDL_Camera)); 433 if (!device) { 434 return NULL; 435 } 436 437 device->name = SDL_strdup(name); 438 if (!device->name) { 439 SDL_free(device); 440 return NULL; 441 } 442 443 device->position = position; 444 445 device->lock = SDL_CreateMutex(); 446 if (!device->lock) { 447 SDL_free(device->name); 448 SDL_free(device); 449 return NULL; 450 } 451 452 device->all_specs = (SDL_CameraSpec *)SDL_calloc(num_specs + 1, sizeof (*specs)); 453 if (!device->all_specs) { 454 SDL_DestroyMutex(device->lock); 455 SDL_free(device->name); 456 SDL_free(device); 457 return NULL; 458 } 459 460 if (num_specs > 0) { 461 SDL_memcpy(device->all_specs, specs, sizeof (*specs) * num_specs); 462 SDL_qsort(device->all_specs, num_specs, sizeof (*specs), CameraSpecCmp); 463 464 // weed out duplicates, just in case. 465 for (int i = 0; i < num_specs; i++) { 466 SDL_CameraSpec *a = &device->all_specs[i]; 467 SDL_CameraSpec *b = &device->all_specs[i + 1]; 468 if (SDL_memcmp(a, b, sizeof (*a)) == 0) { 469 SDL_memmove(a, b, sizeof (*specs) * (num_specs - i)); 470 i--; 471 num_specs--; 472 } 473 } 474 } 475 476 #if DEBUG_CAMERA 477 const char *posstr = "unknown position"; 478 if (position == SDL_CAMERA_POSITION_FRONT_FACING) { 479 posstr = "front-facing"; 480 } else if (position == SDL_CAMERA_POSITION_BACK_FACING) { 481 posstr = "back-facing"; 482 } 483 SDL_Log("CAMERA: Adding device '%s' (%s) with %d spec%s%s", name, posstr, num_specs, (num_specs == 1) ? "" : "s", (num_specs == 0) ? "" : ":"); 484 for (int i = 0; i < num_specs; i++) { 485 const SDL_CameraSpec *spec = &device->all_specs[i]; 486 SDL_Log("CAMERA: - fmt=%s, w=%d, h=%d, numerator=%d, denominator=%d", SDL_GetPixelFormatName(spec->format), spec->width, spec->height, spec->framerate_numerator, spec->framerate_denominator); 487 } 488 #endif 489 490 device->num_specs = num_specs; 491 device->handle = handle; 492 device->instance_id = SDL_GetNextObjectID(); 493 SDL_SetAtomicInt(&device->shutdown, 0); 494 SDL_SetAtomicInt(&device->zombie, 0); 495 RefPhysicalCamera(device); 496 497 SDL_LockRWLockForWriting(camera_driver.device_hash_lock); 498 if (SDL_InsertIntoHashTable(camera_driver.device_hash, (const void *) (uintptr_t) device->instance_id, device, false)) { 499 SDL_AddAtomicInt(&camera_driver.device_count, 1); 500 } else { 501 SDL_DestroyMutex(device->lock); 502 SDL_free(device->all_specs); 503 SDL_free(device->name); 504 SDL_free(device); 505 device = NULL; 506 } 507 508 // Add a device add event to the pending list, to be pushed when the event queue is pumped (away from any of our internal threads). 509 if (device) { 510 SDL_PendingCameraEvent *p = (SDL_PendingCameraEvent *) SDL_malloc(sizeof (SDL_PendingCameraEvent)); 511 if (p) { // if allocation fails, you won't get an event, but we can't help that. 512 p->type = SDL_EVENT_CAMERA_DEVICE_ADDED; 513 p->devid = device->instance_id; 514 p->next = NULL; 515 SDL_assert(camera_driver.pending_events_tail != NULL); 516 SDL_assert(camera_driver.pending_events_tail->next == NULL); 517 camera_driver.pending_events_tail->next = p; 518 camera_driver.pending_events_tail = p; 519 } 520 } 521 SDL_UnlockRWLock(camera_driver.device_hash_lock); 522 523 return device; 524} 525 526// Called when a device is removed from the system, or it fails unexpectedly, from any thread, possibly even the camera device's thread. 527void SDL_CameraDisconnected(SDL_Camera *device) 528{ 529 if (!device) { 530 return; 531 } 532 533 #if DEBUG_CAMERA 534 SDL_Log("CAMERA: DISCONNECTED! dev[%p]", device); 535 #endif 536 537 // Save off removal info in a list so we can send events for each, next 538 // time the event queue pumps, in case something tries to close a device 539 // from an event filter, as this would risk deadlocks and other disasters 540 // if done from the device thread. 541 SDL_PendingCameraEvent pending; 542 pending.next = NULL; 543 SDL_PendingCameraEvent *pending_tail = &pending; 544 545 ObtainPhysicalCameraObj(device); 546 547 const bool first_disconnect = SDL_CompareAndSwapAtomicInt(&device->zombie, 0, 1); 548 if (first_disconnect) { // if already disconnected this device, don't do it twice. 549 // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep 550 // making progress until the app closes it. Otherwise, streams might continue to 551 // accumulate waste data that never drains, apps that depend on audio callbacks to 552 // progress will freeze, etc. 553 device->WaitDevice = ZombieWaitDevice; 554 device->AcquireFrame = ZombieAcquireFrame; 555 device->ReleaseFrame = ZombieReleaseFrame; 556 557 // Zombie functions will just report the timestamp as SDL_GetTicksNS(), so we don't need to adjust anymore to get it to match. 558 device->adjust_timestamp = 0; 559 device->base_timestamp = 0; 560 561 SDL_PendingCameraEvent *p = (SDL_PendingCameraEvent *) SDL_malloc(sizeof (SDL_PendingCameraEvent)); 562 if (p) { // if this failed, no event for you, but you have deeper problems anyhow. 563 p->type = SDL_EVENT_CAMERA_DEVICE_REMOVED; 564 p->devid = device->instance_id; 565 p->next = NULL; 566 pending_tail->next = p; 567 pending_tail = p; 568 } 569 570 UnrefPhysicalCamera(device); // camera is disconnected, drop its reference 571 } 572 573 ReleaseCamera(device); 574 575 if (first_disconnect) { 576 if (pending.next) { // NULL if event is disabled or disaster struck. 577 SDL_LockRWLockForWriting(camera_driver.device_hash_lock); 578 SDL_assert(camera_driver.pending_events_tail != NULL); 579 SDL_assert(camera_driver.pending_events_tail->next == NULL); 580 camera_driver.pending_events_tail->next = pending.next; 581 camera_driver.pending_events_tail = pending_tail; 582 SDL_UnlockRWLock(camera_driver.device_hash_lock); 583 } 584 } 585} 586 587void SDL_CameraPermissionOutcome(SDL_Camera *device, bool approved) 588{ 589 if (!device) { 590 return; 591 } 592 593 SDL_PendingCameraEvent pending; 594 pending.next = NULL; 595 SDL_PendingCameraEvent *pending_tail = &pending; 596 597 const SDL_CameraPermissionState permission = approved ? SDL_CAMERA_PERMISSION_STATE_APPROVED : SDL_CAMERA_PERMISSION_STATE_DENIED; 598 599 ObtainPhysicalCameraObj(device); 600 if (device->permission != permission) { 601 device->permission = permission; 602 SDL_PendingCameraEvent *p = (SDL_PendingCameraEvent *) SDL_malloc(sizeof (SDL_PendingCameraEvent)); 603 if (p) { // if this failed, no event for you, but you have deeper problems anyhow. 604 p->type = approved ? SDL_EVENT_CAMERA_DEVICE_APPROVED : SDL_EVENT_CAMERA_DEVICE_DENIED; 605 p->devid = device->instance_id; 606 p->next = NULL; 607 pending_tail->next = p; 608 pending_tail = p; 609 } 610 } 611 612 ReleaseCamera(device); 613 614 if (pending.next) { // NULL if event is disabled or disaster struck. 615 SDL_LockRWLockForWriting(camera_driver.device_hash_lock); 616 SDL_assert(camera_driver.pending_events_tail != NULL); 617 SDL_assert(camera_driver.pending_events_tail->next == NULL); 618 camera_driver.pending_events_tail->next = pending.next; 619 camera_driver.pending_events_tail = pending_tail; 620 SDL_UnlockRWLock(camera_driver.device_hash_lock); 621 } 622} 623 624typedef struct FindOnePhysicalCameraByCallbackData 625{ 626 bool (*callback)(SDL_Camera *device, void *userdata); 627 void *userdata; 628 SDL_Camera *device; 629} FindOnePhysicalCameraByCallbackData; 630 631static bool SDLCALL FindOnePhysicalCameraByCallback(void *userdata, const SDL_HashTable *table, const void *key, const void *value) 632{ 633 FindOnePhysicalCameraByCallbackData *data = (FindOnePhysicalCameraByCallbackData *) userdata; 634 SDL_Camera *device = (SDL_Camera *) value; 635 if (data->callback(device, data->userdata)) { 636 data->device = device; 637 return false; // stop iterating. 638 } 639 return true; // keep iterating. 640} 641 642// !!! FIXME: this doesn't follow SDL convention of `userdata` being the first param of the callback. 643SDL_Camera *SDL_FindPhysicalCameraByCallback(bool (*callback)(SDL_Camera *device, void *userdata), void *userdata) 644{ 645 if (!SDL_GetCurrentCameraDriver()) { 646 SDL_SetError("Camera subsystem is not initialized"); 647 return NULL; 648 } 649 650 651 FindOnePhysicalCameraByCallbackData data = { callback, userdata, NULL }; 652 SDL_LockRWLockForReading(camera_driver.device_hash_lock); 653 SDL_IterateHashTable(camera_driver.device_hash, FindOnePhysicalCameraByCallback, &data); 654 SDL_UnlockRWLock(camera_driver.device_hash_lock); 655 656 if (!data.device) { 657 SDL_SetError("Device not found"); 658 } 659 660 return data.device; 661} 662 663void SDL_CloseCamera(SDL_Camera *camera) 664{ 665 SDL_Camera *device = camera; // currently there's no separation between physical and logical device. 666 ClosePhysicalCamera(device); 667} 668 669bool SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSpec *spec) 670{ 671 bool result; 672 673 CHECK_PARAM(!camera) { 674 return SDL_InvalidParamError("camera"); 675 } 676 CHECK_PARAM(!spec) { 677 return SDL_InvalidParamError("spec"); 678 } 679 680 SDL_Camera *device = camera; // currently there's no separation between physical and logical device. 681 ObtainPhysicalCameraObj(device); 682 if (device->permission > SDL_CAMERA_PERMISSION_STATE_PENDING) { 683 SDL_copyp(spec, &device->spec); 684 result = true; 685 } else { 686 SDL_zerop(spec); 687 result = SDL_SetError("Camera permission has not been granted"); 688 } 689 ReleaseCamera(device); 690 691 return result; 692} 693 694const char *SDL_GetCameraName(SDL_CameraID instance_id) 695{ 696 const char *result = NULL; 697 SDL_Camera *device = ObtainPhysicalCamera(instance_id); 698 if (device) { 699 result = SDL_GetPersistentString(device->name); 700 ReleaseCamera(device); 701 } 702 return result; 703} 704 705SDL_CameraPosition SDL_GetCameraPosition(SDL_CameraID instance_id) 706{ 707 SDL_CameraPosition result = SDL_CAMERA_POSITION_UNKNOWN; 708 SDL_Camera *device = ObtainPhysicalCamera(instance_id); 709 if (device) { 710 result = device->position; 711 ReleaseCamera(device); 712 } 713 return result; 714} 715 716 717typedef struct GetOneCameraData 718{ 719 SDL_CameraID *result; 720 int devs_seen; 721} GetOneCameraData; 722 723static bool SDLCALL GetOneCamera(void *userdata, const SDL_HashTable *table, const void *key, const void *value) 724{ 725 GetOneCameraData *data = (GetOneCameraData *) userdata; 726 data->result[data->devs_seen++] = (SDL_CameraID) (uintptr_t) key; 727 return true; // keep iterating. 728} 729 730SDL_CameraID *SDL_GetCameras(int *count) 731{ 732 int dummy_count; 733 if (!count) { 734 count = &dummy_count; 735 } 736 737 if (!SDL_GetCurrentCameraDriver()) { 738 *count = 0; 739 SDL_SetError("Camera subsystem is not initialized"); 740 return NULL; 741 } 742 743 SDL_CameraID *result = NULL; 744 745 SDL_LockRWLockForReading(camera_driver.device_hash_lock); 746 int num_devices = SDL_GetAtomicInt(&camera_driver.device_count); 747 result = (SDL_CameraID *) SDL_malloc((num_devices + 1) * sizeof (SDL_CameraID)); 748 if (!result) { 749 num_devices = 0; 750 } else { 751 GetOneCameraData data = { result, 0 }; 752 SDL_IterateHashTable(camera_driver.device_hash, GetOneCamera, &data); 753 SDL_assert(data.devs_seen == num_devices); 754 result[num_devices] = 0; // null-terminated. 755 } 756 SDL_UnlockRWLock(camera_driver.device_hash_lock); 757 758 *count = num_devices; 759 760 return result; 761} 762 763SDL_CameraSpec **SDL_GetCameraSupportedFormats(SDL_CameraID instance_id, int *count) 764{ 765 if (count) { 766 *count = 0; 767 } 768 769 SDL_Camera *device = ObtainPhysicalCamera(instance_id); 770 if (!device) { 771 return NULL; 772 } 773 774 int i; 775 int num_specs = device->num_specs; 776 SDL_CameraSpec **result = (SDL_CameraSpec **) SDL_malloc(((num_specs + 1) * sizeof(*result)) + (num_specs * sizeof (**result))); 777 if (result) { 778 SDL_CameraSpec *specs = (SDL_CameraSpec *)(result + (num_specs + 1)); 779 SDL_memcpy(specs, device->all_specs, num_specs * sizeof(*specs)); 780 for (i = 0; i < num_specs; ++i) { 781 result[i] = specs++; 782 } 783 result[i] = NULL; 784 785 if (count) { 786 *count = num_specs; 787 } 788 } 789 790 ReleaseCamera(device); 791 792 return result; 793} 794 795 796// Camera device thread. This is split into chunks, so drivers that need to control this directly can use the pieces they need without duplicating effort. 797 798void SDL_CameraThreadSetup(SDL_Camera *device) 799{ 800 //camera_driver.impl.ThreadInit(device); 801#ifdef SDL_VIDEO_DRIVER_ANDROID 802 // TODO 803 /* 804 { 805 // Set thread priority to THREAD_PRIORITY_VIDEO 806 extern void Android_JNI_CameraSetThreadPriority(int, int); 807 Android_JNI_CameraSetThreadPriority(device->recording, device); 808 }*/ 809#else 810 // The camera capture is always a high priority thread 811 SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_HIGH); 812#endif 813} 814 815bool SDL_CameraThreadIterate(SDL_Camera *device) 816{ 817 SDL_LockMutex(device->lock); 818 819 if (SDL_GetAtomicInt(&device->shutdown)) { 820 SDL_UnlockMutex(device->lock); 821 return false; // we're done, shut it down. 822 } 823 824 const int permission = device->permission; 825 if (permission <= SDL_CAMERA_PERMISSION_STATE_PENDING) { 826 SDL_UnlockMutex(device->lock); 827 return (permission < SDL_CAMERA_PERMISSION_STATE_PENDING) ? false : true; // if permission was denied, shut it down. if undecided, we're done for now. 828 } 829 830 bool failed = false; // set to true if disaster worthy of treating the device as lost has happened. 831 SDL_Surface *acquired = NULL; 832 SDL_Surface *output_surface = NULL; 833 SurfaceList *slist = NULL; 834 Uint64 timestampNS = 0; 835 float rotation = 0.0f; 836 837 // AcquireFrame SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! 838 const SDL_CameraFrameResult rc = device->AcquireFrame(device, device->acquire_surface, ×tampNS, &rotation); 839 840 if (rc == SDL_CAMERA_FRAME_READY) { // new frame acquired! 841 #if DEBUG_CAMERA 842 SDL_Log("CAMERA: New frame available! pixels=%p pitch=%d", device->acquire_surface->pixels, device->acquire_surface->pitch); 843 #endif 844 845 if (device->drop_frames > 0) { 846 #if DEBUG_CAMERA 847 SDL_Log("CAMERA: Dropping an initial frame"); 848 #endif 849 device->drop_frames--; 850 device->ReleaseFrame(device, device->acquire_surface); 851 device->acquire_surface->pixels = NULL; 852 device->acquire_surface->pitch = 0; 853 } else if (device->empty_output_surfaces.next == NULL) { 854 // uhoh, no output frames available! Either the app is slow, or it forgot to release frames when done with them. Drop this new frame. 855 #if DEBUG_CAMERA 856 SDL_Log("CAMERA: No empty output surfaces! Dropping frame!"); 857 #endif 858 device->ReleaseFrame(device, device->acquire_surface); 859 device->acquire_surface->pixels = NULL; 860 device->acquire_surface->pitch = 0; 861 } else { 862 if (!device->adjust_timestamp) { 863 device->adjust_timestamp = SDL_GetTicksNS(); 864 device->base_timestamp = timestampNS; 865 } 866 timestampNS = (timestampNS - device->base_timestamp) + device->adjust_timestamp; 867 868 slist = device->empty_output_surfaces.next; 869 output_surface = slist->surface; 870 device->empty_output_surfaces.next = slist->next; 871 acquired = device->acquire_surface; 872 slist->timestampNS = timestampNS; 873 } 874 } else if (rc == SDL_CAMERA_FRAME_SKIP) { // no frame available yet; not an error. 875 #if 0 //DEBUG_CAMERA 876 SDL_Log("CAMERA: No frame available yet."); 877 #endif 878 } else { // fatal error! 879 #if DEBUG_CAMERA 880 SDL_Log("CAMERA: dev[%p] error AcquireFrame: %s", device, SDL_GetError()); 881 #endif 882 failed = true; 883 } 884 885 // we can let go of the lock once we've tried to grab a frame of video and maybe moved the output frame off the empty list. 886 // this lets us chew up the CPU for conversion and scaling without blocking other threads. 887 SDL_UnlockMutex(device->lock); 888 889 if (failed) { 890 SDL_assert(slist == NULL); 891 SDL_assert(acquired == NULL); 892 SDL_CameraDisconnected(device); // doh. 893 } else if (acquired) { // we have a new frame, scale/convert if necessary and queue it for the app! 894 SDL_assert(slist != NULL); 895 if (!device->needs_scaling && !device->needs_conversion) { // no conversion needed? Just move the pointer/pitch into the output surface. 896 #if DEBUG_CAMERA 897 SDL_Log("CAMERA: Frame is going through without conversion!"); 898 #endif 899 output_surface->w = acquired->w; 900 output_surface->h = acquired->h; 901 output_surface->pixels = acquired->pixels; 902 output_surface->pitch = acquired->pitch; 903 } else { // convert/scale into a different surface. 904 #if DEBUG_CAMERA 905 SDL_Log("CAMERA: Frame is getting converted!"); 906 #endif 907 SDL_Surface *srcsurf = acquired; 908 if (device->needs_scaling == -1) { // downscaling? Do it first. -1: downscale, 0: no scaling, 1: upscale 909 SDL_Surface *dstsurf = device->needs_conversion ? device->conversion_surface : output_surface; 910 SDL_StretchSurface(srcsurf, NULL, dstsurf, NULL, SDL_SCALEMODE_NEAREST); // !!! FIXME: linear scale? letterboxing? 911 srcsurf = dstsurf; 912 } 913 if (device->needs_conversion) { 914 SDL_Surface *dstsurf = (device->needs_scaling == 1) ? device->conversion_surface : output_surface; 915 SDL_ConvertPixels(srcsurf->w, srcsurf->h, 916 srcsurf->format, srcsurf->pixels, srcsurf->pitch, 917 dstsurf->format, dstsurf->pixels, dstsurf->pitch); 918 srcsurf = dstsurf; 919 } 920 if (device->needs_scaling == 1) { // upscaling? Do it last. -1: downscale, 0: no scaling, 1: upscale 921 SDL_StretchSurface(srcsurf, NULL, output_surface, NULL, SDL_SCALEMODE_NEAREST); // !!! FIXME: linear scale? letterboxing? 922 } 923 924 // we made a copy, so we can give the driver back its resources. 925 device->ReleaseFrame(device, acquired); 926 } 927 928 // we either released these already after we copied the data, or the pointer was migrated to output_surface. 929 acquired->pixels = NULL; 930 acquired->pitch = 0; 931 932 SDL_SetFloatProperty(SDL_GetSurfaceProperties(output_surface), SDL_PROP_SURFACE_ROTATION_FLOAT, rotation); 933 934 // make the filled output surface available to the app. 935 SDL_LockMutex(device->lock); 936 slist->next = device->filled_output_surfaces.next; 937 device->filled_output_surfaces.next = slist; 938 SDL_UnlockMutex(device->lock); 939 } 940 941 return true; // always go on if not shutting down, even if device failed. 942} 943 944void SDL_CameraThreadShutdown(SDL_Camera *device) 945{ 946 //device->FlushRecording(device); 947 //camera_driver.impl.ThreadDeinit(device); 948 //SDL_CameraThreadFinalize(device); 949} 950 951// Actual thread entry point, if driver didn't handle this itself. 952static int SDLCALL CameraThread(void *devicep) 953{ 954 SDL_Camera *device = (SDL_Camera *) devicep; 955 956 #if DEBUG_CAMERA 957 SDL_Log("CAMERA: dev[%p] Start thread 'CameraThread'", devicep); 958 #endif 959 960 RefPhysicalCamera(device); // this thread holds a reference. 961 962 SDL_assert(device != NULL); 963 SDL_CameraThreadSetup(device); 964 965 do { 966 if (!device->WaitDevice(device)) { 967 SDL_CameraDisconnected(device); // doh. (but don't break out of the loop, just be a zombie for now!) 968 } 969 } while (SDL_CameraThreadIterate(device)); 970 971 SDL_CameraThreadShutdown(device); 972 973 UnrefPhysicalCamera(device); // this thread no longer holds a reference. 974 975 #if DEBUG_CAMERA 976 SDL_Log("CAMERA: dev[%p] End thread 'CameraThread'", devicep); 977 #endif 978 979 return 0; 980} 981 982bool SDL_PrepareCameraSurfaces(SDL_Camera *device) 983{ 984 SDL_CameraSpec *appspec = &device->spec; // the app wants this format. 985 const SDL_CameraSpec *devspec = &device->actual_spec; // the hardware is set to this format. 986 987 SDL_assert(device->acquire_surface == NULL); // shouldn't call this function twice on an opened camera! 988 SDL_assert(devspec->format != SDL_PIXELFORMAT_UNKNOWN); // fix the backend, it should have an actual format by now. 989 SDL_assert(devspec->width >= 0); // fix the backend, it should have an actual format by now. 990 SDL_assert(devspec->height >= 0); // fix the backend, it should have an actual format by now. 991 992 if (appspec->width <= 0 || appspec->height <= 0) { 993 appspec->width = devspec->width; 994 appspec->height = devspec->height; 995 } 996 997 if (appspec->format == SDL_PIXELFORMAT_UNKNOWN) { 998 appspec->format = devspec->format; 999 } 1000 1001 if (appspec->framerate_denominator == 0) { 1002 appspec->framerate_numerator = devspec->framerate_numerator; 1003 appspec->framerate_denominator = devspec->framerate_denominator; 1004 } 1005 1006 if ((devspec->width == appspec->width) && (devspec->height == appspec->height)) { 1007 device->needs_scaling = 0; 1008 } else { 1009 const Uint64 srcarea = ((Uint64) devspec->width) * ((Uint64) devspec->height); 1010 const Uint64 dstarea = ((Uint64) appspec->width) * ((Uint64) appspec->height); 1011 if (dstarea <= srcarea) { 1012 device->needs_scaling = -1; // downscaling (or changing to new aspect ratio with same area) 1013 } else { 1014 device->needs_scaling = 1; // upscaling 1015 } 1016 } 1017 1018 device->needs_conversion = (devspec->format != appspec->format); 1019 1020 device->acquire_surface = SDL_CreateSurfaceFrom(devspec->width, devspec->height, devspec->format, NULL, 0); 1021 if (!device->acquire_surface) { 1022 goto failed; 1023 } 1024 SDL_SetSurfaceColorspace(device->acquire_surface, devspec->colorspace); 1025 1026 // if we have to scale _and_ convert, we need a middleman surface, since we can't do both changes at once. 1027 if (device->needs_scaling && device->needs_conversion) { 1028 const bool downscaling_first = (device->needs_scaling < 0); 1029 const SDL_CameraSpec *s = downscaling_first ? appspec : devspec; 1030 const SDL_PixelFormat fmt = downscaling_first ? devspec->format : appspec->format; 1031 device->conversion_surface = SDL_CreateSurface(s->width, s->height, fmt); 1032 if (!device->conversion_surface) { 1033 goto failed; 1034 } 1035 SDL_SetSurfaceColorspace(device->conversion_surface, devspec->colorspace); 1036 } 1037 1038 // output surfaces are in the app-requested format. If no conversion is necessary, we'll just use the pointers 1039 // the backend fills into acquired_surface, and you can get all the way from DMA access in the camera hardware 1040 // to the app without a single copy. Otherwise, these will be full surfaces that hold converted/scaled copies. 1041 1042 for (int i = 0; i < (SDL_arraysize(device->output_surfaces) - 1); i++) { 1043 device->output_surfaces[i].next = &device->output_surfaces[i + 1]; 1044 } 1045 device->empty_output_surfaces.next = device->output_surfaces; 1046 1047 for (int i = 0; i < SDL_arraysize(device->output_surfaces); i++) { 1048 SDL_Surface *surf; 1049 if (device->needs_scaling || device->needs_conversion) { 1050 surf = SDL_CreateSurface(appspec->width, appspec->height, appspec->format); 1051 } else { 1052 surf = SDL_CreateSurfaceFrom(appspec->width, appspec->height, appspec->format, NULL, 0); 1053 } 1054 if (!surf) { 1055 goto failed; 1056 } 1057 SDL_SetSurfaceColorspace(surf, devspec->colorspace); 1058 1059 device->output_surfaces[i].surface = surf; 1060 } 1061 1062 return true; 1063 1064failed: 1065 if (device->acquire_surface) { 1066 SDL_DestroySurface(device->acquire_surface); 1067 device->acquire_surface = NULL; 1068 } 1069 1070 if (device->conversion_surface) { 1071 SDL_DestroySurface(device->conversion_surface); 1072 device->conversion_surface = NULL; 1073 } 1074 1075 for (int i = 0; i < SDL_arraysize(device->output_surfaces); i++) { 1076 SDL_Surface *surf = device->output_surfaces[i].surface; 1077 if (surf) { 1078 SDL_DestroySurface(surf); 1079 } 1080 } 1081 SDL_zeroa(device->output_surfaces); 1082 1083 return false; 1084} 1085 1086static void ChooseBestCameraSpec(SDL_Camera *device, const SDL_CameraSpec *spec, SDL_CameraSpec *closest) 1087{ 1088 // Find the closest available native format/size... 1089 // 1090 // We want the exact size if possible, even if we have 1091 // to convert formats, because we can _probably_ do that 1092 // conversion losslessly at less expense verses scaling. 1093 // 1094 // Failing that, we want the size that's closest to the 1095 // requested aspect ratio, then the closest size within 1096 // that. 1097 1098 SDL_zerop(closest); 1099 1100 if (device->num_specs == 0) { // device listed no specs! You get whatever you want! 1101 if (spec) { 1102 SDL_copyp(closest, spec); 1103 } 1104 return; 1105 } else if (!spec) { // nothing specifically requested, get the best format we can... 1106 // we sorted this into the "best" format order when adding the camera. 1107 SDL_copyp(closest, &device->all_specs[0]); 1108 } else { // specific thing requested, try to get as close to that as possible... 1109 const int num_specs = device->num_specs; 1110 int wantw = spec->width; 1111 int wanth = spec->height; 1112 1113 if (wantw > 0 && wanth > 0) { 1114 // Find the sizes with the closest aspect ratio and then find the best fit of those. 1115 const float wantaspect = ((float)wantw) / ((float)wanth); 1116 const float epsilon = 1e-6f; 1117 float closestaspect = -9999999.0f; 1118 float closestdiff = 999999.0f; 1119 int closestdiffw = 9999999; 1120 1121 for (int i = 0; i < num_specs; i++) { 1122 const SDL_CameraSpec *thisspec = &device->all_specs[i]; 1123 const int thisw = thisspec->width; 1124 const int thish = thisspec->height; 1125 const float thisaspect = ((float)thisw) / ((float)thish); 1126 const float aspectdiff = SDL_fabsf(wantaspect - thisaspect); 1127 const float diff = SDL_fabsf(closestaspect - thisaspect); 1128 const int diffw = SDL_abs(thisw - wantw); 1129 if (diff < epsilon) { // matches current closestaspect? See if resolution is closer in size. 1130 if (diffw < closestdiffw) { 1131 closestdiffw = diffw; 1132 closest->width = thisw; 1133 closest->height = thish; 1134 } 1135 } else if (aspectdiff < closestdiff) { // this is a closer aspect ratio? Take it, reset resolution checks. 1136 closestdiff = aspectdiff; 1137 closestaspect = thisaspect; 1138 closestdiffw = diffw; 1139 closest->width = thisw; 1140 closest->height = thish; 1141 } 1142 } 1143 } else { 1144 SDL_copyp(closest, &device->all_specs[0]); 1145 } 1146 1147 SDL_assert(closest->width > 0); 1148 SDL_assert(closest->height > 0); 1149 1150 // okay, we have what we think is the best resolution, now we just need the best format that supports it... 1151 const SDL_PixelFormat wantfmt = spec->format; 1152 SDL_PixelFormat best_format = SDL_PIXELFORMAT_UNKNOWN; 1153 SDL_Colorspace best_colorspace = SDL_COLORSPACE_UNKNOWN; 1154 for (int i = 0; i < num_specs; i++) { 1155 const SDL_CameraSpec *thisspec = &device->all_specs[i]; 1156 if ((thisspec->width == closest->width) && (thisspec->height == closest->height)) { 1157 if (best_format == SDL_PIXELFORMAT_UNKNOWN) { 1158 best_format = thisspec->format; // spec list is sorted by what we consider "best" format, so unless we find an exact match later, first size match is the one! 1159 best_colorspace = thisspec->colorspace; 1160 } 1161 if (thisspec->format == wantfmt) { 1162 best_format = thisspec->format; 1163 best_colorspace = thisspec->colorspace; 1164 break; // exact match, stop looking. 1165 } 1166 } 1167 } 1168 1169 SDL_assert(best_format != SDL_PIXELFORMAT_UNKNOWN); 1170 SDL_assert(best_colorspace != SDL_COLORSPACE_UNKNOWN); 1171 closest->format = best_format; 1172 closest->colorspace = best_colorspace; 1173 1174 // We have a resolution and a format, find the closest framerate... 1175 const float wantfps = spec->framerate_denominator ? ((float)spec->framerate_numerator / spec->framerate_denominator) : 0.0f; 1176 float closestfps = 9999999.0f; 1177 for (int i = 0; i < num_specs; i++) { 1178 const SDL_CameraSpec *thisspec = &device->all_specs[i]; 1179 if ((thisspec->format == closest->format) && (thisspec->width == closest->width) && (thisspec->height == closest->height)) { 1180 if ((thisspec->framerate_numerator == spec->framerate_numerator) && (thisspec->framerate_denominator == spec->framerate_denominator)) { 1181 closest->framerate_numerator = thisspec->framerate_numerator; 1182 closest->framerate_denominator = thisspec->framerate_denominator; 1183 break; // exact match, stop looking. 1184 } 1185 1186 const float thisfps = thisspec->framerate_denominator ? ((float)thisspec->framerate_numerator / thisspec->framerate_denominator) : 0.0f; 1187 const float fpsdiff = SDL_fabsf(wantfps - thisfps); 1188 if (fpsdiff < closestfps) { // this is a closest FPS? Take it until something closer arrives. 1189 closestfps = fpsdiff; 1190 closest->framerate_numerator = thisspec->framerate_numerator; 1191 closest->framerate_denominator = thisspec->framerate_denominator; 1192 } 1193 } 1194 } 1195 } 1196 1197 SDL_assert(closest->width > 0); 1198 SDL_assert(closest->height > 0); 1199 SDL_assert(closest->format != SDL_PIXELFORMAT_UNKNOWN); 1200} 1201 1202SDL_Camera *SDL_OpenCamera(SDL_CameraID instance_id, const SDL_CameraSpec *spec) 1203{ 1204 SDL_Camera *device = ObtainPhysicalCamera(instance_id); 1205 if (!device) { 1206 return NULL; 1207 } 1208 1209 if (device->hidden != NULL) { 1210 ReleaseCamera(device); 1211 SDL_SetError("Camera already opened"); // we may remove this limitation at some point. 1212 return NULL; 1213 } 1214 1215 SDL_SetAtomicInt(&device->shutdown, 0); 1216 1217 // These start with the backend's implementation, but we might swap them out with zombie versions later. 1218 device->WaitDevice = camera_driver.impl.WaitDevice; 1219 device->AcquireFrame = camera_driver.impl.AcquireFrame; 1220 device->ReleaseFrame = camera_driver.impl.ReleaseFrame; 1221 1222 SDL_CameraSpec closest; 1223 ChooseBestCameraSpec(device, spec, &closest); 1224 1225 #if DEBUG_CAMERA 1226 SDL_Log("CAMERA: App wanted [(%dx%d) fmt=%s framerate=%d/%d], chose [(%dx%d) fmt=%s framerate=%d/%d]", 1227 spec ? spec->width : -1, spec ? spec->height : -1, spec ? SDL_GetPixelFormatName(spec->format) : "(null)", spec ? spec->framerate_numerator : -1, spec ? spec->framerate_denominator : -1, 1228 closest.width, closest.height, SDL_GetPixelFormatName(closest.format), closest.framerate_numerator, closest.framerate_denominator); 1229 #endif 1230 1231 if (!camera_driver.impl.OpenDevice(device, &closest)) { 1232 ClosePhysicalCamera(device); // in case anything is half-initialized. 1233 ReleaseCamera(device); 1234 return NULL; 1235 } 1236 1237 SDL_copyp(&device->spec, spec ? spec : &closest); 1238 SDL_copyp(&device->actual_spec, &closest); 1239 1240 // SDL_PIXELFORMAT_UNKNOWN here is taken as a signal that the backend 1241 // doesn't know its format yet (Emscripten waiting for user permission, 1242 // in this case), and the backend will call SDL_PrepareCameraSurfaces() 1243 // itself, later but before the app is allowed to acquire images. 1244 if (closest.format != SDL_PIXELFORMAT_UNKNOWN) { 1245 if (!SDL_PrepareCameraSurfaces(device)) { 1246 ClosePhysicalCamera(device); 1247 ReleaseCamera(device); 1248 return NULL; 1249 } 1250 } 1251 1252 device->drop_frames = 1; 1253 1254 // Start the camera thread if necessary 1255 if (!camera_driver.impl.ProvidesOwnCallbackThread) { 1256 char threadname[64]; 1257 SDL_GetCameraThreadName(device, threadname, sizeof (threadname)); 1258 device->thread = SDL_CreateThread(CameraThread, threadname, device); 1259 if (!device->thread) { 1260 ClosePhysicalCamera(device); 1261 ReleaseCamera(device); 1262 SDL_SetError("Couldn't create camera thread"); 1263 return NULL; 1264 } 1265 } 1266 1267 RefPhysicalCamera(device); // we're open, hold a reference. 1268 1269 ReleaseCamera(device); // unlock, we're good to go! 1270 1271 return device; // currently there's no separation between physical and logical device. 1272} 1273 1274SDL_Surface *SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS) 1275{ 1276 if (timestampNS) { 1277 *timestampNS = 0; 1278 } 1279 1280 CHECK_PARAM(!camera) { 1281 SDL_InvalidParamError("camera"); 1282 return NULL; 1283 } 1284 1285 SDL_Camera *device = camera; // currently there's no separation between physical and logical device. 1286 1287 ObtainPhysicalCameraObj(device); 1288 1289 if (device->permission <= SDL_CAMERA_PERMISSION_STATE_PENDING) { 1290 ReleaseCamera(device); 1291 SDL_SetError("Camera permission has not been granted"); 1292 return NULL; 1293 } 1294 1295 SDL_Surface *result = NULL; 1296 1297 // frames are in this list from newest to oldest, so find the end of the list... 1298 SurfaceList *slistprev = &device->filled_output_surfaces; 1299 SurfaceList *slist = slistprev; 1300 while (slist->next) { 1301 slistprev = slist; 1302 slist = slist->next; 1303 } 1304 1305 const bool list_is_empty = (slist == slistprev); 1306 if (!list_is_empty) { // report the oldest frame. 1307 if (timestampNS) { 1308 *timestampNS = slist->timestampNS; 1309 } 1310 result = slist->surface; 1311 slistprev->next = slist->next; // remove from filled list. 1312 slist->next = device->app_held_output_surfaces.next; // add to app_held list. 1313 device->app_held_output_surfaces.next = slist; 1314 } 1315 1316 ReleaseCamera(device); 1317 1318 return result; 1319} 1320 1321void SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame) 1322{ 1323 if (!camera || !frame) { 1324 return; 1325 } 1326 1327 SDL_Camera *device = camera; // currently there's no separation between physical and logical device. 1328 ObtainPhysicalCameraObj(device); 1329 1330 SurfaceList *slistprev = &device->app_held_output_surfaces; 1331 SurfaceList *slist; 1332 for (slist = slistprev->next; slist != NULL; slist = slist->next) { 1333 if (slist->surface == frame) { 1334 break; 1335 } 1336 slistprev = slist; 1337 } 1338 1339 if (!slist) { 1340 ReleaseCamera(device); 1341 return; 1342 } 1343 1344 // this pointer was owned by the backend (DMA memory or whatever), clear it out. 1345 if (!device->needs_conversion && !device->needs_scaling) { 1346 device->ReleaseFrame(device, frame); 1347 frame->pixels = NULL; 1348 frame->pitch = 0; 1349 } 1350 1351 slist->timestampNS = 0; 1352 1353 // remove from app_held list... 1354 slistprev->next = slist->next; 1355 1356 // insert at front of empty list (and we'll use it first when we need to fill a new frame). 1357 slist->next = device->empty_output_surfaces.next; 1358 device->empty_output_surfaces.next = slist; 1359 1360 ReleaseCamera(device); 1361} 1362 1363SDL_CameraID SDL_GetCameraID(SDL_Camera *camera) 1364{ 1365 SDL_CameraID result; 1366 1367 CHECK_PARAM(!camera) { 1368 SDL_InvalidParamError("camera"); 1369 return 0; 1370 } 1371 1372 SDL_Camera *device = camera; // currently there's no separation between physical and logical device. 1373 ObtainPhysicalCameraObj(device); 1374 result = device->instance_id; 1375 ReleaseCamera(device); 1376 1377 return result; 1378} 1379 1380SDL_PropertiesID SDL_GetCameraProperties(SDL_Camera *camera) 1381{ 1382 SDL_PropertiesID result; 1383 1384 CHECK_PARAM(!camera) { 1385 SDL_InvalidParamError("camera"); 1386 return 0; 1387 } 1388 1389 SDL_Camera *device = camera; // currently there's no separation between physical and logical device. 1390 ObtainPhysicalCameraObj(device); 1391 if (device->props == 0) { 1392 device->props = SDL_CreateProperties(); 1393 } 1394 result = device->props; 1395 ReleaseCamera(device); 1396 1397 return result; 1398} 1399 1400SDL_CameraPermissionState SDL_GetCameraPermissionState(SDL_Camera *camera) 1401{ 1402 SDL_CameraPermissionState result; 1403 1404 CHECK_PARAM(!camera) { 1405 SDL_InvalidParamError("camera"); 1406 return SDL_CAMERA_PERMISSION_STATE_DENIED; 1407 } 1408 1409 SDL_Camera *device = camera; // currently there's no separation between physical and logical device. 1410 ObtainPhysicalCameraObj(device); 1411 result = device->permission; 1412 ReleaseCamera(device); 1413 1414 return result; 1415} 1416 1417 1418static void CompleteCameraEntryPoints(void) 1419{ 1420 // this doesn't currently fill in stub implementations, it just asserts the backend filled them all in. 1421 #define FILL_STUB(x) SDL_assert(camera_driver.impl.x != NULL) 1422 FILL_STUB(DetectDevices); 1423 FILL_STUB(OpenDevice); 1424 FILL_STUB(CloseDevice); 1425 FILL_STUB(AcquireFrame); 1426 FILL_STUB(ReleaseFrame); 1427 FILL_STUB(FreeDeviceHandle); 1428 FILL_STUB(Deinitialize); 1429 #undef FILL_STUB 1430} 1431 1432void SDL_QuitCamera(void) 1433{ 1434 if (!camera_driver.name) { // not initialized?! 1435 return; 1436 } 1437 1438 SDL_LockRWLockForWriting(camera_driver.device_hash_lock); 1439 SDL_SetAtomicInt(&camera_driver.shutting_down, 1); 1440 SDL_HashTable *device_hash = camera_driver.device_hash; 1441 camera_driver.device_hash = NULL; 1442 SDL_PendingCameraEvent *pending_events = camera_driver.pending_events.next; 1443 camera_driver.pending_events.next = NULL; 1444 SDL_SetAtomicInt(&camera_driver.device_count, 0); 1445 SDL_UnlockRWLock(camera_driver.device_hash_lock); 1446 1447 SDL_PendingCameraEvent *pending_next = NULL; 1448 for (SDL_PendingCameraEvent *i = pending_events; i; i = pending_next) { 1449 pending_next = i->next; 1450 SDL_free(i); 1451 } 1452 1453 SDL_DestroyHashTable(device_hash); 1454 1455 // Free the driver data 1456 camera_driver.impl.Deinitialize(); 1457 1458 SDL_DestroyRWLock(camera_driver.device_hash_lock); 1459 1460 SDL_zero(camera_driver); 1461} 1462 1463// Physical camera objects are only destroyed when removed from the device hash. 1464static void SDLCALL DestroyCameraHashItem(void *userdata, const void *key, const void *value) 1465{ 1466 SDL_Camera *device = (SDL_Camera *) value; 1467 1468 #if DEBUG_CAMERA 1469 SDL_Log("DESTROYING CAMERA '%s'", device->name); 1470 #endif 1471 1472 ClosePhysicalCamera(device); 1473 camera_driver.impl.FreeDeviceHandle(device); 1474 SDL_DestroyMutex(device->lock); 1475 SDL_free(device->all_specs); 1476 SDL_free(device->name); 1477 SDL_free(device); 1478} 1479 1480bool SDL_CameraInit(const char *driver_name) 1481{ 1482 if (SDL_GetCurrentCameraDriver()) { 1483 SDL_QuitCamera(); // shutdown driver if already running. 1484 } 1485 1486 SDL_RWLock *device_hash_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole camera subsystem. 1487 if (!device_hash_lock) { 1488 return false; 1489 } 1490 1491 SDL_HashTable *device_hash = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, DestroyCameraHashItem, NULL); 1492 if (!device_hash) { 1493 SDL_DestroyRWLock(device_hash_lock); 1494 return false; 1495 } 1496 1497 // Select the proper camera driver 1498 if (!driver_name) { 1499 driver_name = SDL_GetHint(SDL_HINT_CAMERA_DRIVER); 1500 } 1501 1502 bool initialized = false; 1503 bool tried_to_init = false; 1504 1505 if (driver_name && (*driver_name != 0)) { 1506 char *driver_name_copy = SDL_strdup(driver_name); 1507 const char *driver_attempt = driver_name_copy; 1508 1509 if (!driver_name_copy) { 1510 SDL_DestroyRWLock(device_hash_lock); 1511 SDL_DestroyHashTable(device_hash); 1512 return false; 1513 } 1514 1515 while (driver_attempt && (*driver_attempt != 0) && !initialized) { 1516 char *driver_attempt_end = SDL_strchr(driver_attempt, ','); 1517 if (driver_attempt_end) { 1518 *driver_attempt_end = '\0'; 1519 } 1520 1521 for (int i = 0; bootstrap[i]; i++) { 1522 if (SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) { 1523 tried_to_init = true; 1524 SDL_zero(camera_driver); 1525 camera_driver.pending_events_tail = &camera_driver.pending_events; 1526 camera_driver.device_hash_lock = device_hash_lock; 1527 camera_driver.device_hash = device_hash; 1528 if (bootstrap[i]->init(&camera_driver.impl)) { 1529 camera_driver.name = bootstrap[i]->name; 1530 camera_driver.desc = bootstrap[i]->desc; 1531 initialized = true; 1532 } 1533 break; 1534 } 1535 } 1536 1537 driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; 1538 } 1539 1540 SDL_free(driver_name_copy); 1541 } else { 1542 for (int i = 0; !initialized && bootstrap[i]; i++) { 1543 if (bootstrap[i]->demand_only) { 1544 continue; 1545 } 1546 1547 tried_to_init = true; 1548 SDL_zero(camera_driver); 1549 camera_driver.pending_events_tail = &camera_driver.pending_events; 1550 camera_driver.device_hash_lock = device_hash_lock; 1551 camera_driver.device_hash = device_hash; 1552 if (bootstrap[i]->init(&camera_driver.impl)) { 1553 camera_driver.name = bootstrap[i]->name; 1554 camera_driver.desc = bootstrap[i]->desc; 1555 initialized = true; 1556 } 1557 } 1558 } 1559 1560 if (initialized) { 1561 SDL_DebugLogBackend("camera", camera_driver.name); 1562 } else { 1563 // specific drivers will set the error message if they fail, but otherwise we do it here. 1564 if (!tried_to_init) { 1565 if (driver_name) { 1566 SDL_SetError("Camera driver '%s' not available", driver_name); 1567 } else { 1568 SDL_SetError("No available camera driver"); 1569 } 1570 } 1571 1572 SDL_zero(camera_driver); 1573 SDL_DestroyRWLock(device_hash_lock); 1574 SDL_DestroyHashTable(device_hash); 1575 return false; // No driver was available, so fail. 1576 } 1577 1578 CompleteCameraEntryPoints(); 1579 1580 // Make sure we have a list of devices available at startup... 1581 camera_driver.impl.DetectDevices(); 1582 1583 return true; 1584} 1585 1586// This is an internal function, so SDL_PumpEvents() can check for pending camera device events. 1587// ("UpdateSubsystem" is the same naming that the other things that hook into PumpEvents use.) 1588void SDL_UpdateCamera(void) 1589{ 1590 SDL_LockRWLockForReading(camera_driver.device_hash_lock); 1591 SDL_PendingCameraEvent *pending_events = camera_driver.pending_events.next; 1592 SDL_UnlockRWLock(camera_driver.device_hash_lock); 1593 1594 if (!pending_events) { 1595 return; // nothing to do, check next time. 1596 } 1597 1598 // okay, let's take this whole list of events so we can dump the lock, and new ones can queue up for a later update. 1599 SDL_LockRWLockForWriting(camera_driver.device_hash_lock); 1600 pending_events = camera_driver.pending_events.next; // in case this changed... 1601 camera_driver.pending_events.next = NULL; 1602 camera_driver.pending_events_tail = &camera_driver.pending_events; 1603 SDL_UnlockRWLock(camera_driver.device_hash_lock); 1604 1605 SDL_PendingCameraEvent *pending_next = NULL; 1606 for (SDL_PendingCameraEvent *i = pending_events; i; i = pending_next) { 1607 pending_next = i->next; 1608 if (SDL_EventEnabled(i->type)) { 1609 SDL_Event event; 1610 SDL_zero(event); 1611 event.type = i->type; 1612 event.cdevice.which = (Uint32) i->devid; 1613 SDL_PushEvent(&event); 1614 } 1615 SDL_free(i); 1616 } 1617} 1618 1619[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.