Browse Source

camera: Reworked to operate with a driver interface, like other subsystems.

Ryan C. Gordon 1 year ago
parent
commit
cb10c80aaf

+ 23 - 5
CMakeLists.txt

@@ -332,6 +332,7 @@ set_option(SDL_KMSDRM              "Use KMS DRM video driver" ${UNIX_SYS})
 dep_option(SDL_KMSDRM_SHARED       "Dynamically load KMS DRM support" ON "SDL_KMSDRM" OFF)
 set_option(SDL_OFFSCREEN           "Use offscreen video driver" ON)
 dep_option(SDL_CAMERA              "Enable camera support" ON SDL_VIDEO OFF)
+set_option(SDL_DUMMYCAMERA         "Support the dummy camera driver" ON)
 option_string(SDL_BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" OFF)
 option_string(SDL_FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" OFF)
 dep_option(SDL_HIDAPI              "Enable the HIDAPI subsystem" ON "NOT VISIONOS" OFF)
@@ -1172,6 +1173,23 @@ if(SDL_AUDIO)
   endif()
 endif()
 
+if(SDL_CAMERA)
+  # CheckDummyCamera/CheckDiskCamera - valid for all platforms
+  if(SDL_DUMMYCAMERA)
+    set(SDL_CAMERA_DRIVER_DUMMY 1)
+    sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c")
+    set(HAVE_DUMMYCAMERA TRUE)
+    set(HAVE_SDL_CAMERA TRUE)
+  endif()
+  # !!! FIXME: for later.
+  #if(SDL_DISKCAMERA)
+  #  set(SDL_CAMERA_DRIVER_DISK 1)
+  #  sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/disk/*.c")
+  #  set(HAVE_DISKCAMERA TRUE)
+  #  set(HAVE_SDL_CAMERA TRUE)
+  #endif()
+endif()
+
 if(UNIX OR APPLE)
   # Relevant for Unix/Darwin only
   set(DYNAPI_NEEDS_DLOPEN 1)
@@ -1290,7 +1308,7 @@ if(ANDROID)
   endif()
 
   if(SDL_CAMERA)
-    set(SDL_CAMERA_ANDROID 1)
+    set(SDL_CAMERA_DRIVER_ANDROID 1)
     set(HAVE_CAMERA TRUE)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/android/*.c")
   endif()
@@ -1531,7 +1549,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
     endif()
 
     if(SDL_CAMERA AND HAVE_LINUX_VIDEODEV2_H)
-      set(SDL_CAMERA_V4L2 1)
+      set(SDL_CAMERA_DRIVER_V4L2 1)
       set(HAVE_CAMERA TRUE)
       sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/v4l2/*.c")
     endif()
@@ -2035,9 +2053,9 @@ elseif(APPLE)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/file/cocoa/*.m")
 
   if(IOS OR TVOS OR MACOSX OR DARWIN)
-    set(SDL_CAMERA_APPLE TRUE)
+    set(SDL_CAMERA_DRIVER_COREMEDIA 1)
     set(HAVE_CAMERA TRUE)
-    sdl_sources("${SDL3_SOURCE_DIR}/src/camera/apple/SDL_camera_apple.m")
+    sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/coremedia/*.m")
   endif()
 
   if(IOS OR TVOS OR VISIONOS)
@@ -2739,7 +2757,7 @@ if(NOT HAVE_SDL_MISC)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/dummy/*.c")
 endif()
 if(NOT HAVE_CAMERA)
-  set(SDL_CAMERA_DUMMY 1)
+  set(SDL_CAMERA_DRIVER_DUMMY 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c")
 endif()
 

+ 65 - 0
include/SDL3/SDL_camera.h

@@ -108,6 +108,71 @@ typedef struct SDL_CameraFrame
 } SDL_CameraFrame;
 
 
+/**
+ * Use this function to get the number of built-in camera drivers.
+ *
+ * This function returns a hardcoded number. This never returns a negative
+ * value; if there are no drivers compiled into this build of SDL, this
+ * function returns zero. The presence of a driver in this list does not mean
+ * it will function, it just means SDL is capable of interacting with that
+ * interface. For example, a build of SDL might have v4l2 support, but if
+ * there's no kernel support available, SDL's v4l2 driver would fail if used.
+ *
+ * By default, SDL tries all drivers, in its preferred order, until one is
+ * found to be usable.
+ *
+ * \returns the number of built-in camera drivers.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetCameraDriver
+ */
+extern DECLSPEC int SDLCALL SDL_GetNumCameraDrivers(void);
+
+/**
+ * Use this function to get the name of a built in camera driver.
+ *
+ * The list of camera drivers is given in the order that they are normally
+ * initialized by default; the drivers that seem more reasonable to choose
+ * first (as far as the SDL developers believe) are earlier in the list.
+ *
+ * The names of drivers are all simple, low-ASCII identifiers, like "v4l2",
+ * "coremedia" or "android". These never have Unicode characters, and are not
+ * meant to be proper names.
+ *
+ * \param index the index of the camera driver; the value ranges from 0 to
+ *              SDL_GetNumCameraDrivers() - 1
+ * \returns the name of the camera driver at the requested index, or NULL if an
+ *          invalid index was specified.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetNumCameraDrivers
+ */
+extern DECLSPEC const char *SDLCALL SDL_GetCameraDriver(int index);
+
+/**
+ * Get the name of the current camera driver.
+ *
+ * The returned string points to internal static memory and thus never becomes
+ * invalid, even if you quit the camera subsystem and initialize a new driver
+ * (although such a case would return a different static string from another
+ * call to this function, of course). As such, you should not modify or free
+ * the returned string.
+ *
+ * \returns the name of the current camera driver or NULL if no driver has been
+ *          initialized.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC const char *SDLCALL SDL_GetCurrentCameraDriver(void);
+
 /**
  * Get a list of currently connected camera devices.
  *

+ 16 - 1
include/SDL3/SDL_hints.h

@@ -355,6 +355,22 @@ extern "C" {
  */
 #define SDL_HINT_BMP_SAVE_LEGACY_FORMAT "SDL_BMP_SAVE_LEGACY_FORMAT"
 
+/**
+ *  A variable that decides what camera backend to use.
+ *
+ *  By default, SDL will try all available camera backends in a reasonable
+ *  order until it finds one that can work, but this hint allows the app
+ *  or user to force a specific target, such as "directshow" if, say, you are
+ *  on Windows Media Foundations but want to try DirectShow instead.
+ *
+ *  The default value is unset, in which case SDL will try to figure out
+ *  the best camera backend on your behalf. This hint needs to be set
+ *  before SDL_Init() is called to be useful.
+ *
+ *  This hint is available since SDL 3.0.0.
+ */
+#define SDL_HINT_CAMERA_DRIVER "SDL_CAMERA_DRIVER"
+
 /**
  * A variable controlling whether DirectInput should be used for controllers
  *
@@ -2478,7 +2494,6 @@ extern "C" {
  */
 #define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"
 
-
 /**
  *  An enumeration of hint priorities
  */

+ 2 - 1
include/SDL3/SDL_init.h

@@ -59,7 +59,8 @@ typedef enum
     SDL_INIT_HAPTIC       = 0x00001000,
     SDL_INIT_GAMEPAD      = 0x00002000,  /**< `SDL_INIT_GAMEPAD` implies `SDL_INIT_JOYSTICK` */
     SDL_INIT_EVENTS       = 0x00004000,
-    SDL_INIT_SENSOR       = 0x00008000
+    SDL_INIT_SENSOR       = 0x00008000,
+    SDL_INIT_CAMERA       = 0x00010000   /**< `SDL_INIT_CAMERA` implies `SDL_INIT_EVENTS` */
 } SDL_InitFlags;
 
 /**

+ 5 - 4
include/build_config/SDL_build_config.h.cmake

@@ -467,10 +467,11 @@
 #cmakedefine SDL_FILESYSTEM_N3DS @SDL_FILESYSTEM_N3DS@
 
 /* Enable camera subsystem */
-#cmakedefine SDL_CAMERA_DUMMY @SDL_CAMERA_DUMMY@
-#cmakedefine SDL_CAMERA_V4L2 @SDL_CAMERA_V4L2@
-#cmakedefine SDL_CAMERA_APPLE @SDL_CAMERA_APPLE@
-#cmakedefine SDL_CAMERA_ANDROID @SDL_CAMERA_ANDROID@
+#cmakedefine SDL_CAMERA_DRIVER_DUMMY @SDL_CAMERA_DRIVER_DUMMY@
+/* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK @SDL_CAMERA_DRIVER_DISK@ */
+#cmakedefine SDL_CAMERA_DRIVER_V4L2 @SDL_CAMERA_DRIVER_V4L2@
+#cmakedefine SDL_CAMERA_DRIVER_COREMEDIA @SDL_CAMERA_DRIVER_COREMEDIA@
+#cmakedefine SDL_CAMERA_DRIVER_ANDROID @SDL_CAMERA_DRIVER_ANDROID@
 
 /* Enable misc subsystem */
 #cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@

+ 1 - 1
include/build_config/SDL_build_config_android.h

@@ -191,6 +191,6 @@
 #define SDL_FILESYSTEM_ANDROID   1
 
 /* Enable the camera driver */
-#define SDL_CAMERA_ANDROID 1
+#define SDL_CAMERA_DRIVER_ANDROID 1
 
 #endif /* SDL_build_config_android_h_ */

+ 1 - 1
include/build_config/SDL_build_config_emscripten.h

@@ -210,6 +210,6 @@
 #define SDL_FILESYSTEM_EMSCRIPTEN 1
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */  /* !!! FIXME */
-#define SDL_CAMERA_DUMMY  1
+#define SDL_CAMERA_DRIVER_DUMMY  1
 
 #endif /* SDL_build_config_emscripten_h */

+ 1 - 1
include/build_config/SDL_build_config_ios.h

@@ -213,6 +213,6 @@
 #define SDL_FILESYSTEM_COCOA   1
 
 /* enable camera support */
-#define SDL_CAMERA_APPLE 1
+#define SDL_CAMERA_DRIVER_COREMEDIA 1
 
 #endif /* SDL_build_config_ios_h_ */

+ 2 - 1
include/build_config/SDL_build_config_macos.h

@@ -270,7 +270,8 @@
 #define SDL_FILESYSTEM_COCOA   1
 
 /* enable camera support */
-#define SDL_CAMERA_APPLE 1
+#define SDL_CAMERA_DRIVER_COREMEDIA 1
+#define SDL_CAMERA_DRIVER_DUMMY 1
 
 /* Enable assembly routines */
 #ifdef __ppc__

+ 1 - 1
include/build_config/SDL_build_config_minimal.h

@@ -90,6 +90,6 @@ typedef unsigned int uintptr_t;
 #define SDL_FILESYSTEM_DUMMY  1
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */
-#define SDL_CAMERA_DUMMY  1
+#define SDL_CAMERA_DRIVER_DUMMY  1
 
 #endif /* SDL_build_config_minimal_h_ */

+ 1 - 1
include/build_config/SDL_build_config_ngage.h

@@ -87,6 +87,6 @@ typedef unsigned long      uintptr_t;
 #define SDL_FILESYSTEM_DUMMY 1
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */
-#define SDL_CAMERA_DUMMY  1
+#define SDL_CAMERA_DRIVER_DUMMY  1
 
 #endif /* SDL_build_config_ngage_h_ */

+ 1 - 1
include/build_config/SDL_build_config_windows.h

@@ -312,6 +312,6 @@ typedef unsigned int uintptr_t;
 #define SDL_FILESYSTEM_WINDOWS  1
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */  /* !!! FIXME */
-#define SDL_CAMERA_DUMMY  1
+#define SDL_CAMERA_DRIVER_DUMMY  1
 
 #endif /* SDL_build_config_windows_h_ */

+ 1 - 1
include/build_config/SDL_build_config_wingdk.h

@@ -248,7 +248,7 @@
 #define SDL_FILESYSTEM_WINDOWS  1
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */  /* !!! FIXME */
-#define SDL_CAMERA_DUMMY  1
+#define SDL_CAMERA_DRIVER_DUMMY  1
 
 /* Use the (inferior) GDK text input method for GDK platforms */
 /*#define SDL_GDK_TEXTINPUT 1*/

+ 1 - 1
include/build_config/SDL_build_config_winrt.h

@@ -216,6 +216,6 @@
 #define SDL_POWER_WINRT 1
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */  /* !!! FIXME */
-#define SDL_CAMERA_DUMMY  1
+#define SDL_CAMERA_DRIVER_DUMMY  1
 
 #endif /* SDL_build_config_winrt_h_ */

+ 1 - 1
include/build_config/SDL_build_config_xbox.h

@@ -237,6 +237,6 @@
 #define SDL_GDK_TEXTINPUT 1
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */
-#define SDL_CAMERA_DUMMY  1
+#define SDL_CAMERA_DRIVER_DUMMY  1
 
 #endif /* SDL_build_config_wingdk_h_ */

+ 37 - 0
src/SDL.c

@@ -46,6 +46,7 @@
 #include "joystick/SDL_gamepad_c.h"
 #include "joystick/SDL_joystick_c.h"
 #include "sensor/SDL_sensor_c.h"
+#include "camera/SDL_camera_c.h"
 
 #define SDL_INIT_EVERYTHING ~0U
 
@@ -365,6 +366,30 @@ int SDL_InitSubSystem(Uint32 flags)
 #endif
     }
 
