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).
main
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

@ -49,23 +49,16 @@ typedef Uint32 SDL_CameraDeviceID;
/**
* The structure used to identify an SDL camera device
* The structure used to identify an opened SDL camera
*/
struct SDL_CameraDevice;
typedef struct SDL_CameraDevice SDL_CameraDevice;
#define SDL_CAMERA_ALLOW_ANY_CHANGE 1
struct SDL_Camera;
typedef struct SDL_Camera SDL_Camera;
/**
* SDL_CameraSpec structure
*
* Only those field can be 'desired' when configuring the device:
* - format
* - width
* - height
*
* \sa SDL_GetCameraFormat
* \sa SDL_GetCameraFrameSize
* \sa SDL_GetCameraDeviceSupportedSpecs
* \sa SDL_GetCameraSpec
*
*/
typedef struct SDL_CameraSpec
@ -75,39 +68,6 @@ typedef struct SDL_CameraSpec
int height; /**< Frame height */
} SDL_CameraSpec;
/**
* SDL Camera Status
*
* Change states but calling the function in this order:
*
* SDL_OpenCamera()
* SDL_SetCameraSpec() -> Init
* SDL_StartCamera() -> Playing
* SDL_StopCamera() -> Stopped
* SDL_CloseCamera()
*
*/
typedef enum
{
SDL_CAMERA_FAIL = -1, /**< Failed */
SDL_CAMERA_INIT = 0, /**< Init, spec hasn't been set */
SDL_CAMERA_STOPPED, /**< Stopped */
SDL_CAMERA_PLAYING /**< Playing */
} SDL_CameraStatus;
/**
* SDL Video Capture Status
*/
typedef struct SDL_CameraFrame
{
Uint64 timestampNS; /**< Frame timestamp in nanoseconds when read from the driver */
int num_planes; /**< Number of planes */
Uint8 *data[3]; /**< Pointer to data of i-th plane */
int pitch[3]; /**< Pitch of i-th plane */
void *internal; /**< Private field */
} SDL_CameraFrame;
/**
* Use this function to get the number of built-in camera drivers.
*
@ -176,11 +136,13 @@ extern DECLSPEC const char *SDLCALL SDL_GetCurrentCameraDriver(void);
/**
* Get a list of currently connected camera devices.
*
* \param count a pointer filled in with the number of camera devices
* \param count a pointer filled in with the number of camera devices. Can be NULL.
* \returns a 0 terminated array of camera instance IDs which should be
* freed with SDL_free(), or NULL on error; call SDL_GetError() for
* more details.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCamera
@ -188,237 +150,202 @@ extern DECLSPEC const char *SDLCALL SDL_GetCurrentCameraDriver(void);
extern DECLSPEC SDL_CameraDeviceID *SDLCALL SDL_GetCameraDevices(int *count);
/**
* Open a Video Capture device
* Get the list of native formats/sizes a camera supports.
*
* This returns a list of all formats and frame sizes that a specific
* camera can offer. This is useful if your app can accept a variety
* of image formats and sizes and so want to find the optimal spec
* that doesn't require conversion.
*
* This function isn't strictly required; if you call SDL_OpenCameraDevice
* with a NULL spec, SDL will choose a native format for you, and if you
* instead specify a desired format, it will transparently convert to the
* requested format on your behalf.
*
* If `count` is not NULL, it will be filled with the number of elements
* in the returned array. Additionally, the last element of the array
* has all fields set to zero (this element is not included in `count`).
*
* The returned list is owned by the caller, and should be released with
* SDL_free() when no longer needed.
*
* \param devid the camera device instance ID to query.
* \param count a pointer filled in with the number of elements in the list. Can be NULL.
* \returns a 0 terminated array of SDL_CameraSpecs, which should be
* freed with SDL_free(), or NULL on error; call SDL_GetError() for
* more details.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraDevices
* \sa SDL_OpenCameraDevice
*/
extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedSpecs(SDL_CameraDeviceID devid, int *count);
/**
* Get human-readable device name for a camera.
*
* The returned string is owned by the caller; please release it with
* SDL_free() when done with it.
*
* \param instance_id the camera device instance ID
* \returns Human-readable device name, or NULL on error; call SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraDevices
*/
extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id);
/**
* Open a video capture device (a "camera").
*
* You can open the device with any reasonable spec, and if the hardware can't
* directly support it, it will convert data seamlessly to the requested
* format. This might incur overhead, including scaling of image data.
*
* If you would rather accept whatever format the device offers, you can
* pass a NULL spec here and it will choose one for you (and you can use
* SDL_Surface's conversion/scaling functions directly if necessary).
*
* You can call SDL_GetCameraSpec() to get the actual data format if
* passing a NULL spec here. You can see the exact specs a device can
* support without conversion with SDL_GetCameraSupportedSpecs().
*
* \param instance_id the camera device instance ID
* \param spec The desired format for data the device will provide. Can be NULL.
* \returns device, or NULL on failure; call SDL_GetError() for more
* information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraDeviceName
* \sa SDL_GetCameraDevices
* \sa SDL_OpenCameraWithSpec
*/
extern DECLSPEC SDL_CameraDevice *SDLCALL SDL_OpenCamera(SDL_CameraDeviceID instance_id);
extern DECLSPEC SDL_Camera *SDLCALL SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *spec);
/**
* Set specification
* Get the instance ID of an opened camera.
*
* \param device opened camera device
* \param desired desired camera spec
* \param obtained obtained camera spec
* \param allowed_changes allow changes or not
* \returns 0 on success or a negative error code on failure; call
* \param device an SDL_Camera to query
* \returns the instance ID of the specified camera on success or 0 on
* failure; call SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCameraDevice
*/
extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *camera);
/**
* Get the properties associated with an opened camera.
*
* \param device the SDL_Camera obtained from SDL_OpenCameraDevice()
* \returns a valid property ID on success or 0 on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCamera
* \sa SDL_OpenCameraWithSpec
* \sa SDL_GetCameraSpec
*/
extern DECLSPEC int SDLCALL SDL_SetCameraSpec(SDL_CameraDevice *device,
const SDL_CameraSpec *desired,
SDL_CameraSpec *obtained,
int allowed_changes);
/**
* Open a Video Capture device and set specification
*
* \param instance_id the camera device instance ID
* \param desired desired camera spec
* \param obtained obtained camera spec
* \param allowed_changes allow changes or not
* \returns device, or NULL on failure; call SDL_GetError() for more
* information.
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCamera
* \sa SDL_SetCameraSpec
* \sa SDL_GetCameraSpec
* \sa SDL_GetProperty
* \sa SDL_SetProperty
*/
extern DECLSPEC SDL_CameraDevice *SDLCALL SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id,
const SDL_CameraSpec *desired,
SDL_CameraSpec *obtained,
int allowed_changes);
extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *camera);
/**
* Get device name
* Get the spec that a camera is using when generating images.
*
* \param instance_id the camera device instance ID
* \returns device name, shouldn't be freed
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraDevices
*/
extern DECLSPEC const char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id);
/**
* Get the obtained camera spec
* Note that this might not be the native format of the hardware, as SDL
* might be converting to this format behind the scenes.
*
* \param device opened camera device
* \param spec The SDL_CameraSpec to be initialized by this function.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetCameraSpec
* \sa SDL_OpenCameraWithSpec
*/
extern DECLSPEC int SDLCALL SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec);
/**
* Get frame format of camera device.
*
* The value can be used to fill SDL_CameraSpec structure.
*
* \param device opened camera device
* \param index format between 0 and num -1
* \param format pointer output format (SDL_PixelFormatEnum)
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetNumCameraFormats
* \sa SDL_OpenCameraDevice
*/
extern DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_CameraDevice *device,
int index,
Uint32 *format);
/**
* Number of available formats for the device
*
* \param device opened camera device
* \returns number of formats or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraFormat
* \sa SDL_SetCameraSpec
*/
extern DECLSPEC int SDLCALL SDL_GetNumCameraFormats(SDL_CameraDevice *device);
/**
* Get frame sizes of the device and the specified input format.
*
* The value can be used to fill SDL_CameraSpec structure.
*
* \param device opened camera device
* \param format a format that can be used by the device (SDL_PixelFormatEnum)
* \param index framesize between 0 and num -1
* \param width output width
* \param height output height
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetNumCameraFrameSizes
*/
extern DECLSPEC int SDLCALL SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height);
/**
* Number of different framesizes available for the device and pixel format.
*
* \param device opened camera device
* \param format frame pixel format (SDL_PixelFormatEnum)
* \returns number of framesizes or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraFrameSize
* \sa SDL_SetCameraSpec
*/
extern DECLSPEC int SDLCALL SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format);
/**
* Get camera status
*
* \param device opened camera device
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_CameraStatus
*/
extern DECLSPEC SDL_CameraStatus SDLCALL SDL_GetCameraStatus(SDL_CameraDevice *device);
/**
* Start camera
*
* \param device opened camera device
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_StopCamera
*/
extern DECLSPEC int SDLCALL SDL_StartCamera(SDL_CameraDevice *device);
extern DECLSPEC int SDLCALL SDL_GetCameraSpec(SDL_Camera *camera, SDL_CameraSpec *spec);
/**
* Acquire a frame.
*
* The frame is a memory pointer to the image data, whose size and format are
* given by the the obtained spec.
* given by the spec requested when opening the device.
*
* Non blocking API. If there is a frame available, frame->num_planes is non
* 0. If frame->num_planes is 0 and returned code is 0, there is no frame at
* that time.
* This is a non blocking API. If there is a frame available, a non-NULL surface is
* returned, and timestampNS will be filled with a non-zero value.
*
* After used, the frame should be released with SDL_ReleaseCameraFrame
* Note that an error case can also return NULL, but a NULL by itself is normal
* and just signifies that a new frame is not yet available. Note that even if a
* camera device fails outright (a USB camera is unplugged while in use, etc), SDL
* will send an event separately to notify the app, but continue to provide blank
* frames at ongoing intervals until SDL_CloseCamera() is called, so real
* failure here is almost always an out of memory condition.
*
* After use, the frame should be released with SDL_ReleaseCameraFrame(). If you
* don't do this, the system may stop providing more video! If the hardware is
* using DMA to write directly into memory, frames held too long may be overwritten
* with new data.
*
* Do not call SDL_FreeSurface() on the returned surface! It must be given back
* to the camera subsystem with SDL_ReleaseCameraFrame!
*
* \param device opened camera device
* \param frame pointer to get the frame
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
* \param timestampNS a pointer filled in with the frame's timestamp, or 0 on error. Can be NULL.
* \returns A new frame of video on success, NULL if none is currently available.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReleaseCameraFrame
*/
extern DECLSPEC int SDLCALL SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame);
extern DECLSPEC SDL_Surface * SDLCALL SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS);
/**
* Release a frame.
* Release a frame of video acquired from a camera.
*
* Let the back-end re-use the internal buffer for camera.
*
* All acquired frames should be released before closing the device.
* This function _must_ be called only on surface objects returned by
* SDL_AcquireCameraFrame(). This function should be called as quickly as
* possible after acquisition, as SDL keeps a small FIFO queue of surfaces
* for video frames; if surfaces aren't released in a timely manner, SDL
* may drop upcoming video frames from the camera.
*
* If the app needs to keep the surface for a significant time, they should
* make a copy of it and release the original.
*
* The app should not use the surface again after calling this function;
* assume the surface is freed and the pointer is invalid.
*
* \param device opened camera device
* \param frame frame pointer.
* \param frame The video frame surface to release.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AcquireCameraFrame
*/
extern DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame);
/**
* Stop Video Capture
*
* \param device opened camera device
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_StartCamera
*/
extern DECLSPEC int SDLCALL SDL_StopCamera(SDL_CameraDevice *device);
extern DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame);
/**
* Use this function to shut down camera processing and close the
@ -426,12 +353,16 @@ extern DECLSPEC int SDLCALL SDL_StopCamera(SDL_CameraDevice *device);
*
* \param device opened camera device
*
* \threadsafety It is safe to call this function from any thread, but
* no thread may reference `device` once this function
* is called.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCameraWithSpec
* \sa SDL_OpenCamera
*/
extern DECLSPEC void SDLCALL SDL_CloseCamera(SDL_CameraDevice *device);
extern DECLSPEC void SDLCALL SDL_CloseCamera(SDL_Camera *camera);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus

View File

@ -205,6 +205,10 @@ typedef enum
SDL_EVENT_PEN_BUTTON_DOWN, /**< Pressure-sensitive pen button pressed */
SDL_EVENT_PEN_BUTTON_UP, /**< Pressure-sensitive pen button released */
/* Camera hotplug events */
SDL_EVENT_CAMERA_DEVICE_ADDED = 0x1400, /**< A new camera device is available */
SDL_EVENT_CAMERA_DEVICE_REMOVED, /**< A camera device has been removed. */
/* Render events */
SDL_EVENT_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
SDL_EVENT_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */
@ -526,6 +530,18 @@ typedef struct SDL_AudioDeviceEvent
Uint8 padding3;
} SDL_AudioDeviceEvent;
/**
* Camera device event structure (event.cdevice.*)
*/
typedef struct SDL_CameraDeviceEvent
{
Uint32 type; /**< ::SDL_EVENT_CAMERA_DEVICE_ADDED, or ::SDL_EVENT_CAMERA_DEVICE_REMOVED */
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
SDL_CameraDeviceID which; /**< SDL_CameraDeviceID for the device being added or removed or changing */
Uint8 padding1;
Uint8 padding2;
Uint8 padding3;
} SDL_CameraDeviceEvent;
/**
* Touch finger event structure (event.tfinger.*)
@ -699,6 +715,7 @@ typedef union SDL_Event
SDL_GamepadTouchpadEvent gtouchpad; /**< Gamepad touchpad event data */
SDL_GamepadSensorEvent gsensor; /**< Gamepad sensor event data */
SDL_AudioDeviceEvent adevice; /**< Audio device event data */
SDL_CameraDeviceEvent cdevice; /**< Camera device event data */
SDL_SensorEvent sensor; /**< Sensor event data */
SDL_QuitEvent quit; /**< Quit request event data */
SDL_UserEvent user; /**< Custom event data */

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

