camera: Massive code reworking.

- Simplified public API, simplified backend interface.
- Camera device hotplug events.
- Thread code is split up so it backends that provide own threads can use it.
- Added "dummy" backend.

Note that CoreMedia (Apple) and Android backends need to be updated, as does
the testcamera app (testcameraminimal works).
This commit is contained in:
Ryan C. Gordon
2023-12-15 11:45:11 -05:00
parent 3d2d5d18f3
commit d3e6ef3cc6
14 changed files with 1772 additions and 1587 deletions

View File

@@ -23,67 +23,141 @@
#ifndef SDL_syscamera_h_
#define SDL_syscamera_h_
#include "../SDL_list.h"
#include "../SDL_hashtable.h"
#define DEBUG_CAMERA 1
// The SDL camera driver
// !!! FIXME: update these drivers!
#ifdef SDL_CAMERA_DRIVER_COREMEDIA
#undef SDL_CAMERA_DRIVER_COREMEDIA
#endif
#ifdef SDL_CAMERA_DRIVER_ANDROID
#undef SDL_CAMERA_DRIVER_ANDROID
#endif
typedef struct SDL_CameraDevice SDL_CameraDevice;
/* Backends should call this as devices are added to the system (such as
a USB camera being plugged in), and should also be called for
for every device found during DetectDevices(). */
extern SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle);
/* Backends should call this if an opened camera device is lost.
This can happen due to i/o errors, or a device being unplugged, etc. */
extern void SDL_CameraDeviceDisconnected(SDL_CameraDevice *device);
// Find an SDL_CameraDevice, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE.
extern SDL_CameraDevice *SDL_FindPhysicalCameraDeviceByCallback(SDL_bool (*callback)(SDL_CameraDevice *device, void *userdata), void *userdata);
// These functions are the heart of the camera threads. Backends can call them directly if they aren't using the SDL-provided thread.
extern void SDL_CameraThreadSetup(SDL_CameraDevice *device);
extern SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device);
extern void SDL_CameraThreadShutdown(SDL_CameraDevice *device);
typedef struct SurfaceList
{
SDL_Surface *surface;
Uint64 timestampNS;
struct SurfaceList *next;
} SurfaceList;
// Define the SDL camera driver structure
struct SDL_CameraDevice
{
// The device's current camera specification
// A mutex for locking
SDL_Mutex *lock;
// Human-readable device name.
char *name;
// When refcount hits zero, we destroy the device object.
SDL_AtomicInt refcount;
// All supported formats/dimensions for this device.
SDL_CameraSpec *all_specs;
// Elements in all_specs.
int num_specs;
// The device's actual specification that the camera is outputting, before conversion.
SDL_CameraSpec actual_spec;
// The device's current camera specification, after conversions.
SDL_CameraSpec spec;
// Device name
char *dev_name;
// Unique value assigned at creation time.
SDL_CameraDeviceID instance_id;
// Driver-specific hardware data on how to open device (`hidden` is driver-specific data _when opened_).
void *handle;
// Pixel data flows from the driver into these, then gets converted for the app if necessary.
SDL_Surface *acquire_surface;
// acquire_surface converts or scales to this surface before landing in output_surfaces, if necessary.
SDL_Surface *conversion_surface;
// A queue of surfaces that buffer converted/scaled frames of video until the app claims them.
SurfaceList output_surfaces[8];
SurfaceList filled_output_surfaces; // this is FIFO
SurfaceList empty_output_surfaces; // this is LIFO
SurfaceList app_held_output_surfaces;
// non-zero if acquire_surface needs to be scaled for final output.
int needs_scaling; // -1: downscale, 0: no scaling, 1: upscale
// SDL_TRUE if acquire_surface needs to be converted for final output.
SDL_bool needs_conversion;
// Current state flags
SDL_AtomicInt shutdown;
SDL_AtomicInt enabled;
SDL_bool is_spec_set;
// A mutex for locking the queue buffers
SDL_Mutex *device_lock;
SDL_Mutex *acquiring_lock;
SDL_AtomicInt zombie;
// A thread to feed the camera device
SDL_Thread *thread;
SDL_ThreadID threadid;
// Queued buffers (if app not using callback).
SDL_ListNode *buffer_queue;
// Optional properties.
SDL_PropertiesID props;
// Data private to this driver
// Data private to this driver, used when device is opened and running.
struct SDL_PrivateCameraData *hidden;
};
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);
int (*OpenDevice)(SDL_CameraDevice *device, const SDL_CameraSpec *spec);
void (*CloseDevice)(SDL_CameraDevice *device);
int (*WaitDevice)(SDL_CameraDevice *device);
int (*AcquireFrame)(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS); // set frame->pixels, frame->pitch, and *timestampNS!
void (*ReleaseFrame)(SDL_CameraDevice *device, SDL_Surface *frame); // Reclaim frame->pixels and frame->pitch!
void (*FreeDeviceHandle)(SDL_CameraDevice *device); // SDL is done with this device; free the handle from SDL_AddCameraDevice()
void (*Deinitialize)(void);
SDL_bool ProvidesOwnCallbackThread;
} SDL_CameraDriverImpl;
typedef struct SDL_PendingCameraDeviceEvent
{
Uint32 type;
SDL_CameraDeviceID devid;
struct SDL_PendingCameraDeviceEvent *next;
} SDL_PendingCameraDeviceEvent;
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_RWLock *device_hash_lock; // A rwlock that protects `device_hash`
SDL_HashTable *device_hash; // the collection of currently-available camera devices
SDL_PendingCameraDeviceEvent pending_events;
SDL_PendingCameraDeviceEvent *pending_events_tail;
SDL_AtomicInt device_count;
SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs.
} SDL_CameraDriver;
typedef struct CameraBootStrap