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

File diff suppressed because it is too large Load Diff

View File

@@ -29,4 +29,7 @@ int SDL_CameraInit(const char *driver_name);
// Shutdown the camera subsystem
void SDL_QuitCamera(void);
// "Pump" the event queue.
extern void SDL_UpdateCamera(void);
#endif // SDL_camera_c_h_

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

View File

@@ -135,7 +135,9 @@ static Uint32 nsfourcc_to_sdlformat(NSString *nsfourcc)
if (SDL_strcmp("yuvs", str) == 0) return SDL_PIXELFORMAT_UYVY;
if (SDL_strcmp("420f", str) == 0) return SDL_PIXELFORMAT_UNKNOWN;
SDL_Log("Unknown format '%s'", str);
#if DEBUG_CAMERA
SDL_Log("CAMERA: Unknown format '%s'", str);
#endif
return SDL_PIXELFORMAT_UNKNOWN;
}
@@ -177,8 +179,9 @@ static NSString *sdlformat_to_nsfourcc(Uint32 fmt)
- (void)captureOutput:(AVCaptureOutput *)output
didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection {
// !!! FIXME #if DEBUG_CAMERA
SDL_Log("Drop frame..");
#if DEBUG_CAMERA
SDL_Log("CAMERA: Drop frame..");
#endif
}
@end
@@ -362,13 +365,13 @@ static int COREMEDIA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *fram
const int numPlanes = CVPixelBufferGetPlaneCount(image);
const int planar = CVPixelBufferIsPlanar(image);
#if 0
#if DEBUG_CAMERA
const int w = CVPixelBufferGetWidth(image);
const int h = CVPixelBufferGetHeight(image);
const int sz = CVPixelBufferGetDataSize(image);
const int pitch = CVPixelBufferGetBytesPerRow(image);
SDL_Log("buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch);
#endif
SDL_Log("CAMERA: buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch);
#endif
CVPixelBufferLockBaseAddress(image, 0);

View File

@@ -0,0 +1,80 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_CAMERA_DRIVER_DUMMY
#include "../SDL_syscamera.h"
static int DUMMYCAMERA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec)
{
return SDL_Unsupported();
}
static void DUMMYCAMERA_CloseDevice(SDL_CameraDevice *device)
{
}
static int DUMMYCAMERA_WaitDevice(SDL_CameraDevice *device)
{
return SDL_Unsupported();
}
static int DUMMYCAMERA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS)
{
return SDL_Unsupported();
}
static void DUMMYCAMERA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame)
{
}
static void DUMMYCAMERA_DetectDevices(void)
{
}
static void DUMMYCAMERA_FreeDeviceHandle(SDL_CameraDevice *device)
{
}
static void DUMMYCAMERA_Deinitialize(void)
{
}
static SDL_bool DUMMYCAMERA_Init(SDL_CameraDriverImpl *impl)
{
impl->DetectDevices = DUMMYCAMERA_DetectDevices;
impl->OpenDevice = DUMMYCAMERA_OpenDevice;
impl->CloseDevice = DUMMYCAMERA_CloseDevice;
impl->WaitDevice = DUMMYCAMERA_WaitDevice;
impl->AcquireFrame = DUMMYCAMERA_AcquireFrame;
impl->ReleaseFrame = DUMMYCAMERA_ReleaseFrame;
impl->FreeDeviceHandle = DUMMYCAMERA_FreeDeviceHandle;
impl->Deinitialize = DUMMYCAMERA_Deinitialize;
return SDL_TRUE;
}
CameraBootStrap DUMMYCAMERA_bootstrap = {
"dummy", "SDL dummy camera driver", DUMMYCAMERA_Init, SDL_TRUE
};
#endif

File diff suppressed because it is too large Load Diff