+    /* Initialize the camera subsystem */
+    if (flags & SDL_INIT_CAMERA) {
+#ifndef SDL_CAMERA_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_CAMERA)) {
+            /* camera implies events */
+            if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) {
+                goto quit_and_error;
+            }
+
+            SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA);
+            if (SDL_CameraInit(NULL) < 0) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA);
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA);
+        }
+        flags_initialized |= SDL_INIT_CAMERA;
+#else
+        SDL_SetError("SDL not built with camera support");
+        goto quit_and_error;
+#endif
+    }
+
     (void)flags_initialized; /* make static analysis happy, since this only gets used in error cases. */
 
     return 0;
@@ -382,6 +407,18 @@ int SDL_Init(Uint32 flags)
 void SDL_QuitSubSystem(Uint32 flags)
 {
     /* Shut down requested initialized subsystems */
+
+#ifndef SDL_CAMERA_DISABLED
+    if (flags & SDL_INIT_CAMERA) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_CAMERA)) {
+            SDL_QuitCamera();
+            /* camera implies events */
+            SDL_QuitSubSystem(SDL_INIT_EVENTS);
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA);
+    }
+#endif
+
 #ifndef SDL_SENSOR_DISABLED
     if (flags & SDL_INIT_SENSOR) {
         if (SDL_ShouldQuitSubsystem(SDL_INIT_SENSOR)) {

+ 185 - 54
src/camera/SDL_camera.c

@@ -25,7 +25,25 @@
 #include "../video/SDL_pixels_c.h"
 #include "../thread/SDL_systhread.h"
 
-#define DEBUG_CAMERA 1
+// Available camera drivers
+static const CameraBootStrap *const bootstrap[] = {
+#ifdef SDL_CAMERA_DRIVER_V4L2
+    &V4L2_bootstrap,
+#endif
+#ifdef SDL_CAMERA_DRIVER_COREMEDIA
+    &COREMEDIA_bootstrap,
+#endif
+#ifdef SDL_CAMERA_DRIVER_ANDROID
+    &ANDROIDCAMERA_bootstrap,
+#endif
+#ifdef SDL_CAMERA_DRIVER_DUMMY
+    &DUMMYCAMERA_bootstrap,
+#endif
+    NULL
+};
+
+static SDL_CameraDriver camera_driver;
+
 
 // list node entries to share frames between SDL and user app
 // !!! FIXME: do we need this struct?
@@ -36,6 +54,25 @@ typedef struct entry_t
 
 static SDL_CameraDevice *open_devices[16];  // !!! FIXME: remove limit
 
+int SDL_GetNumCameraDrivers(void)
+{
+    return SDL_arraysize(bootstrap) - 1;
+}
+
+const char *SDL_GetCameraDriver(int index)
+{
+    if (index >= 0 && index < SDL_GetNumCameraDrivers()) {
+        return bootstrap[index]->name;
+    }
+    return NULL;
+}
+
+const char *SDL_GetCurrentCameraDriver(void)
+{
+    return camera_driver.name;
+}
+
+
 static void CloseCameraDevice(SDL_CameraDevice *device)
 {
     if (!device) {
@@ -69,46 +106,18 @@ static void CloseCameraDevice(SDL_CameraDevice *device)
             SDL_CameraFrame f = entry->frame;
             // Release frames not acquired, if any
             if (f.timestampNS) {
-                ReleaseFrame(device, &f);
+                camera_driver.impl.ReleaseFrame(device, &f);
             }
             SDL_free(entry);
         }
     }
 
-    CloseDevice(device);
+    camera_driver.impl.CloseDevice(device);
 
     SDL_free(device->dev_name);
     SDL_free(device);
 }
 
-// Tell if all devices are closed
-SDL_bool CheckAllDeviceClosed(void)
-{
-    const int n = SDL_arraysize(open_devices);
-    int all_closed = SDL_TRUE;
-    for (int i = 0; i < n; i++) {
-        if (open_devices[i]) {
-            all_closed = SDL_FALSE;
-            break;
-        }
-    }
-    return all_closed;
-}
-
-// Tell if at least one device is in playing state
-SDL_bool CheckDevicePlaying(void)
-{
-    const int n = SDL_arraysize(open_devices);
-    for (int i = 0; i < n; i++) {
-        if (open_devices[i]) {
-            if (SDL_GetCameraStatus(open_devices[i]) == SDL_CAMERA_PLAYING) {
-                return SDL_TRUE;
-            }
-        }
-    }
-    return SDL_FALSE;
-}
-
 void SDL_CloseCamera(SDL_CameraDevice *device)
 {
     if (!device) {
@@ -128,7 +137,7 @@ int SDL_StartCamera(SDL_CameraDevice *device)
         return SDL_SetError("invalid state");
     }
 
-    const int result = StartCamera(device);
+    const int result = camera_driver.impl.StartCamera(device);
     if (result < 0) {
         return result;
     }
@@ -147,7 +156,7 @@ int SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec)
     }
 
     SDL_zerop(spec);
