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 | |