View File

@ -957,21 +957,18 @@ SDL3_0.0.0 {
SDL_SetWindowShape;
SDL_RenderViewportSet;
SDL_HasProperty;
SDL_GetNumCameraDrivers;
SDL_GetCameraDriver;
SDL_GetCurrentCameraDriver;
SDL_GetCameraDevices;
SDL_OpenCamera;
SDL_SetCameraSpec;
SDL_OpenCameraWithSpec;
SDL_GetCameraDeviceSupportedSpecs;
SDL_GetCameraDeviceName;
SDL_OpenCameraDevice;
SDL_GetCameraInstanceID;
SDL_GetCameraProperties;
SDL_GetCameraSpec;
SDL_GetCameraFormat;
SDL_GetNumCameraFormats;
SDL_GetCameraFrameSize;
SDL_GetNumCameraFrameSizes;
SDL_GetCameraStatus;
SDL_StartCamera;
SDL_AcquireCameraFrame;
SDL_ReleaseCameraFrame;
SDL_StopCamera;
SDL_CloseCamera;
# extra symbols go here (don't modify this line)
local: *;

View File

@ -982,19 +982,16 @@
#define SDL_SetWindowShape SDL_SetWindowShape_REAL
#define SDL_RenderViewportSet SDL_RenderViewportSet_REAL
#define SDL_HasProperty SDL_HasProperty_REAL
#define SDL_GetNumCameraDrivers SDL_GetNumCameraDrivers_REAL
#define SDL_GetCameraDriver SDL_GetCameraDriver_REAL
#define SDL_GetCurrentCameraDriver SDL_GetCurrentCameraDriver_REAL
#define SDL_GetCameraDevices SDL_GetCameraDevices_REAL
#define SDL_OpenCamera SDL_OpenCamera_REAL
#define SDL_SetCameraSpec SDL_SetCameraSpec_REAL
#define SDL_OpenCameraWithSpec SDL_OpenCameraWithSpec_REAL
#define SDL_GetCameraDeviceSupportedSpecs SDL_GetCameraDeviceSupportedSpecs_REAL
#define SDL_GetCameraDeviceName SDL_GetCameraDeviceName_REAL
#define SDL_OpenCameraDevice SDL_OpenCameraDevice_REAL
#define SDL_GetCameraInstanceID SDL_GetCameraInstanceID_REAL
#define SDL_GetCameraProperties SDL_GetCameraProperties_REAL
#define SDL_GetCameraSpec SDL_GetCameraSpec_REAL
#define SDL_GetCameraFormat SDL_GetCameraFormat_REAL
#define SDL_GetNumCameraFormats SDL_GetNumCameraFormats_REAL
#define SDL_GetCameraFrameSize SDL_GetCameraFrameSize_REAL
#define SDL_GetNumCameraFrameSizes SDL_GetNumCameraFrameSizes_REAL
#define SDL_GetCameraStatus SDL_GetCameraStatus_REAL
#define SDL_StartCamera SDL_StartCamera_REAL
#define SDL_AcquireCameraFrame SDL_AcquireCameraFrame_REAL
#define SDL_ReleaseCameraFrame SDL_ReleaseCameraFrame_REAL
#define SDL_StopCamera SDL_StopCamera_REAL
#define SDL_CloseCamera SDL_CloseCamera_REAL

View File

@ -1007,19 +1007,16 @@ SDL_DYNAPI_PROC(int,SDL_RenderGeometryRawFloat,(SDL_Renderer *a, SDL_Texture *b,
SDL_DYNAPI_PROC(int,SDL_SetWindowShape,(SDL_Window *a, SDL_Surface *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_RenderViewportSet,(SDL_Renderer *a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_HasProperty,(SDL_PropertiesID a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetNumCameraDrivers,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCameraDriver,(int a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCurrentCameraDriver,(void),(),return)
SDL_DYNAPI_PROC(SDL_CameraDeviceID*,SDL_GetCameraDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(SDL_CameraDevice*,SDL_OpenCamera,(SDL_CameraDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_SetCameraSpec,(SDL_CameraDevice *a, const SDL_CameraSpec *b, SDL_CameraSpec *c, int d),(a,b,c,d),return)
SDL_DYNAPI_PROC(SDL_CameraDevice*,SDL_OpenCameraWithSpec,(SDL_CameraDeviceID a, const SDL_CameraSpec *b, SDL_CameraSpec *c, int d),(a,b,c,d),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCameraDeviceName,(SDL_CameraDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetCameraSpec,(SDL_CameraDevice *a, SDL_CameraSpec *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetCameraFormat,(SDL_CameraDevice *a, int b, Uint32 *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetNumCameraFormats,(SDL_CameraDevice *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetCameraFrameSize,(SDL_CameraDevice *a, Uint32 b, int c, int *d, int *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_GetNumCameraFrameSizes,(SDL_CameraDevice *a, Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_CameraStatus,SDL_GetCameraStatus,(SDL_CameraDevice *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_StartCamera,(SDL_CameraDevice *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_AcquireCameraFrame,(SDL_CameraDevice *a, SDL_CameraFrame *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_CameraDevice *a, SDL_CameraFrame *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_StopCamera,(SDL_CameraDevice *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_CameraDevice *a),(a),)
SDL_DYNAPI_PROC(SDL_CameraSpec*,SDL_GetCameraDeviceSupportedSpecs,(SDL_CameraDeviceID a, int *b),(a,b),return)
SDL_DYNAPI_PROC(char*,SDL_GetCameraDeviceName,(SDL_CameraDeviceID a),(a),return)
SDL_DYNAPI_PROC(SDL_Camera*,SDL_OpenCameraDevice,(SDL_CameraDeviceID a, const SDL_CameraSpec *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_CameraDeviceID,SDL_GetCameraInstanceID,(SDL_Camera *a),(a),return)
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetCameraProperties,(SDL_Camera *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetCameraSpec,(SDL_Camera *a, SDL_CameraSpec *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_Camera *a, SDL_Surface *b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_Camera *a),(a),)

View File

@ -25,6 +25,7 @@
#include "SDL_events_c.h"
#include "../SDL_hints_c.h"
#include "../audio/SDL_audio_c.h"
#include "../camera/SDL_camera_c.h"
#include "../timer/SDL_timer_c.h"
#ifndef SDL_JOYSTICK_DISABLED
#include "../joystick/SDL_joystick_c.h"
@ -554,6 +555,15 @@ static void SDL_LogEvent(const SDL_Event *event)
break;
#undef PRINT_AUDIODEV_EVENT
#define PRINT_CAMERADEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u)", (uint)event->cdevice.timestamp, (uint)event->cdevice.which)
SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_ADDED)
PRINT_CAMERADEV_EVENT(event);
break;
SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_REMOVED)
PRINT_CAMERADEV_EVENT(event);
break;
#undef PRINT_CAMERADEV_EVENT
SDL_EVENT_CASE(SDL_EVENT_SENSOR_UPDATE)
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d data[0]=%f data[1]=%f data[2]=%f data[3]=%f data[4]=%f data[5]=%f)",
(uint)event->sensor.timestamp, (int)event->sensor.which,
@ -942,6 +952,10 @@ static void SDL_PumpEventsInternal(SDL_bool push_sentinel)
SDL_UpdateAudio();
#endif
#ifndef SDL_CAMERA_DISABLED
SDL_UpdateCamera();
#endif
#ifndef SDL_SENSOR_DISABLED
/* Check for sensor state change */
if (SDL_update_sensors) {

View File

@ -18,8 +18,13 @@
#include <emscripten/emscripten.h>
#endif
#include <stdio.h>
#if 1
int main(int argc, char **argv)
{
SDL_Log("FIXME: update me");
return 0;
}
#else
static const char *usage = "\
\n\
=========================================================================\n\
@ -769,3 +774,4 @@ int main(int argc, char **argv)
return 0;
}
#endif

View File

@ -28,21 +28,14 @@ int main(int argc, char **argv)
int quit = 0;
SDLTest_CommonState *state = NULL;
SDL_CameraDevice *device = NULL;
SDL_CameraSpec obtained;
SDL_CameraFrame frame_current;
SDL_Camera *camera = NULL;
SDL_CameraSpec spec;
SDL_Texture *texture = NULL;
int texture_updated = 0;
SDL_Surface *frame_current = NULL;
SDL_zero(evt);
SDL_zero(obtained);
SDL_zero(frame_current);
/* Set 0 to disable TouchEvent to be duplicated as MouseEvent with SDL_TOUCH_MOUSEID */
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
/* Set 0 to disable MouseEvent to be duplicated as TouchEvent with SDL_MOUSE_TOUCHID */
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
SDL_zero(spec);
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
@ -73,20 +66,42 @@ int main(int argc, char **argv)
return 1;
}
device = SDL_OpenCameraWithSpec(0, NULL, &obtained, SDL_CAMERA_ALLOW_ANY_CHANGE);
if (!device) {
SDL_Log("No camera? %s", SDL_GetError());
SDL_CameraDeviceID *devices = SDL_GetCameraDevices(NULL);
if (!devices) {
SDL_Log("SDL_GetCameraDevices failed: %s", SDL_GetError());
return 1;
}
if (SDL_StartCamera(device) < 0) {
SDL_Log("error SDL_StartCamera(): %s", SDL_GetError());
const SDL_CameraDeviceID devid = devices[0]; /* just take the first one. */
SDL_free(devices);
if (!devid) {
SDL_Log("No cameras available? %s", SDL_GetError());
return 1;
}
SDL_CameraSpec *pspec = NULL;
#if 0 /* just for edge-case testing purposes, ignore. */
pspec = &spec;
spec.width = 100 /*1280 * 2*/;
spec.height = 100 /*720 * 2*/;
spec.format = SDL_PIXELFORMAT_YUY2 /*SDL_PIXELFORMAT_RGBA8888*/;
#endif
camera = SDL_OpenCameraDevice(devid, pspec);
if (!camera) {
SDL_Log("Failed to open camera device: %s", SDL_GetError());
return 1;
}
if (SDL_GetCameraSpec(camera, &spec) < 0) {
SDL_Log("Couldn't get camera spec: %s", SDL_GetError());
return 1;
}
/* Create texture with appropriate format */
if (texture == NULL) {
texture = SDL_CreateTexture(renderer, obtained.format, SDL_TEXTUREACCESS_STATIC, obtained.width, obtained.height);
texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height);
if (texture == NULL) {
SDL_Log("Couldn't create texture: %s", SDL_GetError());
return 1;
@ -118,21 +133,18 @@ int main(int argc, char **argv)
}
{
SDL_CameraFrame frame_next;
SDL_zero(frame_next);
Uint64 timestampNS = 0;
SDL_Surface *frame_next = SDL_AcquireCameraFrame(camera, &timestampNS);
if (SDL_AcquireCameraFrame(device, &frame_next) < 0) {
SDL_Log("err SDL_AcquireCameraFrame: %s", SDL_GetError());
}
#if 0
if (frame_next.num_planes) {
SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next.data[0], frame_next.timestampNS);
if (frame_next) {
SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS);
}
#endif
if (frame_next.num_planes) {
if (frame_current.num_planes) {
if (SDL_ReleaseCameraFrame(device, &frame_current) < 0) {
if (frame_next) {
if (frame_current) {
if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) {
SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError());
}
}
@ -146,20 +158,8 @@ int main(int argc, char **argv)
}
/* Update SDL_Texture with last video frame (only once per new frame) */
if (frame_current.num_planes && texture_updated == 0) {
/* Use software data */
if (frame_current.num_planes == 1) {
SDL_UpdateTexture(texture, NULL,
frame_current.data[0], frame_current.pitch[0]);
} else if (frame_current.num_planes == 2) {
SDL_UpdateNVTexture(texture, NULL,
frame_current.data[0], frame_current.pitch[0],
frame_current.data[1], frame_current.pitch[1]);
} else if (frame_current.num_planes == 3) {
SDL_UpdateYUVTexture(texture, NULL, frame_current.data[0], frame_current.pitch[0],
frame_current.data[1], frame_current.pitch[1],
frame_current.data[2], frame_current.pitch[2]);
}
if (frame_current && texture_updated == 0) {
SDL_UpdateTexture(texture, NULL, frame_current->pixels, frame_current->pitch);
texture_updated = 1;
}
@ -177,7 +177,7 @@ int main(int argc, char **argv)
th = (int)((float) th * scale);
}
d.x = (float)(10 );
d.y = (float)(win_h - th);
d.y = ((float)(win_h - th)) / 2.0f;
d.w = (float)tw;
d.h = (float)(th - 10);
SDL_RenderTexture(renderer, texture, NULL, &d);
@ -186,13 +186,10 @@ int main(int argc, char **argv)
SDL_RenderPresent(renderer);
}
if (SDL_StopCamera(device) < 0) {
SDL_Log("error SDL_StopCamera(): %s", SDL_GetError());
if (frame_current) {
SDL_ReleaseCameraFrame(camera, frame_current);
}
if (frame_current.num_planes) {
SDL_ReleaseCameraFrame(device, &frame_current);
}
SDL_CloseCamera(device);
SDL_CloseCamera(camera);
if (texture) {
SDL_DestroyTexture(texture);
@ -205,3 +202,4 @@ int main(int argc, char **argv)
return 0;
}