-    return GetDeviceSpec(device, spec);
+    return camera_driver.impl.GetDeviceSpec(device, spec);
 }
 
 int SDL_StopCamera(SDL_CameraDevice *device)
@@ -162,7 +171,7 @@ int SDL_StopCamera(SDL_CameraDevice *device)
     SDL_AtomicSet(&device->shutdown, 1);
 
     SDL_LockMutex(device->acquiring_lock);
-    const int retval = StopCamera(device);
+    const int retval = camera_driver.impl.StopCamera(device);
     SDL_UnlockMutex(device->acquiring_lock);
 
     return (retval < 0) ? -1 : 0;
@@ -254,14 +263,13 @@ const char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id)
         return NULL;
     }
 
-    if (GetCameraDeviceName(instance_id, buf, sizeof (buf)) < 0) {
+    if (camera_driver.impl.GetDeviceName(instance_id, buf, sizeof (buf)) < 0) {
         buf[0] = 0;
     }
 
     return buf;
 }
 
-
 SDL_CameraDeviceID *SDL_GetCameraDevices(int *count)
 {
     int dummycount = 0;
@@ -270,7 +278,7 @@ SDL_CameraDeviceID *SDL_GetCameraDevices(int *count)
     }
 
     int num = 0;
-    SDL_CameraDeviceID *retval = GetCameraDevices(&num);
+    SDL_CameraDeviceID *retval = camera_driver.impl.GetDevices(&num);
     if (retval) {
         *count = num;
         return retval;
@@ -279,7 +287,6 @@ SDL_CameraDeviceID *SDL_GetCameraDevices(int *count)
     // return list of 0 ID, null terminated
     retval = (SDL_CameraDeviceID *)SDL_calloc(1, sizeof(*retval));
     if (retval == NULL) {
-        SDL_OutOfMemory();
         *count = 0;
         return NULL;
     }
@@ -331,7 +338,7 @@ static int SDLCALL SDL_CameraThread(void *devicep)
         SDL_zero(f);
 
         SDL_LockMutex(device->acquiring_lock);
-        ret = AcquireFrame(device, &f);
+        ret = camera_driver.impl.AcquireFrame(device, &f);
         SDL_UnlockMutex(device->acquiring_lock);
 
         if (ret == 0) {
@@ -376,7 +383,6 @@ error_mem:
     SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError());
 #endif
     SDL_AtomicSet(&device->shutdown, 1);
-    SDL_OutOfMemory();  // !!! FIXME: this error isn't accessible since the thread is about to terminate
     return 0;
 }
 
@@ -436,7 +442,6 @@ SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id)
 
     device = (SDL_CameraDevice *) SDL_calloc(1, sizeof (SDL_CameraDevice));
     if (device == NULL) {
-        SDL_OutOfMemory();
         goto error;
     }
     device->dev_name = SDL_strdup(device_name);
@@ -456,7 +461,7 @@ SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id)
         goto error;
     }
 
-    if (OpenDevice(device) < 0) {
+    if (camera_driver.impl.OpenDevice(device) < 0) {
         goto error;
     }
 
@@ -515,7 +520,7 @@ int SDL_SetCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, S
 
     device->spec = *obtained;
 
-    result = InitDevice(device);
+    result = camera_driver.impl.InitDevice(device);
     if (result < 0) {
         return result;
     }
@@ -541,7 +546,7 @@ int SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
         int ret;
 
         // Wait for a frame
-        while ((ret = AcquireFrame(device, frame)) == 0) {
+        while ((ret = camera_driver.impl.AcquireFrame(device, frame)) == 0) {
             if (frame->num_planes) {
                 return 0;
             }
@@ -576,7 +581,7 @@ int SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
         return SDL_InvalidParamError("device");
     } else if (frame == NULL) {
         return SDL_InvalidParamError("frame");
-    } else if (ReleaseFrame(device, frame) < 0) {
+    } else if (camera_driver.impl.ReleaseFrame(device, frame) < 0) {
         return -1;
     }
 
@@ -589,7 +594,7 @@ int SDL_GetNumCameraFormats(SDL_CameraDevice *device)
     if (!device) {
         return SDL_InvalidParamError("device");
     }
-    return GetNumFormats(device);
+    return camera_driver.impl.GetNumFormats(device);
 }
 
 int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format)
@@ -600,7 +605,7 @@ int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format)
         return SDL_InvalidParamError("format");
     }
     *format = 0;
-    return GetFormat(device, index, format);
+    return camera_driver.impl.GetFormat(device, index, format);
 }
 
 int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format)
@@ -608,7 +613,7 @@ int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format)
     if (!device) {
         return SDL_InvalidParamError("device");
     }
-    return GetNumFrameSizes(device, format);
+    return camera_driver.impl.GetNumFrameSizes(device, format);
 }
 
 int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height)
@@ -621,7 +626,7 @@ int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, i
         return SDL_InvalidParamError("height");
     }
     *width = *height = 0;
-    return GetFrameSize(device, format, index, width, height);
+    return camera_driver.impl.GetFrameSize(device, format, index, width, height);
 }
 
 SDL_CameraDevice *SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
@@ -653,15 +658,35 @@ SDL_CameraStatus SDL_GetCameraStatus(SDL_CameraDevice *device)
     return SDL_CAMERA_INIT;
 }
 
