| 1 | /* |
| 2 | Simple DirectMedia Layer |
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
| 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 | #ifndef SDL_syscamera_h_ |
| 24 | #define SDL_syscamera_h_ |
| 25 | |
| 26 | #include "../video/SDL_surface_c.h" |
| 27 | |
| 28 | #define DEBUG_CAMERA 0 |
| 29 | |
| 30 | /* Backends should call this as devices are added to the system (such as |
| 31 | a USB camera being plugged in), and should also be called for |
| 32 | for every device found during DetectDevices(). */ |
| 33 | extern SDL_Camera *SDL_AddCamera(const char *name, SDL_CameraPosition position, int num_specs, const SDL_CameraSpec *specs, void *handle); |
| 34 | |
| 35 | /* Backends should call this if an opened camera device is lost. |
| 36 | This can happen due to i/o errors, or a device being unplugged, etc. */ |
| 37 | extern void SDL_CameraDisconnected(SDL_Camera *device); |
| 38 | |
| 39 | // Find an SDL_Camera, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE. |
| 40 | extern SDL_Camera *SDL_FindPhysicalCameraByCallback(bool (*callback)(SDL_Camera *device, void *userdata), void *userdata); |
| 41 | |
| 42 | // Backends should call this when the user has approved/denied access to a camera. |
| 43 | extern void SDL_CameraPermissionOutcome(SDL_Camera *device, bool approved); |
| 44 | |
| 45 | // Backends can call this to get a standardized name for a thread to power a specific camera device. |
| 46 | extern char *SDL_GetCameraThreadName(SDL_Camera *device, char *buf, size_t buflen); |
| 47 | |
| 48 | // Backends can call these to change a device's refcount. |
| 49 | extern void RefPhysicalCamera(SDL_Camera *device); |
| 50 | extern void UnrefPhysicalCamera(SDL_Camera *device); |
| 51 | |
| 52 | // These functions are the heart of the camera threads. Backends can call them directly if they aren't using the SDL-provided thread. |
| 53 | extern void SDL_CameraThreadSetup(SDL_Camera *device); |
| 54 | extern bool SDL_CameraThreadIterate(SDL_Camera *device); |
| 55 | extern void SDL_CameraThreadShutdown(SDL_Camera *device); |
| 56 | |
| 57 | // Backends can call this if they have to finish initializing later, like Emscripten. Most backends should _not_ call this directly! |
| 58 | extern bool SDL_PrepareCameraSurfaces(SDL_Camera *device); |
| 59 | |
| 60 | |
| 61 | // common utility functionality to gather up camera specs. Not required! |
| 62 | typedef struct CameraFormatAddData |
| 63 | { |
| 64 | SDL_CameraSpec *specs; |
| 65 | int num_specs; |
| 66 | int allocated_specs; |
| 67 | } CameraFormatAddData; |
| 68 | |
| 69 | bool SDL_AddCameraFormat(CameraFormatAddData *data, SDL_PixelFormat format, SDL_Colorspace colorspace, int w, int h, int framerate_numerator, int framerate_denominator); |
| 70 | |
| 71 | typedef enum SDL_CameraFrameResult |
| 72 | { |
| 73 | SDL_CAMERA_FRAME_ERROR, |
| 74 | SDL_CAMERA_FRAME_SKIP, |
| 75 | SDL_CAMERA_FRAME_READY |
| 76 | } SDL_CameraFrameResult; |
| 77 | |
| 78 | typedef struct SurfaceList |
| 79 | { |
| 80 | SDL_Surface *surface; |
| 81 | Uint64 timestampNS; |
| 82 | struct SurfaceList *next; |
| 83 | } SurfaceList; |
| 84 | |
| 85 | // Define the SDL camera driver structure |
| 86 | struct SDL_Camera |
| 87 | { |
| 88 | // A mutex for locking |
| 89 | SDL_Mutex *lock; |
| 90 | |
| 91 | // Human-readable device name. |
| 92 | char *name; |
| 93 | |
| 94 | // Position of camera (front-facing, back-facing, etc). |
| 95 | SDL_CameraPosition position; |
| 96 | |
| 97 | // When refcount hits zero, we destroy the device object. |
| 98 | SDL_AtomicInt refcount; |
| 99 | |
| 100 | // These are, initially, set from camera_driver, but we might swap them out with Zombie versions on disconnect/failure. |
| 101 | bool (*WaitDevice)(SDL_Camera *device); |
| 102 | SDL_CameraFrameResult (*AcquireFrame)(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS); |
| 103 | void (*ReleaseFrame)(SDL_Camera *device, SDL_Surface *frame); |
| 104 | |
| 105 | // All supported formats/dimensions for this device. |
| 106 | SDL_CameraSpec *all_specs; |
| 107 | |
| 108 | // Elements in all_specs. |
| 109 | int num_specs; |
| 110 | |
| 111 | // The device's actual specification that the camera is outputting, before conversion. |
| 112 | SDL_CameraSpec actual_spec; |
| 113 | |
| 114 | // The device's current camera specification, after conversions. |
| 115 | SDL_CameraSpec spec; |
| 116 | |
| 117 | // Unique value assigned at creation time. |
| 118 | SDL_CameraID instance_id; |
| 119 | |
| 120 | // Driver-specific hardware data on how to open device (`hidden` is driver-specific data _when opened_). |
| 121 | void *handle; |
| 122 | |
| 123 | // Dropping the first frame(s) after open seems to help timing on some platforms. |
| 124 | int drop_frames; |
| 125 | |
| 126 | // Backend timestamp of first acquired frame, so we can keep these meaningful regardless of epoch. |
| 127 | Uint64 base_timestamp; |
| 128 | |
| 129 | // SDL timestamp of first acquired frame, so we can roughly convert to SDL ticks. |
| 130 | Uint64 adjust_timestamp; |
| 131 | |
| 132 | // Pixel data flows from the driver into these, then gets converted for the app if necessary. |
| 133 | SDL_Surface *acquire_surface; |
| 134 | |
| 135 | // acquire_surface converts or scales to this surface before landing in output_surfaces, if necessary. |
| 136 | SDL_Surface *conversion_surface; |
| 137 | |
| 138 | // A queue of surfaces that buffer converted/scaled frames of video until the app claims them. |
| 139 | SurfaceList output_surfaces[8]; |
| 140 | SurfaceList filled_output_surfaces; // this is FIFO |
| 141 | SurfaceList empty_output_surfaces; // this is LIFO |
| 142 | SurfaceList app_held_output_surfaces; |
| 143 | |
| 144 | // A fake video frame we allocate if the camera fails/disconnects. |
| 145 | Uint8 *zombie_pixels; |
| 146 | |
| 147 | // non-zero if acquire_surface needs to be scaled for final output. |
| 148 | int needs_scaling; // -1: downscale, 0: no scaling, 1: upscale |
| 149 | |
| 150 | // true if acquire_surface needs to be converted for final output. |
| 151 | bool needs_conversion; |
| 152 | |
| 153 | // Current state flags |
| 154 | SDL_AtomicInt shutdown; |
| 155 | SDL_AtomicInt zombie; |
| 156 | |
| 157 | // A thread to feed the camera device |
| 158 | SDL_Thread *thread; |
| 159 | |
| 160 | // Optional properties. |
| 161 | SDL_PropertiesID props; |
| 162 | |
| 163 | // -1: user denied permission, 0: waiting for user response, 1: user approved permission. |
| 164 | int permission; |
| 165 | |
| 166 | // Data private to this driver, used when device is opened and running. |
| 167 | struct SDL_PrivateCameraData *hidden; |
| 168 | }; |
| 169 | |
| 170 | typedef struct SDL_CameraDriverImpl |
| 171 | { |
| 172 | void (*DetectDevices)(void); |
| 173 | bool (*OpenDevice)(SDL_Camera *device, const SDL_CameraSpec *spec); |
| 174 | void (*CloseDevice)(SDL_Camera *device); |
| 175 | bool (*WaitDevice)(SDL_Camera *device); |
| 176 | SDL_CameraFrameResult (*AcquireFrame)(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS); // set frame->pixels, frame->pitch, and *timestampNS! |
| 177 | void (*ReleaseFrame)(SDL_Camera *device, SDL_Surface *frame); // Reclaim frame->pixels and frame->pitch! |
| 178 | void (*FreeDeviceHandle)(SDL_Camera *device); // SDL is done with this device; free the handle from SDL_AddCamera() |
| 179 | void (*Deinitialize)(void); |
| 180 | |
| 181 | bool ProvidesOwnCallbackThread; |
| 182 | } SDL_CameraDriverImpl; |
| 183 | |
| 184 | typedef struct SDL_PendingCameraEvent |
| 185 | { |
| 186 | Uint32 type; |
| 187 | SDL_CameraID devid; |
| 188 | struct SDL_PendingCameraEvent *next; |
| 189 | } SDL_PendingCameraEvent; |
| 190 | |
| 191 | typedef struct SDL_CameraDriver |
| 192 | { |
| 193 | const char *name; // The name of this camera driver |
| 194 | const char *desc; // The description of this camera driver |
| 195 | SDL_CameraDriverImpl impl; // the backend's interface |
| 196 | |
| 197 | SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash` // !!! FIXME: device_hash _also_ has a rwlock, see if we still need this one. |
| 198 | SDL_HashTable *device_hash; // the collection of currently-available camera devices |
| 199 | SDL_PendingCameraEvent pending_events; |
| 200 | SDL_PendingCameraEvent *pending_events_tail; |
| 201 | |
| 202 | SDL_AtomicInt device_count; |
| 203 | SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs. |
| 204 | } SDL_CameraDriver; |
| 205 | |
| 206 | typedef struct CameraBootStrap |
| 207 | { |
| 208 | const char *name; |
| 209 | const char *desc; |
| 210 | bool (*init)(SDL_CameraDriverImpl *impl); |
| 211 | bool demand_only; // if true: request explicitly, or it won't be available. |
| 212 | } CameraBootStrap; |
| 213 | |
| 214 | // Not all of these are available in a given build. Use #ifdefs, etc. |
| 215 | extern CameraBootStrap DUMMYCAMERA_bootstrap; |
| 216 | extern CameraBootStrap PIPEWIRECAMERA_bootstrap; |
| 217 | extern CameraBootStrap V4L2_bootstrap; |
| 218 | extern CameraBootStrap COREMEDIA_bootstrap; |
| 219 | extern CameraBootStrap ANDROIDCAMERA_bootstrap; |
| 220 | extern CameraBootStrap EMSCRIPTENCAMERA_bootstrap; |
| 221 | extern CameraBootStrap MEDIAFOUNDATION_bootstrap; |
| 222 | extern CameraBootStrap VITACAMERA_bootstrap; |
| 223 | |
| 224 | #endif // SDL_syscamera_h_ |
| 225 | |