Browse Source

camera: Make refcounts more robust, and actually destroy disconnected cameras.

Fixes #14049.
Ryan C. Gordon 1 day ago
parent
commit
1191f81a3c
1 changed files with 27 additions and 3 deletions
  1. 27 3
      src/camera/SDL_camera.c

+ 27 - 3
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);