-int SDL_CameraInit(void)
+static void CompleteCameraEntryPoints(void)
 {
-    SDL_zeroa(open_devices);
-    SDL_SYS_CameraInit();
-    return 0;
+    // this doesn't currently fill in stub implementations, it just asserts the backend filled them all in.
+    #define FILL_STUB(x) SDL_assert(camera_driver.impl.x != NULL)
+    FILL_STUB(DetectDevices);
+    FILL_STUB(OpenDevice);
+    FILL_STUB(CloseDevice);
+    FILL_STUB(InitDevice);
+    FILL_STUB(GetDeviceSpec);
+    FILL_STUB(StartCamera);
+    FILL_STUB(StopCamera);
+    FILL_STUB(AcquireFrame);
+    FILL_STUB(ReleaseFrame);
+    FILL_STUB(GetNumFormats);
+    FILL_STUB(GetFormat);
+    FILL_STUB(GetNumFrameSizes);
+    FILL_STUB(GetFrameSize);
+    FILL_STUB(GetDeviceName);
+    FILL_STUB(GetDevices);
+    FILL_STUB(Deinitialize);
+    #undef FILL_STUB
 }
 
 void SDL_QuitCamera(void)
 {
+    if (!camera_driver.name) {  // not initialized?!
+        return;
+    }
+
     const int n = SDL_arraysize(open_devices);
     for (int i = 0; i < n; i++) {
         CloseCameraDevice(open_devices[i]);
@@ -669,6 +694,112 @@ void SDL_QuitCamera(void)
 
     SDL_zeroa(open_devices);
 
-    SDL_SYS_CameraQuit();
+#if 0 // !!! FIXME
+    SDL_PendingCameraDeviceEvent *pending_events = camera_driver.pending_events.next;
+    camera_driver.pending_events.next = NULL;
+
+    SDL_PendingCameraDeviceEvent *pending_next = NULL;
+    for (SDL_PendingCameraDeviceEvent *i = pending_events; i; i = pending_next) {
+        pending_next = i->next;
+        SDL_free(i);
+    }
+#endif
+
+    // Free the driver data
+    camera_driver.impl.Deinitialize();
+
+    SDL_zero(camera_driver);
+}
+
+// this is 90% the same code as the audio subsystem uses.
+int SDL_CameraInit(const char *driver_name)
+{
+    if (SDL_GetCurrentCameraDriver()) {
+        SDL_QuitCamera(); // shutdown driver if already running.
+    }
+
+    SDL_zeroa(open_devices);
+
+    // Select the proper camera driver
+    if (!driver_name) {
+        driver_name = SDL_GetHint(SDL_HINT_CAMERA_DRIVER);
+    }
+
+    SDL_bool initialized = SDL_FALSE;
+    SDL_bool tried_to_init = SDL_FALSE;
+
+    if (driver_name && (*driver_name != 0)) {
+        char *driver_name_copy = SDL_strdup(driver_name);
+        const char *driver_attempt = driver_name_copy;
+
+        if (!driver_name_copy) {
+            return -1;
+        }
+
+        while (driver_attempt && (*driver_attempt != 0) && !initialized) {
+            char *driver_attempt_end = SDL_strchr(driver_attempt, ',');
+            if (driver_attempt_end) {
+                *driver_attempt_end = '\0';
+            }
+
+            for (int i = 0; bootstrap[i]; i++) {
+                if (SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) {
+                    tried_to_init = SDL_TRUE;
+                    SDL_zero(camera_driver);
+                    #if 0  // !!! FIXME
+                    camera_driver.pending_events_tail = &camera_driver.pending_events;
+                    #endif
+                    if (bootstrap[i]->init(&camera_driver.impl)) {
+                        camera_driver.name = bootstrap[i]->name;
+                        camera_driver.desc = bootstrap[i]->desc;
+                        initialized = SDL_TRUE;
+                    }
+                    break;
+                }
+            }
+
+            driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
+        }
+
+        SDL_free(driver_name_copy);
+    } else {
+        for (int i = 0; !initialized && bootstrap[i]; i++) {
+            if (bootstrap[i]->demand_only) {
+                continue;
+            }
+
+            tried_to_init = SDL_TRUE;
+            SDL_zero(camera_driver);
+            #if 0  // !!! FIXME
+            camera_driver.pending_events_tail = &camera_driver.pending_events;
+            #endif
+            if (bootstrap[i]->init(&camera_driver.impl)) {
+                camera_driver.name = bootstrap[i]->name;
+                camera_driver.desc = bootstrap[i]->desc;
+                initialized = SDL_TRUE;
+            }
+        }
+    }
+
+    if (!initialized) {
+        // specific drivers will set the error message if they fail, but otherwise we do it here.
+        if (!tried_to_init) {
+            if (driver_name) {
+                SDL_SetError("Camera driver '%s' not available", driver_name);
+            } else {
+                SDL_SetError("No available camera driver");
+            }
+        }
+
+        SDL_zero(camera_driver);
+        return -1;  // No driver was available, so fail.
+    }
+
+    CompleteCameraEntryPoints();
+
+    // Make sure we have a list of devices available at startup...
+    camera_driver.impl.DetectDevices();
+
+    return 0;
 }
 

+ 1 - 1
src/camera/SDL_camera_c.h

@@ -24,7 +24,7 @@
 #define SDL_camera_c_h_
 
 // Initialize the camera subsystem
-int SDL_CameraInit(void);
+int SDL_CameraInit(const char *driver_name);
 
 // Shutdown the camera subsystem
 void SDL_QuitCamera(void);

+ 41 - 28
src/camera/SDL_syscamera.h

@@ -25,6 +25,8 @@
 
 #include "../SDL_list.h"
 
+#define DEBUG_CAMERA 1
+
 // The SDL camera driver
 typedef struct SDL_CameraDevice SDL_CameraDevice;
 
@@ -57,34 +59,45 @@ struct SDL_CameraDevice
     struct SDL_PrivateCameraData *hidden;
 };
 
-extern int SDL_SYS_CameraInit(void);
-extern int SDL_SYS_CameraQuit(void);
-
-// !!! FIXME: These names need to be made camera-specific.
-
-extern int OpenDevice(SDL_CameraDevice *_this);
-extern void CloseDevice(SDL_CameraDevice *_this);
-
-extern int InitDevice(SDL_CameraDevice *_this);
-
-extern int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec);
-
-extern int StartCamera(SDL_CameraDevice *_this);
-extern int StopCamera(SDL_CameraDevice *_this);
-
-extern int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame);
-extern int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame);
-
-extern int GetNumFormats(SDL_CameraDevice *_this);
-extern int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format);
-
-extern int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format);
-extern int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height);
-
-extern int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size);
-extern SDL_CameraDeviceID *GetCameraDevices(int *count);
+typedef struct SDL_CameraDriverImpl
+{
+    void (*DetectDevices)(void);
+    int (*OpenDevice)(SDL_CameraDevice *_this);
+    void (*CloseDevice)(SDL_CameraDevice *_this);
+    int (*InitDevice)(SDL_CameraDevice *_this);
+    int (*GetDeviceSpec)(SDL_CameraDevice *_this, SDL_CameraSpec *spec);
+    int (*StartCamera)(SDL_CameraDevice *_this);
+    int (*StopCamera)(SDL_CameraDevice *_this);
+    int (*AcquireFrame)(SDL_CameraDevice *_this, SDL_CameraFrame *frame);
+    int (*ReleaseFrame)(SDL_CameraDevice *_this, SDL_CameraFrame *frame);
+    int (*GetNumFormats)(SDL_CameraDevice *_this);
+    int (*GetFormat)(SDL_CameraDevice *_this, int index, Uint32 *format);
+    int (*GetNumFrameSizes)(SDL_CameraDevice *_this, Uint32 format);
+    int (*GetFrameSize)(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height);
+    int (*GetDeviceName)(SDL_CameraDeviceID instance_id, char *buf, int size);
+    SDL_CameraDeviceID *(*GetDevices)(int *count);
+    void (*Deinitialize)(void);
+} SDL_CameraDriverImpl;
+
+typedef struct SDL_CameraDriver
+{
+    const char *name;  // The name of this camera driver
+    const char *desc;  // The description of this camera driver
+    SDL_CameraDriverImpl impl; // the backend's interface
+} SDL_CameraDriver;
 
-extern SDL_bool CheckAllDeviceClosed(void);
-extern SDL_bool CheckDevicePlaying(void);
+typedef struct CameraBootStrap
+{
+    const char *name;
+    const char *desc;
+    SDL_bool (*init)(SDL_CameraDriverImpl *impl);
+    SDL_bool demand_only; // if SDL_TRUE: request explicitly, or it won't be available.
+} CameraBootStrap;
+
+// Not all of these are available in a given build. Use #ifdefs, etc.
+extern CameraBootStrap DUMMYCAMERA_bootstrap;
+extern CameraBootStrap V4L2_bootstrap;
+extern CameraBootStrap COREMEDIA_bootstrap;
+extern CameraBootStrap ANDROIDCAMERA_bootstrap;
 
 #endif // SDL_syscamera_h_

