From 1191f81a3c559a62ad463109faf1c8b5b2776c28 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 10 Oct 2025 16:21:59 -0400
Subject: [PATCH] camera: Make refcounts more robust, and actually destroy
disconnected cameras.
Fixes #14049.
---
src/camera/SDL_camera.c | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c
index 10320613a4ff8..7a9172643701f 100644
--- a/src/camera/SDL_camera.c
+++ b/src/camera/SDL_camera.c
@@ -229,10 +229,15 @@ static void ZombieReleaseFrame(SDL_Camera *device, SDL_Surface *frame) // Reclai
// we just leave zombie_pixels alone, as we'll reuse it for every new frame until the camera is closed.
}
+
+static void ObtainPhysicalCameraObj(SDL_Camera *device);
+static void ReleaseCamera(SDL_Camera *device);
+
+
static void ClosePhysicalCamera(SDL_Camera *device)
{
- if (!device) {
- return;
+ if (!device || (device->hidden == NULL)) {
+ return; // device is not open.
}
SDL_SetAtomicInt(&device->shutdown, 1);
@@ -244,6 +249,8 @@ static void ClosePhysicalCamera(SDL_Camera *device)
device->thread = NULL;
}
+ ObtainPhysicalCameraObj(device);
+
// release frames that are queued up somewhere...
if (!device->needs_conversion && !device->needs_scaling) {
for (SurfaceList *i = device->filled_output_surfaces.next; i != NULL; i = i->next) {
@@ -255,6 +262,7 @@ static void ClosePhysicalCamera(SDL_Camera *device)
}
camera_driver.impl.CloseDevice(device);
+ device->hidden = NULL; // just in case backend didn't reset this.
SDL_DestroyProperties(device->props);
@@ -280,13 +288,16 @@ static void ClosePhysicalCamera(SDL_Camera *device)
device->adjust_timestamp = 0;
SDL_zero(device->spec);
+ UnrefPhysicalCamera(device); // we're closed, release a reference.
+
+ ReleaseCamera(device);
}
// Don't hold the device lock when calling this, as we may destroy the device!
void UnrefPhysicalCamera(SDL_Camera *device)
{
if (SDL_AtomicDecRef(&device->refcount)) {
- // take it out of the device list.
+ // take it out of the device list. This will call DestroyCameraHashItem to clean up the object.
SDL_LockRWLockForWriting(camera_driver.device_hash_lock);
if (SDL_RemoveFromHashTable(camera_driver.device_hash, (const void *) (uintptr_t) device->instance_id)) {
SDL_AddAtomicInt(&camera_driver.device_count, -1);
@@ -555,6 +566,8 @@ void SDL_CameraDisconnected(SDL_Camera *device)
pending_tail->next = p;
pending_tail = p;
}
+
+ UnrefPhysicalCamera(device); // camera is disconnected, drop its reference
}
ReleaseCamera(device);
@@ -941,6 +954,8 @@ static int SDLCALL CameraThread(void *devicep)
SDL_Log("CAMERA: dev[%p] Start thread 'CameraThread'", devicep);
#endif
+ RefPhysicalCamera(device); // this thread holds a reference.
+
SDL_assert(device != NULL);
SDL_CameraThreadSetup(device);
@@ -952,6 +967,8 @@ static int SDLCALL CameraThread(void *devicep)
SDL_CameraThreadShutdown(device);
+ UnrefPhysicalCamera(device); // this thread no longer holds a reference.
+
#if DEBUG_CAMERA
SDL_Log("CAMERA: dev[%p] End thread 'CameraThread'", devicep);
#endif
@@ -1245,6 +1262,8 @@ SDL_Camera *SDL_OpenCamera(SDL_CameraID instance_id, const SDL_CameraSpec *spec)
}
}
+ RefPhysicalCamera(device); // we're open, hold a reference.
+
ReleaseCamera(device); // unlock, we're good to go!
return device; // currently there's no separation between physical and logical device.
@@ -1443,6 +1462,11 @@ void SDL_QuitCamera(void)
static void SDLCALL DestroyCameraHashItem(void *userdata, const void *key, const void *value)
{
SDL_Camera *device = (SDL_Camera *) value;
+
+ #if DEBUG_CAMERA
+ SDL_Log("DESTROYING CAMERA '%s'", device->name);
+ #endif
+
ClosePhysicalCamera(device);
camera_driver.impl.FreeDeviceHandle(device);
SDL_DestroyMutex(device->lock);