mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-03-31 06:11:04 +02:00
806 lines
21 KiB
C
806 lines
21 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2024 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"
|
|
|
|
#include "SDL_syscamera.h"
|
|
#include "SDL_camera_c.h"
|
|
#include "../video/SDL_pixels_c.h"
|
|
#include "../thread/SDL_systhread.h"
|
|
|
|
// Available camera drivers
|
|
static const CameraBootStrap *const bootstrap[] = {
|
|
#ifdef SDL_CAMERA_DRIVER_V4L2
|
|
&V4L2_bootstrap,
|
|
#endif
|
|
#ifdef SDL_CAMERA_DRIVER_COREMEDIA
|
|
&COREMEDIA_bootstrap,
|
|
#endif
|
|
#ifdef SDL_CAMERA_DRIVER_ANDROID
|
|
&ANDROIDCAMERA_bootstrap,
|
|
#endif
|
|
#ifdef SDL_CAMERA_DRIVER_DUMMY
|
|
&DUMMYCAMERA_bootstrap,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
static SDL_CameraDriver camera_driver;
|
|
|
|
|
|
// list node entries to share frames between SDL and user app
|
|
// !!! FIXME: do we need this struct?
|
|
typedef struct entry_t
|
|
{
|
|
SDL_CameraFrame frame;
|
|
} entry_t;
|
|
|
|
static SDL_CameraDevice *open_devices[16]; // !!! FIXME: remove limit
|
|
|
|
int SDL_GetNumCameraDrivers(void)
|
|
{
|
|
return SDL_arraysize(bootstrap) - 1;
|
|
}
|
|
|
|
const char *SDL_GetCameraDriver(int index)
|
|
{
|
|
if (index >= 0 && index < SDL_GetNumCameraDrivers()) {
|
|
return bootstrap[index]->name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const char *SDL_GetCurrentCameraDriver(void)
|
|
{
|
|
return camera_driver.name;
|
|
}
|
|
|
|
|
|
static void CloseCameraDevice(SDL_CameraDevice *device)
|
|
{
|
|
if (!device) {
|
|
return;
|
|
}
|
|
|
|
SDL_AtomicSet(&device->shutdown, 1);
|
|
SDL_AtomicSet(&device->enabled, 1);
|
|
|
|
if (device->thread != NULL) {
|
|
SDL_WaitThread(device->thread, NULL);
|
|
}
|
|
if (device->device_lock != NULL) {
|
|
SDL_DestroyMutex(device->device_lock);
|
|
}
|
|
if (device->acquiring_lock != NULL) {
|
|
SDL_DestroyMutex(device->acquiring_lock);
|
|
}
|
|
|
|
const int n = SDL_arraysize(open_devices);
|
|
for (int i = 0; i < n; i++) {
|
|
if (open_devices[i] == device) {
|
|
open_devices[i] = NULL;
|
|
}
|
|
}
|
|
|
|
entry_t *entry = NULL;
|
|
while (device->buffer_queue != NULL) {
|
|
SDL_ListPop(&device->buffer_queue, (void**)&entry);
|
|
if (entry) {
|
|
SDL_CameraFrame f = entry->frame;
|
|
// Release frames not acquired, if any
|
|
if (f.timestampNS) {
|
|
camera_driver.impl.ReleaseFrame(device, &f);
|
|
}
|
|
SDL_free(entry);
|
|
}
|
|
}
|
|
|
|
camera_driver.impl.CloseDevice(device);
|
|
|
|
SDL_free(device->dev_name);
|
|
SDL_free(device);
|
|
}
|
|
|
|
void SDL_CloseCamera(SDL_CameraDevice *device)
|
|
{
|
|
if (!device) {
|
|
SDL_InvalidParamError("device");
|
|
} else {
|
|
CloseCameraDevice(device);
|
|
}
|
|
}
|
|
|
|
int SDL_StartCamera(SDL_CameraDevice *device)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (device->is_spec_set == SDL_FALSE) {
|
|
return SDL_SetError("no spec set");
|
|
} else if (SDL_GetCameraStatus(device) != SDL_CAMERA_INIT) {
|
|
return SDL_SetError("invalid state");
|
|
}
|
|
|
|
const int result = camera_driver.impl.StartCamera(device);
|
|
if (result < 0) {
|
|
return result;
|
|
}
|
|
|
|
SDL_AtomicSet(&device->enabled, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (!spec) {
|
|
return SDL_InvalidParamError("spec");
|
|
}
|
|
|
|
SDL_zerop(spec);
|
|
return camera_driver.impl.GetDeviceSpec(device, spec);
|
|
}
|
|
|
|
int SDL_StopCamera(SDL_CameraDevice *device)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (SDL_GetCameraStatus(device) != SDL_CAMERA_PLAYING) {
|
|
return SDL_SetError("invalid state");
|
|
}
|
|
|
|
SDL_AtomicSet(&device->enabled, 0);
|
|
SDL_AtomicSet(&device->shutdown, 1);
|
|
|
|
SDL_LockMutex(device->acquiring_lock);
|
|
const int retval = camera_driver.impl.StopCamera(device);
|
|
SDL_UnlockMutex(device->acquiring_lock);
|
|
|
|
return (retval < 0) ? -1 : 0;
|
|
}
|
|
|
|
// Check spec has valid format and frame size
|
|
static int prepare_cameraspec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
|
|
{
|
|
// Check format
|
|
const int numfmts = SDL_GetNumCameraFormats(device);
|
|
SDL_bool is_format_valid = SDL_FALSE;
|
|
|
|
for (int i = 0; i < numfmts; i++) {
|
|
Uint32 format;
|
|
if (SDL_GetCameraFormat(device, i, &format) == 0) {
|
|
if (format == desired->format && format != SDL_PIXELFORMAT_UNKNOWN) {
|
|
is_format_valid = SDL_TRUE;
|
|
obtained->format = format;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_format_valid) {
|
|
if (allowed_changes) {
|
|
for (int i = 0; i < numfmts; i++) {
|
|
Uint32 format;
|
|
if (SDL_GetCameraFormat(device, i, &format) == 0) {
|
|
if (format != SDL_PIXELFORMAT_UNKNOWN) {
|
|
obtained->format = format;
|
|
is_format_valid = SDL_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return SDL_SetError("Not allowed to change the format");
|
|
}
|
|
}
|
|
|
|
if (!is_format_valid) {
|
|
return SDL_SetError("Invalid format");
|
|
}
|
|
|
|
// Check frame size
|
|
const int numsizes = SDL_GetNumCameraFrameSizes(device, obtained->format);
|
|
SDL_bool is_framesize_valid = SDL_FALSE;
|
|
|
|
for (int i = 0; i < numsizes; i++) {
|
|
int w, h;
|
|
if (SDL_GetCameraFrameSize(device, obtained->format, i, &w, &h) == 0) {
|
|
if (desired->width == w && desired->height == h) {
|
|
is_framesize_valid = SDL_TRUE;
|
|
obtained->width = w;
|
|
obtained->height = h;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_framesize_valid) {
|
|
if (allowed_changes) {
|
|
int w, h;
|
|
if (SDL_GetCameraFrameSize(device, obtained->format, 0, &w, &h) == 0) {
|
|
is_framesize_valid = SDL_TRUE;
|
|
obtained->width = w;
|
|
obtained->height = h;
|
|
}
|
|
} else {
|
|
return SDL_SetError("Not allowed to change the frame size");
|
|
}
|
|
}
|
|
|
|
if (!is_framesize_valid) {
|
|
return SDL_SetError("Invalid frame size");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id)
|
|
{
|
|
static char buf[256];
|
|
buf[0] = 0;
|
|
buf[255] = 0;
|
|
|
|
if (instance_id == 0) {
|
|
SDL_InvalidParamError("instance_id");
|
|
return NULL;
|
|
}
|
|
|
|
if (camera_driver.impl.GetDeviceName(instance_id, buf, sizeof (buf)) < 0) {
|
|
buf[0] = 0;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
SDL_CameraDeviceID *SDL_GetCameraDevices(int *count)
|
|
{
|
|
int dummycount = 0;
|
|
if (!count) {
|
|
count = &dummycount;
|
|
}
|
|
|
|
int num = 0;
|
|
SDL_CameraDeviceID *retval = camera_driver.impl.GetDevices(&num);
|
|
if (retval) {
|
|
*count = num;
|
|
return retval;
|
|
}
|
|
|
|
// return list of 0 ID, null terminated
|
|
retval = (SDL_CameraDeviceID *)SDL_calloc(1, sizeof(*retval));
|
|
if (retval == NULL) {
|
|
*count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
retval[0] = 0;
|
|
*count = 0;
|
|
|
|
return retval;
|
|
}
|
|
|
|
// Camera thread function
|
|
static int SDLCALL SDL_CameraThread(void *devicep)
|
|
{
|
|
const int delay = 20;
|
|
SDL_CameraDevice *device = (SDL_CameraDevice *) devicep;
|
|
|
|
#if DEBUG_CAMERA
|
|
SDL_Log("Start thread 'SDL_CameraThread'");
|
|
#endif
|
|
|
|
|
|
#ifdef SDL_VIDEO_DRIVER_ANDROID
|
|
// TODO
|
|
/*
|
|
{
|
|
// Set thread priority to THREAD_PRIORITY_VIDEO
|
|
extern void Android_JNI_CameraSetThreadPriority(int, int);
|
|
Android_JNI_CameraSetThreadPriority(device->iscapture, device);
|
|
}*/
|
|
#else
|
|
// The camera capture is always a high priority thread
|
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
|
#endif
|
|
|
|
// Perform any thread setup
|
|
device->threadid = SDL_GetCurrentThreadID();
|
|
|
|
// Init state
|
|
// !!! FIXME: use a semaphore or something
|
|
while (!SDL_AtomicGet(&device->enabled)) {
|
|
SDL_Delay(delay);
|
|
}
|
|
|
|
// Loop, filling the camera buffers
|
|
while (!SDL_AtomicGet(&device->shutdown)) {
|
|
SDL_CameraFrame f;
|
|
int ret;
|
|
|
|
SDL_zero(f);
|
|
|
|
SDL_LockMutex(device->acquiring_lock);
|
|
ret = camera_driver.impl.AcquireFrame(device, &f);
|
|
SDL_UnlockMutex(device->acquiring_lock);
|
|
|
|
if (ret == 0) {
|
|
if (f.num_planes == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (ret < 0) {
|
|
// Flag it as an error
|
|
#if DEBUG_CAMERA
|
|
SDL_Log("dev[%p] error AcquireFrame: %d %s", (void *)device, ret, SDL_GetError());
|
|
#endif
|
|
f.num_planes = 0;
|
|
}
|
|
|
|
|
|
entry_t *entry = SDL_malloc(sizeof (entry_t));
|
|
if (entry == NULL) {
|
|
goto error_mem;
|
|
}
|
|
|
|
entry->frame = f;
|
|
|
|
SDL_LockMutex(device->device_lock);
|
|
ret = SDL_ListAdd(&device->buffer_queue, entry);
|
|
SDL_UnlockMutex(device->device_lock);
|
|
|
|
if (ret < 0) {
|
|
SDL_free(entry);
|
|
goto error_mem;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_CAMERA
|
|
SDL_Log("dev[%p] End thread 'SDL_CameraThread'", (void *)device);
|
|
#endif
|
|
return 0;
|
|
|
|
error_mem:
|
|
#if DEBUG_CAMERA
|
|
SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError());
|
|
#endif
|
|
SDL_AtomicSet(&device->shutdown, 1);
|
|
return 0;
|
|
}
|
|
|
|
SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id)
|
|
{
|
|
const int n = SDL_arraysize(open_devices);
|
|
SDL_CameraDevice *device = NULL;
|
|
const char *device_name = NULL;
|
|
int id = -1;
|
|
|
|
if (!SDL_WasInit(SDL_INIT_VIDEO)) {
|
|
SDL_SetError("Video subsystem is not initialized");
|
|
goto error;
|
|
}
|
|
|
|
// !!! FIXME: there is a race condition here if two devices open from two threads at once.
|
|
// Find an available device ID...
|
|
for (int i = 0; i < n; i++) {
|
|
if (open_devices[i] == NULL) {
|
|
id = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (id == -1) {
|
|
SDL_SetError("Too many open camera devices");
|
|
goto error;
|
|
}
|
|
|
|
if (instance_id != 0) {
|
|
device_name = SDL_GetCameraDeviceName(instance_id);
|
|
if (device_name == NULL) {
|
|
goto error;
|
|
}
|
|
} else {
|
|
SDL_CameraDeviceID *devices = SDL_GetCameraDevices(NULL);
|
|
if (devices && devices[0]) {
|
|
device_name = SDL_GetCameraDeviceName(devices[0]);
|
|
SDL_free(devices);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// FIXME do we need this ?
|
|
// Let the user override.
|
|
{
|
|
const char *dev = SDL_getenv("SDL_CAMERA_DEVICE_NAME");
|
|
if (dev && dev[0]) {
|
|
device_name = dev;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (device_name == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
device = (SDL_CameraDevice *) SDL_calloc(1, sizeof (SDL_CameraDevice));
|
|
if (device == NULL) {
|
|
goto error;
|
|
}
|
|
device->dev_name = SDL_strdup(device_name);
|
|
|
|
SDL_AtomicSet(&device->shutdown, 0);
|
|
SDL_AtomicSet(&device->enabled, 0);
|
|
|
|
device->device_lock = SDL_CreateMutex();
|
|
if (device->device_lock == NULL) {
|
|
SDL_SetError("Couldn't create acquiring_lock");
|
|
goto error;
|
|
}
|
|
|
|
device->acquiring_lock = SDL_CreateMutex();
|
|
if (device->acquiring_lock == NULL) {
|
|
SDL_SetError("Couldn't create acquiring_lock");
|
|
goto error;
|
|
}
|
|
|
|
if (camera_driver.impl.OpenDevice(device) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
// empty
|
|
device->buffer_queue = NULL;
|
|
open_devices[id] = device; // add it to our list of open devices.
|
|
|
|
|
|
// Start the camera thread
|
|
char threadname[64];
|
|
SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", id);
|
|
device->thread = SDL_CreateThreadInternal(SDL_CameraThread, threadname, 0, device);
|
|
if (device->thread == NULL) {
|
|
SDL_SetError("Couldn't create camera thread");
|
|
goto error;
|
|
}
|
|
|
|
return device;
|
|
|
|
error:
|
|
CloseCameraDevice(device);
|
|
return NULL;
|
|
}
|
|
|
|
int SDL_SetCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
|
|
{
|
|
SDL_CameraSpec _obtained;
|
|
SDL_CameraSpec _desired;
|
|
int result;
|
|
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (device->is_spec_set == SDL_TRUE) {
|
|
return SDL_SetError("already configured");
|
|
}
|
|
|
|
if (!desired) {
|
|
SDL_zero(_desired);
|
|
desired = &_desired;
|
|
allowed_changes = SDL_CAMERA_ALLOW_ANY_CHANGE;
|
|
} else {
|
|
// in case desired == obtained
|
|
_desired = *desired;
|
|
desired = &_desired;
|
|
}
|
|
|
|
if (!obtained) {
|
|
obtained = &_obtained;
|
|
}
|
|
|
|
SDL_zerop(obtained);
|
|
|
|
if (prepare_cameraspec(device, desired, obtained, allowed_changes) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
device->spec = *obtained;
|
|
|
|
result = camera_driver.impl.InitDevice(device);
|
|
if (result < 0) {
|
|
return result;
|
|
}
|
|
|
|
*obtained = device->spec;
|
|
|
|
device->is_spec_set = SDL_TRUE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (!frame) {
|
|
return SDL_InvalidParamError("frame");
|
|
}
|
|
|
|
SDL_zerop(frame);
|
|
|
|
if (device->thread == NULL) {
|
|
int ret;
|
|
|
|
// Wait for a frame
|
|
while ((ret = camera_driver.impl.AcquireFrame(device, frame)) == 0) {
|
|
if (frame->num_planes) {
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
} else {
|
|
entry_t *entry = NULL;
|
|
|
|
SDL_LockMutex(device->device_lock);
|
|
SDL_ListPop(&device->buffer_queue, (void**)&entry);
|
|
SDL_UnlockMutex(device->device_lock);
|
|
|
|
if (entry) {
|
|
*frame = entry->frame;
|
|
SDL_free(entry);
|
|
|
|
// Error from thread
|
|
if (frame->num_planes == 0 && frame->timestampNS == 0) {
|
|
return SDL_SetError("error from acquisition thread");
|
|
}
|
|
} else {
|
|
// Queue is empty. Not an error.
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (frame == NULL) {
|
|
return SDL_InvalidParamError("frame");
|
|
} else if (camera_driver.impl.ReleaseFrame(device, frame) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
SDL_zerop(frame);
|
|
return 0;
|
|
}
|
|
|
|
int SDL_GetNumCameraFormats(SDL_CameraDevice *device)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
}
|
|
return camera_driver.impl.GetNumFormats(device);
|
|
}
|
|
|
|
int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (!format) {
|
|
return SDL_InvalidParamError("format");
|
|
}
|
|
*format = 0;
|
|
return camera_driver.impl.GetFormat(device, index, format);
|
|
}
|
|
|
|
int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
}
|
|
return camera_driver.impl.GetNumFrameSizes(device, format);
|
|
}
|
|
|
|
int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height)
|
|
{
|
|
if (!device) {
|
|
return SDL_InvalidParamError("device");
|
|
} else if (!width) {
|
|
return SDL_InvalidParamError("width");
|
|
} else if (!height) {
|
|
return SDL_InvalidParamError("height");
|
|
}
|
|
*width = *height = 0;
|
|
return camera_driver.impl.GetFrameSize(device, format, index, width, height);
|
|
}
|
|
|
|
SDL_CameraDevice *SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
|
|
{
|
|
SDL_CameraDevice *device;
|
|
|
|
if ((device = SDL_OpenCamera(instance_id)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (SDL_SetCameraSpec(device, desired, obtained, allowed_changes) < 0) {
|
|
SDL_CloseCamera(device);
|
|
return NULL;
|
|
}
|
|
return device;
|
|
}
|
|
|
|
SDL_CameraStatus SDL_GetCameraStatus(SDL_CameraDevice *device)
|
|
{
|
|
if (device == NULL) {
|
|
return SDL_CAMERA_INIT;
|
|
} else if (device->is_spec_set == SDL_FALSE) {
|
|
return SDL_CAMERA_INIT;
|
|
} else if (SDL_AtomicGet(&device->shutdown)) {
|
|
return SDL_CAMERA_STOPPED;
|
|
} else if (SDL_AtomicGet(&device->enabled)) {
|
|
return SDL_CAMERA_PLAYING;
|
|
}
|
|
return SDL_CAMERA_INIT;
|
|
}
|
|
|
|
static void CompleteCameraEntryPoints(void)
|
|
{
|
|
// this doesn't currently fill in stub implementations, it just asserts the backend filled them all in.
|
|
#define FILL_STUB(x) SDL_assert(camera_driver.impl.x != NULL)
|
|
FILL_STUB(DetectDevices);
|
|
FILL_STUB(OpenDevice);
|
|
FILL_STUB(CloseDevice);
|
|
FILL_STUB(InitDevice);
|
|
FILL_STUB(GetDeviceSpec);
|
|
FILL_STUB(StartCamera);
|
|
FILL_STUB(StopCamera);
|
|
FILL_STUB(AcquireFrame);
|
|
FILL_STUB(ReleaseFrame);
|
|
FILL_STUB(GetNumFormats);
|
|
FILL_STUB(GetFormat);
|
|
FILL_STUB(GetNumFrameSizes);
|
|
FILL_STUB(GetFrameSize);
|
|
FILL_STUB(GetDeviceName);
|
|
FILL_STUB(GetDevices);
|
|
FILL_STUB(Deinitialize);
|
|
#undef FILL_STUB
|
|
}
|
|
|
|
void SDL_QuitCamera(void)
|
|
{
|
|
if (!camera_driver.name) { // not initialized?!
|
|
return;
|
|
}
|
|
|
|
const int n = SDL_arraysize(open_devices);
|
|
for (int i = 0; i < n; i++) {
|
|
CloseCameraDevice(open_devices[i]);
|
|
}
|
|
|
|
SDL_zeroa(open_devices);
|
|
|
|
#if 0 // !!! FIXME
|
|
SDL_PendingCameraDeviceEvent *pending_events = camera_driver.pending_events.next;
|
|
camera_driver.pending_events.next = NULL;
|
|
|
|
SDL_PendingCameraDeviceEvent *pending_next = NULL;
|
|
for (SDL_PendingCameraDeviceEvent *i = pending_events; i; i = pending_next) {
|
|
pending_next = i->next;
|
|
SDL_free(i);
|
|
}
|
|
#endif
|
|
|
|
// Free the driver data
|
|
camera_driver.impl.Deinitialize();
|
|
|
|
SDL_zero(camera_driver);
|
|
}
|
|
|
|
// this is 90% the same code as the audio subsystem uses.
|
|
int SDL_CameraInit(const char *driver_name)
|
|
{
|
|
if (SDL_GetCurrentCameraDriver()) {
|
|
SDL_QuitCamera(); // shutdown driver if already running.
|
|
}
|
|
|
|
SDL_zeroa(open_devices);
|
|
|
|
// Select the proper camera driver
|
|
if (!driver_name) {
|
|
driver_name = SDL_GetHint(SDL_HINT_CAMERA_DRIVER);
|
|
}
|
|
|
|
SDL_bool initialized = SDL_FALSE;
|
|
SDL_bool tried_to_init = SDL_FALSE;
|
|
|
|
if (driver_name && (*driver_name != 0)) {
|
|
char *driver_name_copy = SDL_strdup(driver_name);
|
|
const char *driver_attempt = driver_name_copy;
|
|
|
|
if (!driver_name_copy) {
|
|
return -1;
|
|
}
|
|
|
|
while (driver_attempt && (*driver_attempt != 0) && !initialized) {
|
|
char *driver_attempt_end = SDL_strchr(driver_attempt, ',');
|
|
if (driver_attempt_end) {
|
|
*driver_attempt_end = '\0';
|
|
}
|
|
|
|
for (int i = 0; bootstrap[i]; i++) {
|
|
if (SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) {
|
|
tried_to_init = SDL_TRUE;
|
|
SDL_zero(camera_driver);
|
|
#if 0 // !!! FIXME
|
|
camera_driver.pending_events_tail = &camera_driver.pending_events;
|
|
#endif
|
|
if (bootstrap[i]->init(&camera_driver.impl)) {
|
|
camera_driver.name = bootstrap[i]->name;
|
|
camera_driver.desc = bootstrap[i]->desc;
|
|
initialized = SDL_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
|
|
}
|
|
|
|
SDL_free(driver_name_copy);
|
|
} else {
|
|
for (int i = 0; !initialized && bootstrap[i]; i++) {
|
|
if (bootstrap[i]->demand_only) {
|
|
continue;
|
|
}
|
|
|
|
tried_to_init = SDL_TRUE;
|
|
SDL_zero(camera_driver);
|
|
#if 0 // !!! FIXME
|
|
camera_driver.pending_events_tail = &camera_driver.pending_events;
|
|
#endif
|
|
if (bootstrap[i]->init(&camera_driver.impl)) {
|
|
camera_driver.name = bootstrap[i]->name;
|
|
camera_driver.desc = bootstrap[i]->desc;
|
|
initialized = SDL_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!initialized) {
|
|
// specific drivers will set the error message if they fail, but otherwise we do it here.
|
|
if (!tried_to_init) {
|
|
if (driver_name) {
|
|
SDL_SetError("Camera driver '%s' not available", driver_name);
|
|
} else {
|
|
SDL_SetError("No available camera driver");
|
|
}
|
|
}
|
|
|
|
SDL_zero(camera_driver);
|
|
return -1; // No driver was available, so fail.
|
|
}
|
|
|
|
CompleteCameraEntryPoints();
|
|
|
|
// Make sure we have a list of devices available at startup...
|
|
camera_driver.impl.DetectDevices();
|
|
|
|
return 0;
|
|
}
|
|
|