+ 63 - 33
src/camera/android/SDL_camera_android.c

@@ -25,9 +25,9 @@
 #include "../../video/SDL_pixels_c.h"
 #include "../../thread/SDL_systhread.h"
 
-#define DEBUG_CAMERA 1
+#if defined(SDL_CAMERA_DRIVER_ANDROID)
 
-#if defined(SDL_CAMERA_ANDROID) && __ANDROID_API__ >= 24
+#if __ANDROID_API__ >= 24
 
 /*
  * APP_PLATFORM=android-24
@@ -62,7 +62,7 @@
 static ACameraManager *cameraMgr = NULL;
 static ACameraIdList *cameraIdList = NULL;
 
-static void create_cameraMgr(void)
+static int CreateCameraManager(void)
 {
     if (cameraMgr == NULL) {
         #if 0  // !!! FIXME: this is getting replaced in a different branch.
@@ -78,9 +78,13 @@ static void create_cameraMgr(void)
             SDL_Log("Create ACameraManager");
         }
     }
+
+    cameraMgr = ACameraManager_create();
+
+    return cameraMgr ? 0 : SDL_SetError("Error creating ACameraManager");
 }
 
-static void delete_cameraMgr(void)
+static void DestroyCameraManager(void)
 {
     if (cameraIdList) {
         ACameraManager_deleteCameraIdList(cameraIdList);
@@ -215,7 +219,7 @@ static void onActive(void* context, ACameraCaptureSession *session)
     #endif
 }
 
-int OpenDevice(SDL_CameraDevice *_this)
+static int ANDROIDCAMERA_OpenDevice(SDL_CameraDevice *_this)
 {
     /* Cannot open a second camera, while the first one is opened.
      * If you want to play several camera, they must all be opened first, then played.
@@ -231,10 +235,10 @@ int OpenDevice(SDL_CameraDevice *_this)
 
     _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
     if (_this->hidden == NULL) {
-        return SDL_OutOfMemory();
+        return -1;
     }
 
-    create_cameraMgr();
+    CreateCameraManager();
 
     _this->hidden->dev_callbacks.context = (void *) _this;
     _this->hidden->dev_callbacks.onDisconnected = onDisconnected;
@@ -248,7 +252,7 @@ int OpenDevice(SDL_CameraDevice *_this)
     return 0;
 }
 
-void CloseDevice(SDL_CameraDevice *_this)
+static void ANDROIDCAMERA_CloseDevice(SDL_CameraDevice *_this)
 {
     if (_this && _this->hidden) {
         if (_this->hidden->session) {
@@ -271,14 +275,9 @@ void CloseDevice(SDL_CameraDevice *_this)
 
         _this->hidden = NULL;
     }
-
-    // !!! FIXME: just refcount this?
-    if (CheckAllDeviceClosed()) {
-        delete_cameraMgr();
-    }
 }
 
-int InitDevice(SDL_CameraDevice *_this)
+static int ANDROIDCAMERA_InitDevice(SDL_CameraDevice *_this)
 {
     size_t size, pitch;
     SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE);
@@ -286,7 +285,7 @@ int InitDevice(SDL_CameraDevice *_this)
     return 0;
 }
 
-int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
+static int ANDROIDCAMERA_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
 {
     // !!! FIXME: catch NULLs at higher level
     if (spec) {
@@ -296,7 +295,7 @@ int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
     return -1;
 }
 
-int StartCamera(SDL_CameraDevice *_this)
+static int ANDROIDCAMERA_StartCamera(SDL_CameraDevice *_this)
 {
     // !!! FIXME: maybe log the error code in SDL_SetError
     camera_status_t res;
@@ -377,14 +376,14 @@ error:
     return -1;
 }
 
-int StopCamera(SDL_CameraDevice *_this)
+static int ANDROIDCAMERA_StopCamera(SDL_CameraDevice *_this)
 {
     ACameraCaptureSession_close(_this->hidden->session);
     _this->hidden->session = NULL;
     return 0;
 }
 
-int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
+static int ANDROIDCAMERA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 {
     media_status_t res;
     AImage *image;
@@ -435,7 +434,7 @@ int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
     return 0;
 }
 
-int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
+static int ANDROIDCAMERA_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 {
     if (frame->internal){
         AImage_delete((AImage *)frame->internal);
@@ -443,7 +442,7 @@ int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
     return 0;
 }
 
-int GetNumFormats(SDL_CameraDevice *_this)
+static int ANDROIDCAMERA_GetNumFormats(SDL_CameraDevice *_this)
 {
     camera_status_t res;
     SDL_bool unknown = SDL_FALSE;
@@ -505,7 +504,7 @@ int GetNumFormats(SDL_CameraDevice *_this)
     return _this->hidden->num_formats;
 }
 
-int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
+static int ANDROIDCAMERA_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
 {
     int i2 = 0;
 
@@ -533,7 +532,7 @@ int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
     return 0;
 }
 
-int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
+static int ANDROIDCAMERA_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
 {
     // !!! FIXME: call SDL_SetError()?
     if (_this->hidden->num_formats == 0) {
@@ -559,7 +558,7 @@ int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
     return -1;
 }
 
-int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
+static int ANDROIDCAMERA_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
 {
     // !!! FIXME: call SDL_SetError()?
     camera_status_t res;
@@ -608,10 +607,10 @@ int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width,
     return -1;
 }
 
-static int GetNumDevices(void)
+static int ANDROIDCAMERA_GetNumDevices(void)
 {
     camera_status_t res;
-    create_cameraMgr();
+    CreateCameraManager();
 
     if (cameraIdList) {
         ACameraManager_deleteCameraIdList(cameraIdList);
@@ -628,11 +627,11 @@ static int GetNumDevices(void)
     return -1;
 }
 
-int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
+static int ANDROIDCAMERA_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
 {
     // !!! FIXME: call SDL_SetError()?
     int index = instance_id - 1;
-    create_cameraMgr();
+    CreateCameraManager();
 
     if (cameraIdList == NULL) {
         GetNumDevices();
@@ -648,14 +647,13 @@ int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
     return -1;
 }
 
-SDL_CameraDeviceID *GetCameraDevices(int *count)
+static SDL_CameraDeviceID *ANDROIDCAMERA_GetDevices(int *count)
 {
     // hard-coded list of ID
     const int num = GetNumDevices();
     SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret));
 
     if (retval == NULL) {
-        SDL_OutOfMemory();
         *count = 0;
         return NULL;
     }
@@ -668,15 +666,47 @@ SDL_CameraDeviceID *GetCameraDevices(int *count)
     return retval;
 }
 
-int SDL_SYS_CameraInit(void)
+static void ANDROIDCAMERA_Deinitialize(void)
 {
-    return 0;
+    DestroyCameraManager();
 }
 
-int SDL_SYS_CameraQuit(void)
+#endif  // __ANDROID_API__ >= 24
+
+
+static SDL_bool ANDROIDCAMERA_Init(SDL_CameraDriverImpl *impl)
 {
-    return 0;
+#if __ANDROID_API__ < 24
+    return SDL_FALSE;
+#else
+    if (CreateCameraManager() < 0) {
+        return SDL_FALSE;
+    }
+
+    impl->DetectDevices = ANDROIDCAMERA_DetectDevices;
+    impl->OpenDevice = ANDROIDCAMERA_OpenDevice;
+    impl->CloseDevice = ANDROIDCAMERA_CloseDevice;
+    impl->InitDevice = ANDROIDCAMERA_InitDevice;
+    impl->GetDeviceSpec = ANDROIDCAMERA_GetDeviceSpec;
+    impl->StartCamera = ANDROIDCAMERA_StartCamera;
+    impl->StopCamera = ANDROIDCAMERA_StopCamera;
+    impl->AcquireFrame = ANDROIDCAMERA_AcquireFrame;
+    impl->ReleaseFrame = ANDROIDCAMERA_ReleaseFrame;
+    impl->GetNumFormats = ANDROIDCAMERA_GetNumFormats;
+    impl->GetFormat = ANDROIDCAMERA_GetFormat;
+    impl->GetNumFrameSizes = ANDROIDCAMERA_GetNumFrameSizes;
+    impl->GetFrameSize = ANDROIDCAMERA_GetFrameSize;
+    impl->GetDeviceName = ANDROIDCAMERA_GetDeviceName;
+    impl->GetDevices = ANDROIDCAMERA_GetDevices;
+    impl->Deinitialize = ANDROIDCAMERA_Deinitialize;
+
+    return SDL_TRUE;
+#endif
 }
 
+CameraBootStrap ANDROIDCAMERA_bootstrap = {
+    "android", "SDL Android camera driver", ANDROIDCAMERA_Init, SDL_FALSE
+};
+
 #endif
 

+ 52 - 78
src/camera/apple/SDL_camera_apple.m → src/camera/coremedia/SDL_camera_coremedia.m

@@ -20,7 +20,7 @@
 */
 #include "SDL_internal.h"
 
-#ifdef SDL_CAMERA_APPLE
+#ifdef SDL_CAMERA_DRIVER_COREMEDIA
 
 #include "../SDL_syscamera.h"
 #include "../SDL_camera_c.h"
@@ -35,59 +35,7 @@
 #undef HAVE_COREMEDIA
 #endif
 
-// !!! FIXME: use the dummy driver
-// !!! FIXME: actually, move everything over to backend callbacks instead.
-#ifndef HAVE_COREMEDIA
-int InitDevice(SDL_CameraDevice *_this) {
-    return -1;
-}
-int OpenDevice(SDL_CameraDevice *_this) {
-    return -1;
-}
-int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) {
-    return -1;
-}
-void CloseDevice(SDL_CameraDevice *_this) {
-}
-int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) {
-    return -1;
-}
-int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) {
-    return -1;
-}
-int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) {
-    return -1;
-}
-int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) {
-    return -1;
-}
-SDL_CameraDeviceID *GetCameraDevices(int *count) {
-    return NULL;
-}
-int GetNumFormats(SDL_CameraDevice *_this) {
-    return 0;
-}
-int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) {
-    return 0;
-}
-int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) {
-    return 0;
-}
-int StartCapture(SDL_CameraDevice *_this) {
-    return 0;
-}
-int StopCapture(SDL_CameraDevice *_this) {
-    return 0;
-}
-int SDL_SYS_CameraInit(void) {
-    return 0;
-}
-int SDL_SYS_CameraQuit(void) {
-    return 0;
-}
-
-
-#else
+#ifdef HAVE_COREMEDIA
 
 #import <AVFoundation/AVFoundation.h>
 #import <CoreMedia/CoreMedia.h>
@@ -234,16 +182,16 @@ static NSString *sdlformat_to_nsfourcc(Uint32 fmt)
         }
 @end
 
-int OpenDevice(SDL_CameraDevice *_this)
+static int COREMEDIA_OpenDevice(SDL_CameraDevice *_this)
 {
     _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
     if (_this->hidden == NULL) {
-        return SDL_OutOfMemory();
+        return -1;
     }
     return 0;
 }
 
-void CloseDevice(SDL_CameraDevice *_this)
+static void COREMEDIA_CloseDevice(SDL_CameraDevice *_this)
 {
     if (!_this) {
         return;
@@ -271,7 +219,7 @@ void CloseDevice(SDL_CameraDevice *_this)
     }
 }
 
-int InitDevice(SDL_CameraDevice *_this)
+static int COREMEDIA_InitDevice(SDL_CameraDevice *_this)
 {
     // !!! FIXME: autorelease pool?
     NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format);
@@ -381,7 +329,7 @@ int InitDevice(SDL_CameraDevice *_this)
     return 0;
 }
 
-int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
+static int COREMEDIA_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
 {
     // !!! FIXME: make sure higher level checks spec != NULL
     if (spec) {
@@ -391,19 +339,19 @@ int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
     return -1;
 }
 
-int StartCamera(SDL_CameraDevice *_this)
+static int COREMEDIA_StartCamera(SDL_CameraDevice *_this)
 {
     [_this->hidden->session startRunning];
     return 0;
 }
 
-int StopCamera(SDL_CameraDevice *_this)
+static int COREMEDIA_StopCamera(SDL_CameraDevice *_this)
 {
     [_this->hidden->session stopRunning];
     return 0;
 }
 
-int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
+static int COREMEDIA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 {
     if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) {
         CMSampleBufferRef sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue);
@@ -444,7 +392,7 @@ int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
     return 0;
 }
 
-int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
+static int COREMEDIA_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 {
     if (frame->internal) {
         CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal;
@@ -456,7 +404,7 @@ int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
     return 0;
 }
 
-int GetNumFormats(SDL_CameraDevice *_this)
+static int COREMEDIA_GetNumFormats(SDL_CameraDevice *_this)
 {
     AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
     if (device) {
@@ -476,7 +424,7 @@ int GetNumFormats(SDL_CameraDevice *_this)
     return 0;
 }
 
-int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
+static int COREMEDIA_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
 {
     AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
     if (device) {
@@ -503,7 +451,7 @@ int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
     return -1;
 }
 
-int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
+static int COREMEDIA_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
 {
     AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
     if (device) {
@@ -525,8 +473,7 @@ int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
     return 0;
 }
 
-int
-GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
+static int COREMEDIA_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
 {
     AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
     if (device) {
@@ -553,7 +500,7 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
     return -1;
 }
 
-int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
+static int COREMEDIA_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
 {
     int index = instance_id - 1;
     NSArray<AVCaptureDevice *> *devices = DiscoverCameraDevices();
@@ -567,20 +514,19 @@ int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
     return -1;
 }
 
-static int GetNumDevices(void)
+static int GetNumCameraDevices(void)
 {
     NSArray<AVCaptureDevice *> *devices = DiscoverCameraDevices();
     return [devices count];
 }
 
-SDL_CameraDeviceID *GetCameraDevices(int *count)
+static SDL_CameraDeviceID *COREMEDIA_GetDevices(int *count)
 {
     // hard-coded list of ID
-    const int num = GetNumDevices();
+    const int num = GetNumCameraDevices();
     SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_calloc((num + 1), sizeof(*ret));
 
     if (retval == NULL) {
-        SDL_OutOfMemory();
         *count = 0;
         return NULL;
     }
@@ -593,17 +539,45 @@ SDL_CameraDeviceID *GetCameraDevices(int *count)
     return ret;
 }
 
-int SDL_SYS_CameraInit(void)
+static void COREMEDIA_DetectDevices(void)
 {
-    return 0;
 }
 
-int SDL_SYS_CameraQuit(void)
+static void COREMEDIA_Deinitialize(void)
 {
-    return 0;
 }
 
+static SDL_bool COREMEDIA_Init(SDL_CameraDriverImpl *impl)
+{
+#ifndef HAVE_COREMEDIA
+    return SDL_FALSE;
+#else
+    impl->DetectDevices = COREMEDIA_DetectDevices;
+    impl->OpenDevice = COREMEDIA_OpenDevice;
+    impl->CloseDevice = COREMEDIA_CloseDevice;
+    impl->InitDevice = COREMEDIA_InitDevice;
+    impl->GetDeviceSpec = COREMEDIA_GetDeviceSpec;
+    impl->StartCamera = COREMEDIA_StartCamera;
+    impl->StopCamera = COREMEDIA_StopCamera;
+    impl->AcquireFrame = COREMEDIA_AcquireFrame;
+    impl->ReleaseFrame = COREMEDIA_ReleaseFrame;
+    impl->GetNumFormats = COREMEDIA_GetNumFormats;
+    impl->GetFormat = COREMEDIA_GetFormat;
+    impl->GetNumFrameSizes = COREMEDIA_GetNumFrameSizes;
+    impl->GetFrameSize = COREMEDIA_GetFrameSize;
+    impl->GetDeviceName = COREMEDIA_GetDeviceName;
+    impl->GetDevices = COREMEDIA_GetDevices;
+    impl->Deinitialize = COREMEDIA_Deinitialize;
+
+    return SDL_TRUE;
+#endif
+}
+
+CameraBootStrap COREMEDIA_bootstrap = {
+    "coremedia", "SDL Apple CoreMedia camera driver", COREMEDIA_Init, SDL_FALSE
+};
+
 #endif // HAVE_COREMEDIA
 
-#endif // SDL_CAMERA_APPLE
+#endif // SDL_CAMERA_COREMEDIA
 

+ 107 - 108
src/camera/v4l2/SDL_camera_v4l2.c

@@ -20,7 +20,7 @@
 */
 #include "SDL_internal.h"
 
-#ifdef SDL_CAMERA_V4L2
+#ifdef SDL_CAMERA_DRIVER_V4L2
 
 #include "../SDL_syscamera.h"
 #include "../SDL_camera_c.h"
@@ -30,8 +30,6 @@
 #include "../../core/linux/SDL_udev.h"
 #include <limits.h>      // INT_MAX
 
-#define DEBUG_CAMERA 1
-
 #define MAX_CAMERA_DEVICES 128 // It's doubtful someone has more than that
 
 static int MaybeAddDevice(const char *path);
@@ -206,7 +204,7 @@ static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 }
 
 
-int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
+static int V4L2_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 {
     struct v4l2_buffer buf;
     const int fd = _this->hidden->fd;
@@ -259,8 +257,7 @@ int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
     return 0;
 }
 
-
-int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
+static int V4L2_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 {
     fd_set fds;
     struct timeval tv;
@@ -310,7 +307,7 @@ int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
 }
 
 
-int StopCamera(SDL_CameraDevice *_this)
+static int V4L2_StopCamera(SDL_CameraDevice *_this)
 {
     enum v4l2_buf_type type;
     const int fd = _this->hidden->fd;
@@ -428,7 +425,7 @@ static int PreEnqueueBuffers(SDL_CameraDevice *_this)
     return 0;
 }
 
-int StartCamera(SDL_CameraDevice *_this)
+static int V4L2_StartCamera(SDL_CameraDevice *_this)
 {
     enum v4l2_buf_type type;
 
@@ -476,15 +473,10 @@ static int AllocBufferRead(SDL_CameraDevice *_this, size_t buffer_size)
 {
     _this->hidden->buffers[0].length = buffer_size;
     _this->hidden->buffers[0].start = SDL_calloc(1, buffer_size);
-
-    if (!_this->hidden->buffers[0].start) {
-        return SDL_OutOfMemory();
-    }
-    return 0;
+    return _this->hidden->buffers[0].start ? 0 : -1;
 }
 
-static int
-AllocBufferMmap(SDL_CameraDevice *_this)
+static int AllocBufferMmap(SDL_CameraDevice *_this)
 {
     int fd = _this->hidden->fd;
     int i;
@@ -516,8 +508,7 @@ AllocBufferMmap(SDL_CameraDevice *_this)
     return 0;
 }
 
-static int
-AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size)
+static int AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size)
 {
     int i;
     for (i = 0; i < _this->hidden->nb_buffers; ++i) {
@@ -525,41 +516,38 @@ AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size)
         _this->hidden->buffers[i].start = SDL_calloc(1, buffer_size);
 
         if (!_this->hidden->buffers[i].start) {
-            return SDL_OutOfMemory();
+            return -1;
         }
     }
     return 0;
 }
 
-static Uint32
-format_v4l2_2_sdl(Uint32 fmt)
+static Uint32 format_v4l2_to_sdl(Uint32 fmt)
 {
     switch (fmt) {
-#define CASE(x, y)  case x: return y
+        #define CASE(x, y)  case x: return y
         CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2);
         CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN);
-#undef CASE
+        #undef CASE
         default:
             SDL_Log("Unknown format V4L2_PIX_FORMAT '%d'", fmt);
             return SDL_PIXELFORMAT_UNKNOWN;
     }
 }
 
-static Uint32
-format_sdl_2_v4l2(Uint32 fmt)
+static Uint32 format_sdl_to_v4l2(Uint32 fmt)
 {
     switch (fmt) {
-#define CASE(y, x)  case x: return y
+        #define CASE(y, x)  case x: return y
         CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2);
         CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN);
-#undef CASE
+        #undef CASE
         default:
             return 0;
     }
 }
 
-int
-GetNumFormats(SDL_CameraDevice *_this)
+static int V4L2_GetNumFormats(SDL_CameraDevice *_this)
 {
     int fd = _this->hidden->fd;
     int i = 0;
@@ -574,8 +562,7 @@ GetNumFormats(SDL_CameraDevice *_this)
     return i;
 }
 
-int
-GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
+static int V4L2_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
 {
     int fd = _this->hidden->fd;
     struct v4l2_fmtdesc fmtdesc;
@@ -584,7 +571,7 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
     fmtdesc.index = index;
     fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     if (ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0) {
-        *format = format_v4l2_2_sdl(fmtdesc.pixelformat);
+        *format = format_v4l2_to_sdl(fmtdesc.pixelformat);
 
 #if DEBUG_CAMERA
         if (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED) {
@@ -600,8 +587,7 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
     return -1;
 }
 
-int
-GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
+static int V4L2_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
 {
     int fd = _this->hidden->fd;
     int i = 0;
@@ -609,7 +595,7 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
 
     SDL_zero(frmsizeenum);
     frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    frmsizeenum.pixel_format = format_sdl_2_v4l2(format);
+    frmsizeenum.pixel_format = format_sdl_to_v4l2(format);
     while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) {
         frmsizeenum.index++;
         if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
@@ -624,8 +610,7 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
     return i;
 }
 
-int
-GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
+static int V4L2_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
 {
     int fd = _this->hidden->fd;
     struct v4l2_frmsizeenum frmsizeenum;
@@ -633,7 +618,7 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
 
     SDL_zero(frmsizeenum);
     frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    frmsizeenum.pixel_format = format_sdl_2_v4l2(format);
+    frmsizeenum.pixel_format = format_sdl_to_v4l2(format);
     while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) {
         frmsizeenum.index++;
 
@@ -663,12 +648,8 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
 
     return -1;
 }
-
-
-
-#if DEBUG_VIDEO_CAPTURE_CAPTURE
-static void
-dbg_v4l2_pixelformat(const char *str, int f) {
+static void dbg_v4l2_pixelformat(const char *str, int f)
+{
     SDL_Log("%s  V4L2_format=%d  %c%c%c%c", str, f,
                 (f >> 0) & 0xff,
                 (f >> 8) & 0xff,
@@ -677,8 +658,7 @@ dbg_v4l2_pixelformat(const char *str, int f) {
 }
 #endif
 
-int
-GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
+static int V4L2_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
 {
     struct v4l2_format fmt;
     int fd = _this->hidden->fd;
@@ -705,13 +685,12 @@ GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
     //spec->width = fmt.fmt.pix.width;
     //spec->height = fmt.fmt.pix.height;
     _this->hidden->driver_pitch = fmt.fmt.pix.bytesperline;
-    //spec->format = format_v4l2_2_sdl(fmt.fmt.pix.pixelformat);
+    //spec->format = format_v4l2_to_sdl(fmt.fmt.pix.pixelformat);
 
     return 0;
 }
 
-int
-InitDevice(SDL_CameraDevice *_this)
+static int V4L2_InitDevice(SDL_CameraDevice *_this)
 {
     struct v4l2_cropcap cropcap;
     struct v4l2_crop crop;
@@ -753,7 +732,7 @@ InitDevice(SDL_CameraDevice *_this)
         fmt.fmt.pix.height      = _this->spec.height;
 
 
-        fmt.fmt.pix.pixelformat = format_sdl_2_v4l2(_this->spec.format);
+        fmt.fmt.pix.pixelformat = format_sdl_to_v4l2(_this->spec.format);
         //    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
         fmt.fmt.pix.field       = V4L2_FIELD_ANY;
 
@@ -767,7 +746,7 @@ InitDevice(SDL_CameraDevice *_this)
         }
     }
 
-    GetDeviceSpec(_this, &_this->spec);
+    V4L2_GetDeviceSpec(_this, &_this->spec);
 
     if (PreEnqueueBuffers(_this) < 0) {
         return -1;
@@ -776,7 +755,7 @@ InitDevice(SDL_CameraDevice *_this)
     {
         _this->hidden->buffers = SDL_calloc(_this->hidden->nb_buffers, sizeof(*_this->hidden->buffers));
         if (!_this->hidden->buffers) {
-            return SDL_OutOfMemory();
+            return -1;
         }
     }
 
@@ -802,7 +781,7 @@ InitDevice(SDL_CameraDevice *_this)
     return (retval < 0) ? -1 : 0;
 }
 
-void CloseDevice(SDL_CameraDevice *_this)
+static void V4L2_CloseDevice(SDL_CameraDevice *_this)
 {
     if (!_this) {
         return;
@@ -846,8 +825,7 @@ void CloseDevice(SDL_CameraDevice *_this)
     }
 }
 
-
-int OpenDevice(SDL_CameraDevice *_this)
+static int V4L2_OpenDevice(SDL_CameraDevice *_this)
 {
     struct stat st;
     struct v4l2_capability cap;
@@ -856,7 +834,6 @@ int OpenDevice(SDL_CameraDevice *_this)
 
     _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
     if (_this->hidden == NULL) {
-        SDL_OutOfMemory();
         return -1;
     }
 
@@ -921,7 +898,7 @@ int OpenDevice(SDL_CameraDevice *_this)
     return 0;
 }
 
-int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
+static int V4L2_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
 {
     SDL_cameralist_item *item;
     for (item = SDL_cameralist; item; item = item->next) {
@@ -935,15 +912,13 @@ int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
     return -1;
 }
 
-
-SDL_CameraDeviceID *GetCameraDevices(int *count)
+static SDL_CameraDeviceID *V4L2_GetDevices(int *count)
 {
     // real list of ID
     const int num = num_cameras;
     SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*retval));
 
     if (retval == NULL) {
-        SDL_OutOfMemory();
         *count = 0;
         return NULL;
     }
@@ -959,55 +934,6 @@ SDL_CameraDeviceID *GetCameraDevices(int *count)
 }
 
 
-// Initializes the subsystem by finding available devices.
-int SDL_SYS_CameraInit(void)
-{
-    const char pattern[] = "/dev/video%d";
-    char path[PATH_MAX];
-
-    /*
-     * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have
-     * permission to some or all devices.
-     */
-    for (int i = 0; i < MAX_CAMERA_DEVICES; i++) {
-        (void)SDL_snprintf(path, PATH_MAX, pattern, i);
-        if (MaybeAddDevice(path) == -2) {
-            break;
-        }
-    }
-
-#ifdef SDL_USE_LIBUDEV
-    if (SDL_UDEV_Init() < 0) {
-        return SDL_SetError("Could not initialize UDEV");
-    } else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) {
-        SDL_UDEV_Quit();
-        return SDL_SetError("Could not setup Video Capture <-> udev callback");
-    }
-
-    // Force a scan to build the initial device list
-    SDL_UDEV_Scan();
-#endif // SDL_USE_LIBUDEV
-
-    return num_cameras;
-}
-
-int SDL_SYS_CameraQuit(void)
-{
-    for (SDL_cameralist_item *item = SDL_cameralist; item; ) {
-        SDL_cameralist_item *tmp = item->next;
-        SDL_free(item->fname);
-        SDL_free(item->bus_info);
-        SDL_free(item);
-        item = tmp;
-    }
-
-    num_cameras = 0;
-    SDL_cameralist = NULL;
-    SDL_cameralist_tail = NULL;
-
-    return SDL_FALSE;
-}
-
 #ifdef SDL_USE_LIBUDEV
 static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
 {
@@ -1150,5 +1076,78 @@ static int MaybeRemoveDevice(const char *path)
 }
 #endif // SDL_USE_LIBUDEV
 
+
+static void V4L2_Deinitialize(void)
+{
+    for (SDL_cameralist_item *item = SDL_cameralist; item; ) {
+        SDL_cameralist_item *tmp = item->next;
+        SDL_free(item->fname);
+        SDL_free(item->bus_info);
+        SDL_free(item);
+        item = tmp;
+    }
+
+    num_cameras = 0;
+    SDL_cameralist = NULL;
+    SDL_cameralist_tail = NULL;
+}
+
+static void V4L2_DetectDevices(void)
+{
+}
+
+static SDL_bool V4L2_Init(SDL_CameraDriverImpl *impl)
+{
+    // !!! FIXME: move to DetectDevices
+    const char pattern[] = "/dev/video%d";
+    char path[PATH_MAX];
+
+    /*
+     * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have
+     * permission to some or all devices.
+     */
+    for (int i = 0; i < MAX_CAMERA_DEVICES; i++) {
+        (void)SDL_snprintf(path, PATH_MAX, pattern, i);
+        if (MaybeAddDevice(path) == -2) {
+            break;
+        }
+    }
+
+#ifdef SDL_USE_LIBUDEV
+    if (SDL_UDEV_Init() < 0) {
+        return SDL_SetError("Could not initialize UDEV");
+    } else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) {
+        SDL_UDEV_Quit();
+        return SDL_SetError("Could not setup Video Capture <-> udev callback");
+    }
+
+    // Force a scan to build the initial device list
+    SDL_UDEV_Scan();
+#endif // SDL_USE_LIBUDEV
+
+    impl->DetectDevices = V4L2_DetectDevices;
+    impl->OpenDevice = V4L2_OpenDevice;
+    impl->CloseDevice = V4L2_CloseDevice;
+    impl->InitDevice = V4L2_InitDevice;
+    impl->GetDeviceSpec = V4L2_GetDeviceSpec;
+    impl->StartCamera = V4L2_StartCamera;
+    impl->StopCamera = V4L2_StopCamera;
+    impl->AcquireFrame = V4L2_AcquireFrame;
+    impl->ReleaseFrame = V4L2_ReleaseFrame;
+    impl->GetNumFormats = V4L2_GetNumFormats;
+    impl->GetFormat = V4L2_GetFormat;
+    impl->GetNumFrameSizes = V4L2_GetNumFrameSizes;
+    impl->GetFrameSize = V4L2_GetFrameSize;
+    impl->GetDeviceName = V4L2_GetDeviceName;
+    impl->GetDevices = V4L2_GetDevices;
+    impl->Deinitialize = V4L2_Deinitialize;
+
+    return SDL_TRUE;
+}
+
+CameraBootStrap V4L2_bootstrap = {
+    "v4l2", "SDL Video4Linux2 camera driver", V4L2_Init, SDL_FALSE
+};
+
 #endif // SDL_CAMERA_V4L2
 

+ 0 - 9
src/video/SDL_video.c

@@ -458,7 +458,6 @@ int SDL_VideoInit(const char *driver_name)
     SDL_bool init_keyboard = SDL_FALSE;
     SDL_bool init_mouse = SDL_FALSE;
     SDL_bool init_touch = SDL_FALSE;
-    SDL_bool init_camera = SDL_FALSE;
     int i = 0;
 
     /* Check to make sure we don't overwrite '_this' */
@@ -485,10 +484,6 @@ int SDL_VideoInit(const char *driver_name)
         goto pre_driver_error;
     }
     init_touch = SDL_TRUE;
-    if (SDL_CameraInit() < 0) {
-        goto pre_driver_error;
-    }
-    init_camera = SDL_TRUE;
 
     /* Select the proper video driver */
     video = NULL;
@@ -590,9 +585,6 @@ int SDL_VideoInit(const char *driver_name)
 
 pre_driver_error:
     SDL_assert(_this == NULL);
-    if (init_camera) {
-        SDL_QuitCamera();
-    }
     if (init_touch) {
         SDL_QuitTouch();
     }
@@ -3784,7 +3776,6 @@ void SDL_VideoQuit(void)
     SDL_ClearClipboardData();
 
     /* Halt event processing before doing anything else */
-    SDL_QuitCamera();
     SDL_QuitTouch();
     SDL_QuitMouse();
     SDL_QuitKeyboard();

+ 1 - 1
test/testcamera.c

@@ -213,7 +213,7 @@ int main(int argc, char **argv)
     SDL_Log("%s", usage);
 
     /* Load the SDL library */
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { /* FIXME: SDL_INIT_JOYSTICK needed for add/removing devices at runtime */
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
         return 1;
     }

+ 1 - 1
test/testcameraminimal.c

@@ -54,7 +54,7 @@ int main(int argc, char **argv)
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
 
     /* Load the SDL library */
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { /* FIXME: SDL_INIT_JOYSTICK needed for add/removing devices at runtime */
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
         return 1;
     }