camera: Made a pass over all the sources, cleaning up for SDL3 style, etc.

main
Ryan C. Gordon 2023-11-28 23:03:19 -05:00
parent 7ae955ce68
commit 2ad44bd162
6 changed files with 539 additions and 763 deletions

View File

@ -20,8 +20,6 @@
*/ */
#include "SDL_internal.h" #include "SDL_internal.h"
#include "SDL3/SDL.h"
#include "SDL3/SDL_camera.h"
#include "SDL_syscamera.h" #include "SDL_syscamera.h"
#include "SDL_camera_c.h" #include "SDL_camera_c.h"
#include "../video/SDL_pixels_c.h" #include "../video/SDL_pixels_c.h"
@ -29,16 +27,16 @@
#define DEBUG_CAMERA 1 #define DEBUG_CAMERA 1
/* list node entries to share frames between SDL and user app */ // list node entries to share frames between SDL and user app
// !!! FIXME: do we need this struct?
typedef struct entry_t typedef struct entry_t
{ {
SDL_CameraFrame frame; SDL_CameraFrame frame;
} entry_t; } entry_t;
static SDL_CameraDevice *open_devices[16]; static SDL_CameraDevice *open_devices[16]; // !!! FIXME: remove limit
static void static void CloseCameraDevice(SDL_CameraDevice *device)
close_device(SDL_CameraDevice *device)
{ {
if (!device) { if (!device) {
return; return;
@ -57,27 +55,23 @@ close_device(SDL_CameraDevice *device)
SDL_DestroyMutex(device->acquiring_lock); SDL_DestroyMutex(device->acquiring_lock);
} }
{ const int n = SDL_arraysize(open_devices);
int i, n = SDL_arraysize(open_devices); for (int i = 0; i < n; i++) {
for (i = 0; i < n; i++) { if (open_devices[i] == device) {
if (open_devices[i] == device) { open_devices[i] = NULL;
open_devices[i] = NULL;
}
} }
} }
{ entry_t *entry = NULL;
entry_t *entry = NULL; while (device->buffer_queue != NULL) {
while (device->buffer_queue != NULL) { SDL_ListPop(&device->buffer_queue, (void**)&entry);
SDL_ListPop(&device->buffer_queue, (void**)&entry); if (entry) {
if (entry) { SDL_CameraFrame f = entry->frame;
SDL_CameraFrame f = entry->frame; // Release frames not acquired, if any
/* Release frames not acquired, if any */ if (f.timestampNS) {
if (f.timestampNS) { ReleaseFrame(device, &f);
ReleaseFrame(device, &f);
}
SDL_free(entry);
} }
SDL_free(entry);
} }
} }
@ -87,12 +81,12 @@ close_device(SDL_CameraDevice *device)
SDL_free(device); SDL_free(device);
} }
/* Tell if all device are closed */ // Tell if all devices are closed
SDL_bool check_all_device_closed(void) SDL_bool CheckAllDeviceClosed(void)
{ {
int i, n = SDL_arraysize(open_devices); const int n = SDL_arraysize(open_devices);
int all_closed = SDL_TRUE; int all_closed = SDL_TRUE;
for (i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (open_devices[i]) { if (open_devices[i]) {
all_closed = SDL_FALSE; all_closed = SDL_FALSE;
break; break;
@ -101,11 +95,11 @@ SDL_bool check_all_device_closed(void)
return all_closed; return all_closed;
} }
/* Tell if at least one device is in playing state */ // Tell if at least one device is in playing state
SDL_bool check_device_playing(void) SDL_bool CheckDevicePlaying(void)
{ {
int i, n = SDL_arraysize(open_devices); const int n = SDL_arraysize(open_devices);
for (i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (open_devices[i]) { if (open_devices[i]) {
if (SDL_GetCameraStatus(open_devices[i]) == SDL_CAMERA_PLAYING) { if (SDL_GetCameraStatus(open_devices[i]) == SDL_CAMERA_PLAYING) {
return SDL_TRUE; return SDL_TRUE;
@ -115,35 +109,26 @@ SDL_bool check_device_playing(void)
return SDL_FALSE; return SDL_FALSE;
} }
void void SDL_CloseCamera(SDL_CameraDevice *device)
SDL_CloseCamera(SDL_CameraDevice *device)
{ {
if (!device) { if (!device) {
SDL_InvalidParamError("device"); SDL_InvalidParamError("device");
return; } else {
CloseCameraDevice(device);
} }
close_device(device);
} }
int int SDL_StartCamera(SDL_CameraDevice *device)
SDL_StartCamera(SDL_CameraDevice *device)
{ {
SDL_CameraStatus status;
int result;
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (device->is_spec_set == SDL_FALSE) {
if (device->is_spec_set == SDL_FALSE) {
return SDL_SetError("no spec set"); return SDL_SetError("no spec set");
} } else if (SDL_GetCameraStatus(device) != SDL_CAMERA_INIT) {
status = SDL_GetCameraStatus(device);
if (status != SDL_CAMERA_INIT) {
return SDL_SetError("invalid state"); return SDL_SetError("invalid state");
} }
result = StartCamera(device); const int result = StartCamera(device);
if (result < 0) { if (result < 0) {
return result; return result;
} }
@ -153,34 +138,23 @@ SDL_StartCamera(SDL_CameraDevice *device)
return 0; return 0;
} }
int int SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec)
SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec)
{ {
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (!spec) {
if (!spec) {
return SDL_InvalidParamError("spec"); return SDL_InvalidParamError("spec");
} }
SDL_zerop(spec); SDL_zerop(spec);
return GetDeviceSpec(device, spec); return GetDeviceSpec(device, spec);
} }
int int SDL_StopCamera(SDL_CameraDevice *device)
SDL_StopCamera(SDL_CameraDevice *device)
{ {
SDL_CameraStatus status;
int ret;
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (SDL_GetCameraStatus(device) != SDL_CAMERA_PLAYING) {
status = SDL_GetCameraStatus(device);
if (status != SDL_CAMERA_PLAYING) {
return SDL_SetError("invalid state"); return SDL_SetError("invalid state");
} }
@ -188,104 +162,88 @@ SDL_StopCamera(SDL_CameraDevice *device)
SDL_AtomicSet(&device->shutdown, 1); SDL_AtomicSet(&device->shutdown, 1);
SDL_LockMutex(device->acquiring_lock); SDL_LockMutex(device->acquiring_lock);
ret = StopCamera(device); const int retval = StopCamera(device);
SDL_UnlockMutex(device->acquiring_lock); SDL_UnlockMutex(device->acquiring_lock);
if (ret < 0) { return (retval < 0) ? -1 : 0;
return -1;
}
return 0;
} }
/* Check spec has valid format and frame size */ // Check spec has valid format and frame size
static int static int prepare_cameraspec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
prepare_cameraspec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
{ {
/* Check format */ // Check format
{ const int numfmts = SDL_GetNumCameraFormats(device);
int i, num = SDL_GetNumCameraFormats(device); SDL_bool is_format_valid = SDL_FALSE;
int is_format_valid = 0;
for (i = 0; i < num; i++) { for (int i = 0; i < numfmts; i++) {
Uint32 format; Uint32 format;
if (SDL_GetCameraFormat(device, i, &format) == 0) { if (SDL_GetCameraFormat(device, i, &format) == 0) {
if (format == desired->format && format != SDL_PIXELFORMAT_UNKNOWN) { if (format == desired->format && format != SDL_PIXELFORMAT_UNKNOWN) {
is_format_valid = 1; is_format_valid = SDL_TRUE;
obtained->format = format; obtained->format = format;
break; break;
}
} }
} }
}
if (!is_format_valid) { if (!is_format_valid) {
if (allowed_changes) { if (allowed_changes) {
for (i = 0; i < num; i++) { for (int i = 0; i < numfmts; i++) {
Uint32 format; Uint32 format;
if (SDL_GetCameraFormat(device, i, &format) == 0) { if (SDL_GetCameraFormat(device, i, &format) == 0) {
if (format != SDL_PIXELFORMAT_UNKNOWN) { if (format != SDL_PIXELFORMAT_UNKNOWN) {
obtained->format = format; obtained->format = format;
is_format_valid = 1; is_format_valid = SDL_TRUE;
break; break;
}
} }
} }
} else {
SDL_SetError("Not allowed to change the format");
return -1;
} }
} } else {
return SDL_SetError("Not allowed to change the format");
if (!is_format_valid) {
SDL_SetError("Invalid format");
return -1;
} }
} }
/* Check frame size */ if (!is_format_valid) {
{ return SDL_SetError("Invalid format");
int i, num = SDL_GetNumCameraFrameSizes(device, obtained->format); }
int is_framesize_valid = 0;
for (i = 0; i < num; i++) { // 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; int w, h;
if (SDL_GetCameraFrameSize(device, obtained->format, i, &w, &h) == 0) { if (SDL_GetCameraFrameSize(device, obtained->format, 0, &w, &h) == 0) {
if (desired->width == w && desired->height == h) { is_framesize_valid = SDL_TRUE;
is_framesize_valid = 1; obtained->width = w;
obtained->width = w; obtained->height = h;
obtained->height = h;
break;
}
} }
} else {
return SDL_SetError("Not allowed to change the frame size");
} }
}
if (!is_framesize_valid) { if (!is_framesize_valid) {
if (allowed_changes) { return SDL_SetError("Invalid frame size");
int w, h;
if (SDL_GetCameraFrameSize(device, obtained->format, 0, &w, &h) == 0) {
is_framesize_valid = 1;
obtained->width = w;
obtained->height = h;
}
} else {
SDL_SetError("Not allowed to change the frame size");
return -1;
}
}
if (!is_framesize_valid) {
SDL_SetError("Invalid frame size");
return -1;
}
} }
return 0; return 0;
} }
const char * const char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id)
SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id)
{ {
static char buf[256]; static char buf[256];
buf[0] = 0; buf[0] = 0;
@ -299,47 +257,41 @@ SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id)
if (GetCameraDeviceName(instance_id, buf, sizeof (buf)) < 0) { if (GetCameraDeviceName(instance_id, buf, sizeof (buf)) < 0) {
buf[0] = 0; buf[0] = 0;
} }
return buf; return buf;
} }
SDL_CameraDeviceID * SDL_CameraDeviceID *SDL_GetCameraDevices(int *count)
SDL_GetCameraDevices(int *count)
{ {
int dummycount = 0;
int num = 0; if (!count) {
SDL_CameraDeviceID *ret = GetCameraDevices(&num); count = &dummycount;
if (ret) {
if (count) {
*count = num;
}
return ret;
} }
/* return list of 0 ID, null terminated */ int num = 0;
num = 0; SDL_CameraDeviceID *retval = GetCameraDevices(&num);
ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (retval) {
*count = num;
return retval;
}
if (ret == NULL) { // return list of 0 ID, null terminated
retval = (SDL_CameraDeviceID *)SDL_calloc(1, sizeof(*retval));
if (retval == NULL) {
SDL_OutOfMemory(); SDL_OutOfMemory();
if (count) { *count = 0;
*count = 0;
}
return NULL; return NULL;
} }
ret[num] = 0; retval[0] = 0;
if (count) { *count = 0;
*count = num;
}
return ret; return retval;
} }
/* Camera thread function */ // Camera thread function
static int SDLCALL static int SDLCALL SDL_CameraThread(void *devicep)
SDL_CameraThread(void *devicep)
{ {
const int delay = 20; const int delay = 20;
SDL_CameraDevice *device = (SDL_CameraDevice *) devicep; SDL_CameraDevice *device = (SDL_CameraDevice *) devicep;
@ -358,23 +310,23 @@ SDL_CameraThread(void *devicep)
Android_JNI_CameraSetThreadPriority(device->iscapture, device); Android_JNI_CameraSetThreadPriority(device->iscapture, device);
}*/ }*/
#else #else
/* The camera capture is always a high priority thread */ // The camera capture is always a high priority thread
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
#endif #endif
/* Perform any thread setup */ // Perform any thread setup
device->threadid = SDL_GetCurrentThreadID(); device->threadid = SDL_GetCurrentThreadID();
/* Init state */ // Init state
// !!! FIXME: use a semaphore or something
while (!SDL_AtomicGet(&device->enabled)) { while (!SDL_AtomicGet(&device->enabled)) {
SDL_Delay(delay); SDL_Delay(delay);
} }
/* Loop, filling the camera buffers */ // Loop, filling the camera buffers
while (!SDL_AtomicGet(&device->shutdown)) { while (!SDL_AtomicGet(&device->shutdown)) {
SDL_CameraFrame f; SDL_CameraFrame f;
int ret; int ret;
entry_t *entry;
SDL_zero(f); SDL_zero(f);
@ -389,7 +341,7 @@ SDL_CameraThread(void *devicep)
} }
if (ret < 0) { if (ret < 0) {
/* Flag it as an error */ // Flag it as an error
#if DEBUG_CAMERA #if DEBUG_CAMERA
SDL_Log("dev[%p] error AcquireFrame: %d %s", (void *)device, ret, SDL_GetError()); SDL_Log("dev[%p] error AcquireFrame: %d %s", (void *)device, ret, SDL_GetError());
#endif #endif
@ -397,7 +349,7 @@ SDL_CameraThread(void *devicep)
} }
entry = SDL_malloc(sizeof (entry_t)); entry_t *entry = SDL_malloc(sizeof (entry_t));
if (entry == NULL) { if (entry == NULL) {
goto error_mem; goto error_mem;
} }
@ -424,26 +376,25 @@ error_mem:
SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError()); SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError());
#endif #endif
SDL_AtomicSet(&device->shutdown, 1); SDL_AtomicSet(&device->shutdown, 1);
SDL_OutOfMemory(); SDL_OutOfMemory(); // !!! FIXME: this error isn't accessible since the thread is about to terminate
return 0; return 0;
} }
SDL_CameraDevice * SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id)
SDL_OpenCamera(SDL_CameraDeviceID instance_id)
{ {
int i, n = SDL_arraysize(open_devices); const int n = SDL_arraysize(open_devices);
int id = -1;
SDL_CameraDevice *device = NULL; SDL_CameraDevice *device = NULL;
const char *device_name = NULL; const char *device_name = NULL;
int id = -1;
if (!SDL_WasInit(SDL_INIT_VIDEO)) { if (!SDL_WasInit(SDL_INIT_VIDEO)) {
SDL_SetError("Video subsystem is not initialized"); SDL_SetError("Video subsystem is not initialized");
goto error; goto error;
} }
/* !!! FIXME: there is a race condition here if two devices open from two threads at once. */ // !!! FIXME: there is a race condition here if two devices open from two threads at once.
/* Find an available device ID... */ // Find an available device ID...
for (i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (open_devices[i] == NULL) { if (open_devices[i] == NULL) {
id = i; id = i;
break; break;
@ -470,7 +421,7 @@ SDL_OpenCamera(SDL_CameraDeviceID instance_id)
#if 0 #if 0
// FIXME do we need this ? // FIXME do we need this ?
/* Let the user override. */ // Let the user override.
{ {
const char *dev = SDL_getenv("SDL_CAMERA_DEVICE_NAME"); const char *dev = SDL_getenv("SDL_CAMERA_DEVICE_NAME");
if (dev && dev[0]) { if (dev && dev[0]) {
@ -490,7 +441,6 @@ SDL_OpenCamera(SDL_CameraDeviceID instance_id)
} }
device->dev_name = SDL_strdup(device_name); device->dev_name = SDL_strdup(device_name);
SDL_AtomicSet(&device->shutdown, 0); SDL_AtomicSet(&device->shutdown, 0);
SDL_AtomicSet(&device->enabled, 0); SDL_AtomicSet(&device->enabled, 0);
@ -510,37 +460,28 @@ SDL_OpenCamera(SDL_CameraDeviceID instance_id)
goto error; goto error;
} }
/* empty */ // empty
device->buffer_queue = NULL; device->buffer_queue = NULL;
open_devices[id] = device; /* add it to our list of open devices. */ open_devices[id] = device; // add it to our list of open devices.
/* Start the camera thread */ // Start the camera thread
{ char threadname[64];
const size_t stacksize = 64 * 1024; SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", id);
char threadname[64]; device->thread = SDL_CreateThreadInternal(SDL_CameraThread, threadname, 0, device);
if (device->thread == NULL) {
SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", id); SDL_SetError("Couldn't create camera thread");
device->thread = SDL_CreateThreadInternal(SDL_CameraThread, threadname, stacksize, device); goto error;
if (device->thread == NULL) {
SDL_SetError("Couldn't create camera thread");
goto error;
}
} }
return device; return device;
error: error:
close_device(device); CloseCameraDevice(device);
return NULL; return NULL;
} }
int int SDL_SetCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
SDL_SetCameraSpec(SDL_CameraDevice *device,
const SDL_CameraSpec *desired,
SDL_CameraSpec *obtained,
int allowed_changes)
{ {
SDL_CameraSpec _obtained; SDL_CameraSpec _obtained;
SDL_CameraSpec _desired; SDL_CameraSpec _desired;
@ -548,9 +489,7 @@ SDL_SetCameraSpec(SDL_CameraDevice *device,
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (device->is_spec_set == SDL_TRUE) {
if (device->is_spec_set == SDL_TRUE) {
return SDL_SetError("already configured"); return SDL_SetError("already configured");
} }
@ -559,7 +498,7 @@ SDL_SetCameraSpec(SDL_CameraDevice *device,
desired = &_desired; desired = &_desired;
allowed_changes = SDL_CAMERA_ALLOW_ANY_CHANGE; allowed_changes = SDL_CAMERA_ALLOW_ANY_CHANGE;
} else { } else {
/* in case desired == obtained */ // in case desired == obtained
_desired = *desired; _desired = *desired;
desired = &_desired; desired = &_desired;
} }
@ -588,14 +527,11 @@ SDL_SetCameraSpec(SDL_CameraDevice *device,
return 0; return 0;
} }
int int SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
{ {
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (!frame) {
if (!frame) {
return SDL_InvalidParamError("frame"); return SDL_InvalidParamError("frame");
} }
@ -604,7 +540,7 @@ SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
if (device->thread == NULL) { if (device->thread == NULL) {
int ret; int ret;
/* Wait for a frame */ // Wait for a frame
while ((ret = AcquireFrame(device, frame)) == 0) { while ((ret = AcquireFrame(device, frame)) == 0) {
if (frame->num_planes) { if (frame->num_planes) {
return 0; return 0;
@ -622,42 +558,33 @@ SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
*frame = entry->frame; *frame = entry->frame;
SDL_free(entry); SDL_free(entry);
/* Error from thread */ // Error from thread
if (frame->num_planes == 0 && frame->timestampNS == 0) { if (frame->num_planes == 0 && frame->timestampNS == 0) {
return SDL_SetError("error from acquisition thread"); return SDL_SetError("error from acquisition thread");
} }
} else { } else {
/* Queue is empty. Not an error. */ // Queue is empty. Not an error.
} }
} }
return 0; return 0;
} }
int int SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame)
{ {
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (frame == NULL) {
if (frame == NULL) {
return SDL_InvalidParamError("frame"); return SDL_InvalidParamError("frame");
} } else if (ReleaseFrame(device, frame) < 0) {
if (ReleaseFrame(device, frame) < 0) {
return -1; return -1;
} }
SDL_zerop(frame); SDL_zerop(frame);
return 0; return 0;
} }
int int SDL_GetNumCameraFormats(SDL_CameraDevice *device)
SDL_GetNumCameraFormats(SDL_CameraDevice *device)
{ {
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
@ -665,21 +592,18 @@ SDL_GetNumCameraFormats(SDL_CameraDevice *device)
return GetNumFormats(device); return GetNumFormats(device);
} }
int int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format)
SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format)
{ {
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (!format) {
if (!format) {
return SDL_InvalidParamError("format"); return SDL_InvalidParamError("format");
} }
*format = 0; *format = 0;
return GetFormat(device, index, format); return GetFormat(device, index, format);
} }
int int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format)
SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format)
{ {
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
@ -687,29 +611,20 @@ SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format)
return GetNumFrameSizes(device, format); return GetNumFrameSizes(device, format);
} }
int int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height)
SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height)
{ {
if (!device) { if (!device) {
return SDL_InvalidParamError("device"); return SDL_InvalidParamError("device");
} } else if (!width) {
if (!width) {
return SDL_InvalidParamError("width"); return SDL_InvalidParamError("width");
} } else if (!height) {
if (!height) {
return SDL_InvalidParamError("height"); return SDL_InvalidParamError("height");
} }
*width = 0; *width = *height = 0;
*height = 0;
return GetFrameSize(device, format, index, width, height); return GetFrameSize(device, format, index, width, height);
} }
SDL_CameraDevice * SDL_CameraDevice *SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes)
SDL_OpenCameraWithSpec(
SDL_CameraDeviceID instance_id,
const SDL_CameraSpec *desired,
SDL_CameraSpec *obtained,
int allowed_changes)
{ {
SDL_CameraDevice *device; SDL_CameraDevice *device;
@ -724,42 +639,32 @@ SDL_OpenCameraWithSpec(
return device; return device;
} }
SDL_CameraStatus SDL_CameraStatus SDL_GetCameraStatus(SDL_CameraDevice *device)
SDL_GetCameraStatus(SDL_CameraDevice *device)
{ {
if (device == NULL) { if (device == NULL) {
return SDL_CAMERA_INIT; return SDL_CAMERA_INIT;
} } else if (device->is_spec_set == SDL_FALSE) {
if (device->is_spec_set == SDL_FALSE) {
return SDL_CAMERA_INIT; return SDL_CAMERA_INIT;
} } else if (SDL_AtomicGet(&device->shutdown)) {
if (SDL_AtomicGet(&device->shutdown)) {
return SDL_CAMERA_STOPPED; return SDL_CAMERA_STOPPED;
} } else if (SDL_AtomicGet(&device->enabled)) {
if (SDL_AtomicGet(&device->enabled)) {
return SDL_CAMERA_PLAYING; return SDL_CAMERA_PLAYING;
} }
return SDL_CAMERA_INIT; return SDL_CAMERA_INIT;
} }
int int SDL_CameraInit(void)
SDL_CameraInit(void)
{ {
SDL_zeroa(open_devices); SDL_zeroa(open_devices);
SDL_SYS_CameraInit(); SDL_SYS_CameraInit();
return 0; return 0;
} }
void void SDL_QuitCamera(void)
SDL_QuitCamera(void)
{ {
int i, n = SDL_arraysize(open_devices); const int n = SDL_arraysize(open_devices);
for (i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
close_device(open_devices[i]); CloseCameraDevice(open_devices[i]);
} }
SDL_zeroa(open_devices); SDL_zeroa(open_devices);

View File

@ -23,10 +23,10 @@
#ifndef SDL_camera_c_h_ #ifndef SDL_camera_c_h_
#define SDL_camera_c_h_ #define SDL_camera_c_h_
/* Initialize the camera subsystem */ // Initialize the camera subsystem
int SDL_CameraInit(void); int SDL_CameraInit(void);
/* Shutdown the camera subsystem */ // Shutdown the camera subsystem
void SDL_QuitCamera(void); void SDL_QuitCamera(void);
#endif /* SDL_camera_c_h_ */ #endif // SDL_camera_c_h_

View File

@ -25,45 +25,43 @@
#include "../SDL_list.h" #include "../SDL_list.h"
/* The SDL camera driver */ // The SDL camera driver
typedef struct SDL_CameraDevice SDL_CameraDevice; typedef struct SDL_CameraDevice SDL_CameraDevice;
/* Define the SDL camera driver structure */ // Define the SDL camera driver structure
struct SDL_CameraDevice struct SDL_CameraDevice
{ {
/* * * */ // The device's current camera specification
/* Data common to all devices */
/* The device's current camera specification */
SDL_CameraSpec spec; SDL_CameraSpec spec;
/* Device name */ // Device name
char *dev_name; char *dev_name;
/* Current state flags */ // Current state flags
SDL_AtomicInt shutdown; SDL_AtomicInt shutdown;
SDL_AtomicInt enabled; SDL_AtomicInt enabled;
SDL_bool is_spec_set; SDL_bool is_spec_set;
/* A mutex for locking the queue buffers */ // A mutex for locking the queue buffers
SDL_Mutex *device_lock; SDL_Mutex *device_lock;
SDL_Mutex *acquiring_lock; SDL_Mutex *acquiring_lock;
/* A thread to feed the camera device */ // A thread to feed the camera device
SDL_Thread *thread; SDL_Thread *thread;
SDL_ThreadID threadid; SDL_ThreadID threadid;
/* Queued buffers (if app not using callback). */ // Queued buffers (if app not using callback).
SDL_ListNode *buffer_queue; SDL_ListNode *buffer_queue;
/* * * */ // Data private to this driver
/* Data private to this driver */
struct SDL_PrivateCameraData *hidden; struct SDL_PrivateCameraData *hidden;
}; };
extern int SDL_SYS_CameraInit(void); extern int SDL_SYS_CameraInit(void);
extern int SDL_SYS_CameraQuit(void); extern int SDL_SYS_CameraQuit(void);
// !!! FIXME: These names need to be made camera-specific.
extern int OpenDevice(SDL_CameraDevice *_this); extern int OpenDevice(SDL_CameraDevice *_this);
extern void CloseDevice(SDL_CameraDevice *_this); extern void CloseDevice(SDL_CameraDevice *_this);
@ -86,7 +84,7 @@ extern int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *
extern int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size); extern int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size);
extern SDL_CameraDeviceID *GetCameraDevices(int *count); extern SDL_CameraDeviceID *GetCameraDevices(int *count);
extern SDL_bool check_all_device_closed(void); extern SDL_bool CheckAllDeviceClosed(void);
extern SDL_bool check_device_playing(void); extern SDL_bool CheckDevicePlaying(void);
#endif /* SDL_syscamera_h_ */ #endif // SDL_syscamera_h_

View File

@ -62,8 +62,7 @@
static ACameraManager *cameraMgr = NULL; static ACameraManager *cameraMgr = NULL;
static ACameraIdList *cameraIdList = NULL; static ACameraIdList *cameraIdList = NULL;
static void static void create_cameraMgr(void)
create_cameraMgr(void)
{ {
if (cameraMgr == NULL) { if (cameraMgr == NULL) {
#if 0 // !!! FIXME: this is getting replaced in a different branch. #if 0 // !!! FIXME: this is getting replaced in a different branch.
@ -81,8 +80,7 @@ create_cameraMgr(void)
} }
} }
static void static void delete_cameraMgr(void)
delete_cameraMgr(void)
{ {
if (cameraIdList) { if (cameraIdList) {
ACameraManager_deleteCameraIdList(cameraIdList); ACameraManager_deleteCameraIdList(cameraIdList);
@ -104,50 +102,46 @@ struct SDL_PrivateCameraData
ACaptureSessionOutputContainer *sessionOutputContainer; ACaptureSessionOutputContainer *sessionOutputContainer;
AImageReader *reader; AImageReader *reader;
int num_formats; int num_formats;
int count_formats[6]; // see format_2_id int count_formats[6]; // see format_to_id
}; };
/**/
#define FORMAT_SDL SDL_PIXELFORMAT_NV12 #define FORMAT_SDL SDL_PIXELFORMAT_NV12
static int static int format_to_id(int fmt) {
format_2_id(int fmt) {
switch (fmt) { switch (fmt) {
#define CASE(x, y) case x: return y #define CASE(x, y) case x: return y
CASE(FORMAT_SDL, 0); CASE(FORMAT_SDL, 0);
CASE(SDL_PIXELFORMAT_RGB565, 1); CASE(SDL_PIXELFORMAT_RGB565, 1);
CASE(SDL_PIXELFORMAT_XRGB8888, 2); CASE(SDL_PIXELFORMAT_XRGB8888, 2);
CASE(SDL_PIXELFORMAT_RGBA8888, 3); CASE(SDL_PIXELFORMAT_RGBA8888, 3);
CASE(SDL_PIXELFORMAT_RGBX8888, 4); CASE(SDL_PIXELFORMAT_RGBX8888, 4);
CASE(SDL_PIXELFORMAT_UNKNOWN, 5); CASE(SDL_PIXELFORMAT_UNKNOWN, 5);
#undef CASE #undef CASE
default: default:
return 5; return 5;
} }
} }
static int static int id_to_format(int fmt) {
id_2_format(int fmt) {
switch (fmt) { switch (fmt) {
#define CASE(x, y) case y: return x #define CASE(x, y) case y: return x
CASE(FORMAT_SDL, 0); CASE(FORMAT_SDL, 0);
CASE(SDL_PIXELFORMAT_RGB565, 1); CASE(SDL_PIXELFORMAT_RGB565, 1);
CASE(SDL_PIXELFORMAT_XRGB8888, 2); CASE(SDL_PIXELFORMAT_XRGB8888, 2);
CASE(SDL_PIXELFORMAT_RGBA8888, 3); CASE(SDL_PIXELFORMAT_RGBA8888, 3);
CASE(SDL_PIXELFORMAT_RGBX8888, 4); CASE(SDL_PIXELFORMAT_RGBX8888, 4);
CASE(SDL_PIXELFORMAT_UNKNOWN, 5); CASE(SDL_PIXELFORMAT_UNKNOWN, 5);
#undef CASE #undef CASE
default: default:
return SDL_PIXELFORMAT_UNKNOWN; return SDL_PIXELFORMAT_UNKNOWN;
} }
} }
static Uint32 static Uint32 format_android_to_sdl(Uint32 fmt)
format_android_2_sdl(Uint32 fmt)
{ {
switch (fmt) { switch (fmt) {
#define CASE(x, y) case x: return y #define CASE(x, y) case x: return y
CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL); CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL);
CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565); CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565);
CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888); CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888);
@ -157,71 +151,72 @@ format_android_2_sdl(Uint32 fmt)
CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_UNKNOWN); // 64bits CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_UNKNOWN); // 64bits
CASE(AIMAGE_FORMAT_RAW_PRIVATE, SDL_PIXELFORMAT_UNKNOWN); CASE(AIMAGE_FORMAT_RAW_PRIVATE, SDL_PIXELFORMAT_UNKNOWN);
CASE(AIMAGE_FORMAT_JPEG, SDL_PIXELFORMAT_UNKNOWN); CASE(AIMAGE_FORMAT_JPEG, SDL_PIXELFORMAT_UNKNOWN);
#undef CASE #undef CASE
default: default:
SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt); SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt);
return SDL_PIXELFORMAT_UNKNOWN; return SDL_PIXELFORMAT_UNKNOWN;
} }
} }
static Uint32 static Uint32 format_sdl_to_android(Uint32 fmt)
format_sdl_2_android(Uint32 fmt)
{ {
switch (fmt) { switch (fmt) {
#define CASE(x, y) case y: return x #define CASE(x, y) case y: return x
CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL); CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL);
CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565); CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565);
CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888); CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888);
CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888); CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888);
CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888); CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888);
#undef CASE #undef CASE
default: default:
return 0; return 0;
} }
} }
static void static void onDisconnected(void *context, ACameraDevice *device)
onDisconnected(void *context, ACameraDevice *device)
{ {
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context; // SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
#if DEBUG_CAMERA
SDL_Log("CB onDisconnected"); SDL_Log("CB onDisconnected");
#endif
} }
static void static void onError(void *context, ACameraDevice *device, int error)
onError(void *context, ACameraDevice *device, int error)
{ {
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context; // SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
#if DEBUG_CAMERA
SDL_Log("CB onError"); SDL_Log("CB onError");
#endif
} }
static void static void onClosed(void* context, ACameraCaptureSession *session)
onClosed(void* context, ACameraCaptureSession *session)
{ {
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context; // SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
#if DEBUG_CAMERA
SDL_Log("CB onClosed"); SDL_Log("CB onClosed");
#endif
} }
static void static void onReady(void* context, ACameraCaptureSession *session)
onReady(void* context, ACameraCaptureSession *session)
{ {
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context; // SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
#if DEBUG_CAMERA
SDL_Log("CB onReady"); SDL_Log("CB onReady");
#endif
} }
static void static void onActive(void* context, ACameraCaptureSession *session)
onActive(void* context, ACameraCaptureSession *session)
{ {
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context; // SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
#if DEBUG_CAMERA
SDL_Log("CB onActive"); SDL_Log("CB onActive");
#endif
} }
int int OpenDevice(SDL_CameraDevice *_this)
OpenDevice(SDL_CameraDevice *_this)
{ {
camera_status_t res;
/* Cannot open a second camera, while the first one is opened. /* Cannot open a second camera, while the first one is opened.
* If you want to play several camera, they must all be opened first, then played. * If you want to play several camera, they must all be opened first, then played.
* *
@ -230,7 +225,7 @@ OpenDevice(SDL_CameraDevice *_this)
* before configuring sessions on any of the camera devices. * " * before configuring sessions on any of the camera devices. * "
* *
*/ */
if (check_device_playing()) { if (CheckDevicePlaying()) {
return SDL_SetError("A camera is already playing"); return SDL_SetError("A camera is already playing");
} }
@ -245,7 +240,7 @@ OpenDevice(SDL_CameraDevice *_this)
_this->hidden->dev_callbacks.onDisconnected = onDisconnected; _this->hidden->dev_callbacks.onDisconnected = onDisconnected;
_this->hidden->dev_callbacks.onError = onError; _this->hidden->dev_callbacks.onError = onError;
res = ACameraManager_openCamera(cameraMgr, _this->dev_name, &_this->hidden->dev_callbacks, &_this->hidden->device); camera_status_t res = ACameraManager_openCamera(cameraMgr, _this->dev_name, &_this->hidden->dev_callbacks, &_this->hidden->device);
if (res != ACAMERA_OK) { if (res != ACAMERA_OK) {
return SDL_SetError("Failed to open camera"); return SDL_SetError("Failed to open camera");
} }
@ -253,8 +248,7 @@ OpenDevice(SDL_CameraDevice *_this)
return 0; return 0;
} }
void void CloseDevice(SDL_CameraDevice *_this)
CloseDevice(SDL_CameraDevice *_this)
{ {
if (_this && _this->hidden) { if (_this && _this->hidden) {
if (_this->hidden->session) { if (_this->hidden->session) {
@ -278,13 +272,13 @@ CloseDevice(SDL_CameraDevice *_this)
_this->hidden = NULL; _this->hidden = NULL;
} }
if (check_all_device_closed()) { // !!! FIXME: just refcount this?
if (CheckAllDeviceClosed()) {
delete_cameraMgr(); delete_cameraMgr();
} }
} }
int int InitDevice(SDL_CameraDevice *_this)
InitDevice(SDL_CameraDevice *_this)
{ {
size_t size, pitch; size_t size, pitch;
SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE); SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE);
@ -292,19 +286,19 @@ InitDevice(SDL_CameraDevice *_this)
return 0; return 0;
} }
int int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
{ {
// !!! FIXME: catch NULLs at higher level
if (spec) { if (spec) {
*spec = _this->spec; SDL_copyp(spec, &_this->spec);
return 0; return 0;
} }
return -1; return -1;
} }
int int StartCamera(SDL_CameraDevice *_this)
StartCamera(SDL_CameraDevice *_this)
{ {
// !!! FIXME: maybe log the error code in SDL_SetError
camera_status_t res; camera_status_t res;
media_status_t res2; media_status_t res2;
ANativeWindow *window = NULL; ANativeWindow *window = NULL;
@ -312,7 +306,7 @@ StartCamera(SDL_CameraDevice *_this)
ACameraOutputTarget *outputTarget; ACameraOutputTarget *outputTarget;
ACaptureRequest *request; ACaptureRequest *request;
res2 = AImageReader_new(_this->spec.width, _this->spec.height, format_sdl_2_android(_this->spec.format), 10 /* nb buffers */, &_this->hidden->reader); res2 = AImageReader_new(_this->spec.width, _this->spec.height, format_sdl_to_android(_this->spec.format), 10 /* nb buffers */, &_this->hidden->reader);
if (res2 != AMEDIA_OK) { if (res2 != AMEDIA_OK) {
SDL_SetError("Error AImageReader_new"); SDL_SetError("Error AImageReader_new");
goto error; goto error;
@ -321,10 +315,8 @@ StartCamera(SDL_CameraDevice *_this)
if (res2 != AMEDIA_OK) { if (res2 != AMEDIA_OK) {
SDL_SetError("Error AImageReader_new"); SDL_SetError("Error AImageReader_new");
goto error; goto error;
} }
res = ACaptureSessionOutput_create(window, &sessionOutput); res = ACaptureSessionOutput_create(window, &sessionOutput);
if (res != ACAMERA_OK) { if (res != ACAMERA_OK) {
SDL_SetError("Error ACaptureSessionOutput_create"); SDL_SetError("Error ACaptureSessionOutput_create");
@ -341,14 +333,12 @@ StartCamera(SDL_CameraDevice *_this)
goto error; goto error;
} }
res = ACameraOutputTarget_create(window, &outputTarget); res = ACameraOutputTarget_create(window, &outputTarget);
if (res != ACAMERA_OK) { if (res != ACAMERA_OK) {
SDL_SetError("Error ACameraOutputTarget_create"); SDL_SetError("Error ACameraOutputTarget_create");
goto error; goto error;
} }
res = ACameraDevice_createCaptureRequest(_this->hidden->device, TEMPLATE_RECORD, &request); res = ACameraDevice_createCaptureRequest(_this->hidden->device, TEMPLATE_RECORD, &request);
if (res != ACAMERA_OK) { if (res != ACAMERA_OK) {
SDL_SetError("Error ACameraDevice_createCaptureRequest"); SDL_SetError("Error ACameraDevice_createCaptureRequest");
@ -361,7 +351,6 @@ StartCamera(SDL_CameraDevice *_this)
goto error; goto error;
} }
_this->hidden->capture_callbacks.context = (void *) _this; _this->hidden->capture_callbacks.context = (void *) _this;
_this->hidden->capture_callbacks.onClosed = onClosed; _this->hidden->capture_callbacks.onClosed = onClosed;
_this->hidden->capture_callbacks.onReady = onReady; _this->hidden->capture_callbacks.onReady = onReady;
@ -388,16 +377,14 @@ error:
return -1; return -1;
} }
int int StopCamera(SDL_CameraDevice *_this)
StopCamera(SDL_CameraDevice *_this)
{ {
ACameraCaptureSession_close(_this->hidden->session); ACameraCaptureSession_close(_this->hidden->session);
_this->hidden->session = NULL; _this->hidden->session = NULL;
return 0; return 0;
} }
int int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{ {
media_status_t res; media_status_t res;
AImage *image; AImage *image;
@ -406,20 +393,17 @@ AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
res = AImageReader_acquireLatestImage(_this->hidden->reader, &image); res = AImageReader_acquireLatestImage(_this->hidden->reader, &image);
*/ */
if (res == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE ) { if (res == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE ) {
SDL_Delay(20); // TODO fix some delay SDL_Delay(20); // TODO fix some delay
#if DEBUG_CAMERA #if DEBUG_CAMERA
// SDL_Log("AImageReader_acquireNextImage: AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE"); //SDL_Log("AImageReader_acquireNextImage: AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE");
#endif #endif
return 0;
} else if (res == AMEDIA_OK ) { } else if (res == AMEDIA_OK ) {
int i = 0;
int32_t numPlanes = 0; int32_t numPlanes = 0;
AImage_getNumberOfPlanes(image, &numPlanes); AImage_getNumberOfPlanes(image, &numPlanes);
frame->timestampNS = SDL_GetTicksNS(); frame->timestampNS = SDL_GetTicksNS();
for (i = 0; i < numPlanes && i < 3; i++) { for (int i = 0; i < numPlanes && i < 3; i++) {
int dataLength = 0; int dataLength = 0;
int rowStride = 0; int rowStride = 0;
uint8_t *data = NULL; uint8_t *data = NULL;
@ -442,18 +426,16 @@ AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
} }
frame->internal = (void*)image; frame->internal = (void*)image;
return 0;
} else if (res == AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED) { } else if (res == AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED) {
SDL_SetError("AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED"); return SDL_SetError("AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED");
} else { } else {
SDL_SetError("AImageReader_acquireNextImage: %d", res); return SDL_SetError("AImageReader_acquireNextImage: %d", res);
} }
return -1; return 0;
} }
int int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{ {
if (frame->internal){ if (frame->internal){
AImage_delete((AImage *)frame->internal); AImage_delete((AImage *)frame->internal);
@ -461,12 +443,10 @@ ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
return 0; return 0;
} }
int int GetNumFormats(SDL_CameraDevice *_this)
GetNumFormats(SDL_CameraDevice *_this)
{ {
camera_status_t res; camera_status_t res;
int i; SDL_bool unknown = SDL_FALSE;
int unknown = 0;
ACameraMetadata *metadata; ACameraMetadata *metadata;
ACameraMetadata_const_entry entry; ACameraMetadata_const_entry entry;
@ -486,34 +466,33 @@ GetNumFormats(SDL_CameraDevice *_this)
SDL_Log("got entry ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS"); SDL_Log("got entry ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS");
for (i = 0; i < entry.count; i += 4) { for (int i = 0; i < entry.count; i += 4) {
int32_t format = entry.data.i32[i + 0]; const int32_t format = entry.data.i32[i + 0];
int32_t type = entry.data.i32[i + 3]; const int32_t type = entry.data.i32[i + 3];
Uint32 fmt;
if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
continue; continue;
} }
fmt = format_android_2_sdl(format); const Uint32 fmt = format_android_to_sdl(format);
_this->hidden->count_formats[format_2_id(fmt)] += 1; _this->hidden->count_formats[format_to_id(fmt)] += 1;
#if DEBUG_CAMERA #if DEBUG_CAMERA
if (fmt != SDL_PIXELFORMAT_UNKNOWN) { if (fmt != SDL_PIXELFORMAT_UNKNOWN) {
int w = entry.data.i32[i + 1]; int w = entry.data.i32[i + 1];
int h = entry.data.i32[i + 2]; int h = entry.data.i32[i + 2];
SDL_Log("Got format android 0x%08x -> %s %d x %d", format, SDL_GetPixelFormatName(fmt), w, h); SDL_Log("Got format android 0x%08x -> %s %d x %d", format, SDL_GetPixelFormatName(fmt), w, h);
} else { } else {
unknown += 1; unknown = SDL_TRUE;
} }
#endif #endif
} }
#if DEBUG_CAMERA #if DEBUG_CAMERA
if (unknown) { if (unknown) {
SDL_Log("Got unknown android"); SDL_Log("Got unknown android");
} }
#endif #endif
if ( _this->hidden->count_formats[0]) _this->hidden->num_formats += 1; if ( _this->hidden->count_formats[0]) _this->hidden->num_formats += 1;
@ -526,10 +505,8 @@ GetNumFormats(SDL_CameraDevice *_this)
return _this->hidden->num_formats; return _this->hidden->num_formats;
} }
int int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
{ {
int i;
int i2 = 0; int i2 = 0;
if (_this->hidden->num_formats == 0) { if (_this->hidden->num_formats == 0) {
@ -537,16 +514,17 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
} }
if (index < 0 || index >= _this->hidden->num_formats) { if (index < 0 || index >= _this->hidden->num_formats) {
// !!! FIXME: call SDL_SetError()?
return -1; return -1;
} }
for (i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { for (int i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) {
if (_this->hidden->count_formats[i] == 0) { if (_this->hidden->count_formats[i] == 0) {
continue; continue;
} }
if (i2 == index) { if (i2 == index) {
*format = id_2_format(i); *format = id_to_format(i);
} }
i2++; i2++;
@ -555,17 +533,17 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
return 0; return 0;
} }
int int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
{ {
int i, i2 = 0, index; // !!! FIXME: call SDL_SetError()?
if (_this->hidden->num_formats == 0) { if (_this->hidden->num_formats == 0) {
GetNumFormats(_this); GetNumFormats(_this);
} }
index = format_2_id(format); const int index = format_to_id(format);
for (i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { int i2 = 0;
for (int i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) {
if (_this->hidden->count_formats[i] == 0) { if (_this->hidden->count_formats[i] == 0) {
continue; continue;
} }
@ -581,11 +559,10 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
return -1; return -1;
} }
int int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
{ {
// !!! FIXME: call SDL_SetError()?
camera_status_t res; camera_status_t res;
int i, i2 = 0;
ACameraMetadata *metadata; ACameraMetadata *metadata;
ACameraMetadata_const_entry entry; ACameraMetadata_const_entry entry;
@ -603,19 +580,18 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
return -1; return -1;
} }
for (i = 0; i < entry.count; i += 4) { int i2 = 0;
for (int i = 0; i < entry.count; i += 4) {
int32_t f = entry.data.i32[i + 0]; int32_t f = entry.data.i32[i + 0];
int w = entry.data.i32[i + 1]; const int w = entry.data.i32[i + 1];
int h = entry.data.i32[i + 2]; const int h = entry.data.i32[i + 2];
int32_t type = entry.data.i32[i + 3]; int32_t type = entry.data.i32[i + 3];
Uint32 fmt;
if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
continue; continue;
} }
Uint32 fmt = format_android_to_sdl(f);
fmt = format_android_2_sdl(f);
if (fmt != format) { if (fmt != format) {
continue; continue;
} }
@ -628,33 +604,11 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
i2++; i2++;
} }
return -1;
}
static int GetNumDevices(void);
int
GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
{
int index = instance_id - 1;
create_cameraMgr();
if (cameraIdList == NULL) {
GetNumDevices();
}
if (cameraIdList) {
if (index >= 0 && index < cameraIdList->numCameras) {
SDL_snprintf(buf, size, "%s", cameraIdList->cameraIds[index]);
return 0;
}
}
return -1; return -1;
} }
static int static int GetNumDevices(void)
GetNumDevices(void)
{ {
camera_status_t res; camera_status_t res;
create_cameraMgr(); create_cameraMgr();
@ -674,37 +628,55 @@ GetNumDevices(void)
return -1; return -1;
} }
int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
{
// !!! FIXME: call SDL_SetError()?
int index = instance_id - 1;
create_cameraMgr();
if (cameraIdList == NULL) {
GetNumDevices();
}
if (cameraIdList) {
if (index >= 0 && index < cameraIdList->numCameras) {
SDL_snprintf(buf, size, "%s", cameraIdList->cameraIds[index]);
return 0;
}
}
return -1;
}
SDL_CameraDeviceID *GetCameraDevices(int *count) SDL_CameraDeviceID *GetCameraDevices(int *count)
{ {
/* hard-coded list of ID */ // hard-coded list of ID
int i; const int num = GetNumDevices();
int num = GetNumDevices(); SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret));
SDL_CameraDeviceID *ret;
ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (retval == NULL) {
if (ret == NULL) {
SDL_OutOfMemory(); SDL_OutOfMemory();
*count = 0; *count = 0;
return NULL; return NULL;
} }
for (i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
ret[i] = i + 1; retval[i] = i + 1;
} }
ret[num] = 0; retval[num] = 0;
*count = num; *count = num;
return ret; return retval;
} }
int SDL_SYS_CameraInit(void) { int SDL_SYS_CameraInit(void)
{
return 0; return 0;
} }
int SDL_SYS_CameraQuit(void) { int SDL_SYS_CameraQuit(void)
{
return 0; return 0;
} }
#endif #endif

View File

@ -1,6 +1,6 @@
/* /*
Simple DirectMedia Layer Simple DirectMedia Layer
Copyright (C) 2021 Valve Corporation Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -27,7 +27,7 @@
#include "../thread/SDL_systhread.h" #include "../thread/SDL_systhread.h"
#if defined(HAVE_COREMEDIA) && defined(SDL_PLATFORM_MACOS) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101500) #if defined(HAVE_COREMEDIA) && defined(SDL_PLATFORM_MACOS) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101500)
/* AVCaptureDeviceTypeBuiltInWideAngleCamera requires macOS SDK 10.15 */ // AVCaptureDeviceTypeBuiltInWideAngleCamera requires macOS SDK 10.15
#undef HAVE_COREMEDIA #undef HAVE_COREMEDIA
#endif #endif
@ -35,7 +35,9 @@
#undef HAVE_COREMEDIA #undef HAVE_COREMEDIA
#endif #endif
#ifndef HAVE_COREMEDIA /* !!! FIXME: use the dummy driver. */ // !!! FIXME: use the dummy driver
// !!! FIXME: actually, move everything over to backend callbacks instead.
#ifndef HAVE_COREMEDIA
int InitDevice(SDL_CameraDevice *_this) { int InitDevice(SDL_CameraDevice *_this) {
return -1; return -1;
} }
@ -119,16 +121,14 @@ struct SDL_PrivateCameraData
CMSimpleQueueRef frame_queue; CMSimpleQueueRef frame_queue;
}; };
static NSString * static NSString *fourcc_to_nstring(Uint32 code)
fourcc_to_nstring(Uint32 code)
{ {
Uint8 buf[4]; Uint8 buf[4];
*(Uint32 *)buf = code; *(Uint32 *)buf = code;
return [NSString stringWithFormat:@"%c%c%c%c", buf[3], buf[2], buf[1], buf[0]]; return [NSString stringWithFormat:@"%c%c%c%c", buf[3], buf[2], buf[1], buf[0]];
} }
static NSArray<AVCaptureDevice *> * static NSArray<AVCaptureDevice *> *DiscoverCameraDevices()
discover_devices()
{ {
NSArray *deviceType = @[AVCaptureDeviceTypeBuiltInWideAngleCamera]; NSArray *deviceType = @[AVCaptureDeviceTypeBuiltInWideAngleCamera];
@ -154,10 +154,9 @@ discover_devices()
return devices; return devices;
} }
static AVCaptureDevice * static AVCaptureDevice *GetCameraDeviceByName(const char *dev_name)
get_device_by_name(const char *dev_name)
{ {
NSArray<AVCaptureDevice *> *devices = discover_devices(); NSArray<AVCaptureDevice *> *devices = DiscoverCameraDevices();
for (AVCaptureDevice *device in devices) { for (AVCaptureDevice *device in devices) {
char buf[1024]; char buf[1024];
@ -171,45 +170,41 @@ get_device_by_name(const char *dev_name)
return nil; return nil;
} }
static Uint32 static Uint32 nsfourcc_to_sdlformat(NSString *nsfourcc)
nsfourcc_to_sdlformat(NSString *nsfourcc)
{ {
const char *str = [nsfourcc UTF8String]; const char *str = [nsfourcc UTF8String];
/* FIXME /* FIXME
* on IOS this mode gives 2 planes, and it's NV12 * on IOS this mode gives 2 planes, and it's NV12
* on macos, 1 plane/ YVYU * on macos, 1 plane/ YVYU
* */
*/ #ifdef SDL_PLATFORM_MACOS
#ifdef SDL_PLATFORM_MACOS if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_YVYU;
if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_YVYU; #else
#else if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_NV12;
if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_NV12; #endif
#endif
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 (SDL_strcmp("yuvs", str) == 0) return SDL_PIXELFORMAT_UYVY;
if (SDL_strcmp("420f", str) == 0) return SDL_PIXELFORMAT_UNKNOWN;
return SDL_PIXELFORMAT_UNKNOWN; SDL_Log("Unknown format '%s'", str);
return SDL_PIXELFORMAT_UNKNOWN;
} }
static NSString * static NSString *sdlformat_to_nsfourcc(Uint32 fmt)
sdlformat_to_nsfourcc(Uint32 fmt)
{ {
const char *str = ""; const char *str = "";
NSString *result; NSString *result;
#ifdef SDL_PLATFORM_MACOS #ifdef SDL_PLATFORM_MACOS
if (fmt == SDL_PIXELFORMAT_YVYU) str = "420v"; if (fmt == SDL_PIXELFORMAT_YVYU) str = "420v";
#else #else
if (fmt == SDL_PIXELFORMAT_NV12) str = "420v"; if (fmt == SDL_PIXELFORMAT_NV12) str = "420v";
#endif #endif
if (fmt == SDL_PIXELFORMAT_UYVY) str = "yuvs"; if (fmt == SDL_PIXELFORMAT_UYVY) str = "yuvs";
result = [[NSString alloc] initWithUTF8String: str]; return [[NSString alloc] initWithUTF8String: str];
return result;
} }
@ -234,27 +229,21 @@ sdlformat_to_nsfourcc(Uint32 fmt)
- (void)captureOutput:(AVCaptureOutput *)output - (void)captureOutput:(AVCaptureOutput *)output
didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection { fromConnection:(AVCaptureConnection *)connection {
// !!! FIXME #if DEBUG_CAMERA
SDL_Log("Drop frame.."); SDL_Log("Drop frame..");
} }
@end @end
int int OpenDevice(SDL_CameraDevice *_this)
OpenDevice(SDL_CameraDevice *_this)
{ {
_this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
if (_this->hidden == NULL) { if (_this->hidden == NULL) {
SDL_OutOfMemory(); return SDL_OutOfMemory();
goto error;
} }
return 0; return 0;
error:
return -1;
} }
void void CloseDevice(SDL_CameraDevice *_this)
CloseDevice(SDL_CameraDevice *_this)
{ {
if (!_this) { if (!_this) {
return; return;
@ -282,9 +271,9 @@ CloseDevice(SDL_CameraDevice *_this)
} }
} }
int int InitDevice(SDL_CameraDevice *_this)
InitDevice(SDL_CameraDevice *_this)
{ {
// !!! FIXME: autorelease pool?
NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format); NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format);
int w = _this->spec.width; int w = _this->spec.width;
int h = _this->spec.height; int h = _this->spec.height;
@ -298,13 +287,13 @@ InitDevice(SDL_CameraDevice *_this)
#ifdef SDL_PLATFORM_MACOS #ifdef SDL_PLATFORM_MACOS
if (@available(macOS 10.15, *)) { if (@available(macOS 10.15, *)) {
/* good. */ // good.
} else { } else {
return -1; return -1;
} }
#endif #endif
device = get_device_by_name(_this->dev_name); device = GetCameraDeviceByName(_this->dev_name);
if (!device) { if (!device) {
goto error; goto error;
} }
@ -317,14 +306,13 @@ InitDevice(SDL_CameraDevice *_this)
[_this->hidden->session setSessionPreset:AVCaptureSessionPresetHigh]; [_this->hidden->session setSessionPreset:AVCaptureSessionPresetHigh];
// Pick format that matches the spec // Pick format that matches the spec
{ NSArray<AVCaptureDeviceFormat *> *formats = [device formats];
NSArray<AVCaptureDeviceFormat *> *formats = [device formats]; for (AVCaptureDeviceFormat *format in formats) {
for (AVCaptureDeviceFormat *format in formats) { CMFormatDescriptionRef formatDescription = [format formatDescription];
CMFormatDescriptionRef formatDescription = [format formatDescription]; FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); NSString *str = fourcc_to_nstring(mediaSubType);
NSString *str = fourcc_to_nstring(mediaSubType); if ([str isEqualToString:fmt]) {
if (str == fmt) { CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription);
CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription);
if (dim.width == w && dim.height == h) { if (dim.width == w && dim.height == h) {
spec_format = format; spec_format = format;
break; break;
@ -334,8 +322,7 @@ InitDevice(SDL_CameraDevice *_this)
} }
if (spec_format == nil) { if (spec_format == nil) {
SDL_SetError("format not found"); return SDL_SetError("format not found");
goto error;
} }
// Set format // Set format
@ -343,15 +330,13 @@ InitDevice(SDL_CameraDevice *_this)
device.activeFormat = spec_format; device.activeFormat = spec_format;
[device unlockForConfiguration]; [device unlockForConfiguration];
} else { } else {
SDL_SetError("Cannot lockForConfiguration"); return SDL_SetError("Cannot lockForConfiguration");
goto error;
} }
// Input // Input
input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) { if (!input) {
SDL_SetError("Cannot create AVCaptureDeviceInput"); return SDL_SetError("Cannot create AVCaptureDeviceInput");
goto error;
} }
// Output // Output
@ -373,105 +358,85 @@ InitDevice(SDL_CameraDevice *_this)
CMSimpleQueueCreate(kCFAllocatorDefault, 30 /* buffers */, &_this->hidden->frame_queue); CMSimpleQueueCreate(kCFAllocatorDefault, 30 /* buffers */, &_this->hidden->frame_queue);
if (_this->hidden->frame_queue == nil) { if (_this->hidden->frame_queue == nil) {
goto error; return SDL_SetError("CMSimpleQueueCreate() failed");
} }
_this->hidden->queue = dispatch_queue_create("my_queue", NULL); _this->hidden->queue = dispatch_queue_create("my_queue", NULL);
[output setSampleBufferDelegate:_this->hidden->delegate queue:_this->hidden->queue]; [output setSampleBufferDelegate:_this->hidden->delegate queue:_this->hidden->queue];
if ([_this->hidden->session canAddInput:input] ){ if ([_this->hidden->session canAddInput:input] ){
[_this->hidden->session addInput:input]; [_this->hidden->session addInput:input];
} else { } else {
SDL_SetError("Cannot add AVCaptureDeviceInput"); return SDL_SetError("Cannot add AVCaptureDeviceInput");
goto error;
} }
if ([_this->hidden->session canAddOutput:output] ){ if ([_this->hidden->session canAddOutput:output] ){
[_this->hidden->session addOutput:output]; [_this->hidden->session addOutput:output];
} else { } else {
SDL_SetError("Cannot add AVCaptureVideoDataOutput"); return SDL_SetError("Cannot add AVCaptureVideoDataOutput");
goto error;
} }
[_this->hidden->session commitConfiguration]; [_this->hidden->session commitConfiguration];
return 0; return 0;
error:
return -1;
} }
int int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
{ {
// !!! FIXME: make sure higher level checks spec != NULL
if (spec) { if (spec) {
*spec = _this->spec; SDL_copyp(spec, &_this->spec);
return 0; return 0;
} }
return -1; return -1;
} }
int int StartCamera(SDL_CameraDevice *_this)
StartCamera(SDL_CameraDevice *_this)
{ {
[_this->hidden->session startRunning]; [_this->hidden->session startRunning];
return 0; return 0;
} }
int int StopCamera(SDL_CameraDevice *_this)
StopCamera(SDL_CameraDevice *_this)
{ {
[_this->hidden->session stopRunning]; [_this->hidden->session stopRunning];
return 0; return 0;
} }
int int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{ {
if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) { if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) {
int i, numPlanes, planar; CMSampleBufferRef sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue);
CMSampleBufferRef sampleBuffer;
CVImageBufferRef image;
sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue);
frame->internal = (void *) sampleBuffer; frame->internal = (void *) sampleBuffer;
frame->timestampNS = SDL_GetTicksNS(); frame->timestampNS = SDL_GetTicksNS();
i = 0; CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer);
image = CMSampleBufferGetImageBuffer(sampleBuffer); const int numPlanes = CVPixelBufferGetPlaneCount(image);
numPlanes = CVPixelBufferGetPlaneCount(image); const int planar = CVPixelBufferIsPlanar(image);
planar = CVPixelBufferIsPlanar(image);
#if 0 #if 0
int w = CVPixelBufferGetWidth(image); const int w = CVPixelBufferGetWidth(image);
int h = CVPixelBufferGetHeight(image); const int h = CVPixelBufferGetHeight(image);
int sz = CVPixelBufferGetDataSize(image); const int sz = CVPixelBufferGetDataSize(image);
int pitch = CVPixelBufferGetBytesPerRow(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); SDL_Log("buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch);
#endif #endif
CVPixelBufferLockBaseAddress(image, 0); CVPixelBufferLockBaseAddress(image, 0);
if (planar == 0 && numPlanes == 0) { if ((planar == 0) && (numPlanes == 0)) {
frame->pitch[0] = CVPixelBufferGetBytesPerRow(image); frame->pitch[0] = CVPixelBufferGetBytesPerRow(image);
frame->data[0] = CVPixelBufferGetBaseAddress(image); frame->data[0] = CVPixelBufferGetBaseAddress(image);
frame->num_planes = 1; frame->num_planes = 1;
} else { } else {
for (i = 0; i < numPlanes && i < 3; i++) { for (int i = 0; (i < numPlanes) && (i < 3); i++) {
int rowStride = 0;
uint8_t *data = NULL;
frame->num_planes += 1; frame->num_planes += 1;
frame->data[i] = CVPixelBufferGetBaseAddressOfPlane(image, i);
rowStride = CVPixelBufferGetBytesPerRowOfPlane(image, i); frame->pitch[i] = CVPixelBufferGetBytesPerRowOfPlane(image, i);
data = CVPixelBufferGetBaseAddressOfPlane(image, i);
frame->data[i] = data;
frame->pitch[i] = rowStride;
} }
} }
/* Unlocked when frame is released */ // Unlocked when frame is released
} else { } else {
// no frame // no frame
SDL_Delay(20); // TODO fix some delay SDL_Delay(20); // TODO fix some delay
@ -479,24 +444,21 @@ AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
return 0; return 0;
} }
int int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{ {
if (frame->internal){ if (frame->internal) {
CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal; CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal;
CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer); CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferUnlockBaseAddress(image, 0); CVPixelBufferUnlockBaseAddress(image, 0);
CFRelease(sampleBuffer); CFRelease(sampleBuffer);
} }
return 0; return 0;
} }
int int GetNumFormats(SDL_CameraDevice *_this)
GetNumFormats(SDL_CameraDevice *_this)
{ {
AVCaptureDevice *device = get_device_by_name(_this->dev_name); AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
if (device) { if (device) {
// LIST FORMATS // LIST FORMATS
NSMutableOrderedSet<NSString *> *array_formats = [NSMutableOrderedSet new]; NSMutableOrderedSet<NSString *> *array_formats = [NSMutableOrderedSet new];
@ -514,10 +476,9 @@ GetNumFormats(SDL_CameraDevice *_this)
return 0; return 0;
} }
int int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
{ {
AVCaptureDevice *device = get_device_by_name(_this->dev_name); AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
if (device) { if (device) {
// LIST FORMATS // LIST FORMATS
NSMutableOrderedSet<NSString *> *array_formats = [NSMutableOrderedSet new]; NSMutableOrderedSet<NSString *> *array_formats = [NSMutableOrderedSet new];
@ -542,10 +503,9 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
return -1; return -1;
} }
int int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
{ {
AVCaptureDevice *device = get_device_by_name(_this->dev_name); AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
if (device) { if (device) {
NSString *fmt = sdlformat_to_nsfourcc(format); NSString *fmt = sdlformat_to_nsfourcc(format);
int count = 0; int count = 0;
@ -556,8 +516,8 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
NSString *str = fourcc_to_nstring(mediaSubType); NSString *str = fourcc_to_nstring(mediaSubType);
if (str == fmt) { if ([str isEqualToString:fmt]) {
count += 1; count++;
} }
} }
return count; return count;
@ -568,7 +528,7 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
int int
GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
{ {
AVCaptureDevice *device = get_device_by_name(_this->dev_name); AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name);
if (device) { if (device) {
NSString *fmt = sdlformat_to_nsfourcc(format); NSString *fmt = sdlformat_to_nsfourcc(format);
int count = 0; int count = 0;
@ -579,25 +539,24 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription);
NSString *str = fourcc_to_nstring(mediaSubType); NSString *str = fourcc_to_nstring(mediaSubType);
if (str == fmt) { if ([str isEqualToString:fmt]) {
if (index == count) { if (index == count) {
CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription); CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription);
*width = dim.width; *width = dim.width;
*height = dim.height; *height = dim.height;
return 0; return 0;
} }
count += 1; count++;
} }
} }
} }
return -1; return -1;
} }
int int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
{ {
int index = instance_id - 1; int index = instance_id - 1;
NSArray<AVCaptureDevice *> *devices = discover_devices(); NSArray<AVCaptureDevice *> *devices = DiscoverCameraDevices();
if (index < [devices count]) { if (index < [devices count]) {
AVCaptureDevice *device = devices[index]; AVCaptureDevice *device = devices[index];
NSString *cameraID = [device localizedName]; NSString *cameraID = [device localizedName];
@ -608,32 +567,28 @@ GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
return -1; return -1;
} }
static int static int GetNumDevices(void)
GetNumDevices(void)
{ {
NSArray<AVCaptureDevice *> *devices = discover_devices(); NSArray<AVCaptureDevice *> *devices = DiscoverCameraDevices();
return [devices count]; return [devices count];
} }
SDL_CameraDeviceID *GetCameraDevices(int *count) SDL_CameraDeviceID *GetCameraDevices(int *count)
{ {
/* hard-coded list of ID */ // hard-coded list of ID
int i; const int num = GetNumDevices();
int num = GetNumDevices(); SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_calloc((num + 1), sizeof(*ret));
SDL_CameraDeviceID *ret;
ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (retval == NULL) {
if (ret == NULL) {
SDL_OutOfMemory(); SDL_OutOfMemory();
*count = 0; *count = 0;
return NULL; return NULL;
} }
for (i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
ret[i] = i + 1; retval[i] = i + 1;
} }
ret[num] = 0; retval[num] = 0;
*count = num; *count = num;
return ret; return ret;
} }
@ -648,7 +603,7 @@ int SDL_SYS_CameraQuit(void)
return 0; return 0;
} }
#endif /* HAVE_COREMEDIA */ #endif // HAVE_COREMEDIA
#endif /* SDL_CAMERA_APPLE */ #endif // SDL_CAMERA_APPLE

View File

@ -28,27 +28,25 @@
#include "../../thread/SDL_systhread.h" #include "../../thread/SDL_systhread.h"
#include "../../core/linux/SDL_evdev_capabilities.h" #include "../../core/linux/SDL_evdev_capabilities.h"
#include "../../core/linux/SDL_udev.h" #include "../../core/linux/SDL_udev.h"
#include <limits.h> /* INT_MAX */ #include <limits.h> // INT_MAX
#define DEBUG_CAMERA 1 #define DEBUG_CAMERA 1
#define MAX_CAMERA_DEVICES 128 /* It's doubtful someone has more than that */ #define MAX_CAMERA_DEVICES 128 // It's doubtful someone has more than that
static int MaybeAddDevice(const char *path); static int MaybeAddDevice(const char *path);
#ifdef SDL_USE_LIBUDEV #ifdef SDL_USE_LIBUDEV
static int MaybeRemoveDevice(const char *path); static int MaybeRemoveDevice(const char *path);
static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
#endif /* SDL_USE_LIBUDEV */ #endif // SDL_USE_LIBUDEV
/* // List of available camera devices.
* List of available camera devices.
*/
typedef struct SDL_cameralist_item typedef struct SDL_cameralist_item
{ {
char *fname; /* Dev path name (like /dev/video0) */ char *fname; // Dev path name (like /dev/video0)
char *bus_info; /* don't add two paths with same bus_info (eg /dev/video0 and /dev/video1 */ char *bus_info; // don't add two paths with same bus_info (eg /dev/video0 and /dev/video1
SDL_CameraDeviceID instance_id; SDL_CameraDeviceID instance_id;
SDL_CameraDevice *device; /* Associated device */ SDL_CameraDevice *device; // Associated device
struct SDL_cameralist_item *next; struct SDL_cameralist_item *next;
} SDL_cameralist_item; } SDL_cameralist_item;
@ -67,7 +65,7 @@ enum io_method {
struct buffer { struct buffer {
void *start; void *start;
size_t length; size_t length;
int available; /* Is available in userspace */ int available; // Is available in userspace
}; };
struct SDL_PrivateCameraData struct SDL_PrivateCameraData
@ -82,34 +80,30 @@ struct SDL_PrivateCameraData
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <fcntl.h> /* low-level i/o */ #include <fcntl.h> // low-level i/o
#include <errno.h> #include <errno.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
static int static int xioctl(int fh, int request, void *arg)
xioctl(int fh, int request, void *arg)
{ {
int r; int r;
do { do {
r = ioctl(fh, request, arg); r = ioctl(fh, request, arg);
} while (r == -1 && errno == EINTR); } while ((r == -1) && (errno == EINTR));
return r; return r;
} }
/* -1:error 1:frame 0:no frame*/ // -1:error 1:frame 0:no frame
static int static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{ {
struct v4l2_buffer buf; const int fd = _this->hidden->fd;
int i;
int fd = _this->hidden->fd;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
size_t size = _this->hidden->buffers[0].length; size_t size = _this->hidden->buffers[0].length;
struct v4l2_buffer buf;
switch (io) { switch (io) {
case IO_METHOD_READ: case IO_METHOD_READ:
@ -119,9 +113,8 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
return 0; return 0;
case EIO: case EIO:
/* Could ignore EIO, see spec. */ // Could ignore EIO, see spec.
// fall through
/* fall through */
default: default:
return SDL_SetError("read"); return SDL_SetError("read");
@ -145,9 +138,8 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
return 0; return 0;
case EIO: case EIO:
/* Could ignore EIO, see spec. */ // Could ignore EIO, see spec.
// fall through
/* fall through */
default: default:
return SDL_SetError("VIDIOC_DQBUF: %d", errno); return SDL_SetError("VIDIOC_DQBUF: %d", errno);
@ -180,15 +172,16 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
return 0; return 0;
case EIO: case EIO:
/* Could ignore EIO, see spec. */ // Could ignore EIO, see spec.
/* fall through */ // fall through
default: default:
return SDL_SetError("VIDIOC_DQBUF"); return SDL_SetError("VIDIOC_DQBUF");
} }
} }
int i;
for (i = 0; i < _this->hidden->nb_buffers; ++i) { for (i = 0; i < _this->hidden->nb_buffers; ++i) {
if (buf.m.userptr == (unsigned long)_this->hidden->buffers[i].start && buf.length == size) { if (buf.m.userptr == (unsigned long)_this->hidden->buffers[i].start && buf.length == size) {
break; break;
@ -213,13 +206,12 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
} }
int int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{ {
struct v4l2_buffer buf; struct v4l2_buffer buf;
int i; const int fd = _this->hidden->fd;
int fd = _this->hidden->fd;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
int i;
for (i = 0; i < _this->hidden->nb_buffers; ++i) { for (i = 0; i < _this->hidden->nb_buffers; ++i) {
if (frame->num_planes && frame->data[0] == _this->hidden->buffers[i].start) { if (frame->num_planes && frame->data[0] == _this->hidden->buffers[i].start) {
@ -268,25 +260,23 @@ ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
} }
int int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{ {
fd_set fds; fd_set fds;
struct timeval tv; struct timeval tv;
int ret;
int fd = _this->hidden->fd; const int fd = _this->hidden->fd;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(fd, &fds); FD_SET(fd, &fds);
/* Timeout. */ // Timeout.
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 300 * 1000; tv.tv_usec = 300 * 1000;
ret = select(fd + 1, &fds, NULL, NULL, &tv); int retval = select(fd + 1, &fds, NULL, NULL, &tv);
if (ret == -1) { if (retval == -1) {
if (errno == EINTR) { if (errno == EINTR) {
#if DEBUG_CAMERA #if DEBUG_CAMERA
SDL_Log("continue .."); SDL_Log("continue ..");
@ -296,35 +286,34 @@ AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
return SDL_SetError("select"); return SDL_SetError("select");
} }
if (ret == 0) { if (retval == 0) {
/* Timeout. Not an error */ // Timeout. Not an error
SDL_SetError("timeout select"); SDL_SetError("timeout select");
return 0; return 0;
} }
ret = acquire_frame(_this, frame); retval = acquire_frame(_this, frame);
if (ret < 0) { if (retval < 0) {
return -1; return -1;
} }
if (ret == 1){ if (retval == 1){
frame->timestampNS = SDL_GetTicksNS(); frame->timestampNS = SDL_GetTicksNS();
} else if (ret == 0) { } else if (retval == 0) {
#if DEBUG_CAMERA #if DEBUG_CAMERA
SDL_Log("No frame continue: %s", SDL_GetError()); SDL_Log("No frame continue: %s", SDL_GetError());
#endif #endif
} }
/* EAGAIN - continue select loop. */ // EAGAIN - continue select loop.
return 0; return 0;
} }
int int StopCamera(SDL_CameraDevice *_this)
StopCamera(SDL_CameraDevice *_this)
{ {
enum v4l2_buf_type type; enum v4l2_buf_type type;
int fd = _this->hidden->fd; const int fd = _this->hidden->fd;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
switch (io) { switch (io) {
@ -343,18 +332,16 @@ StopCamera(SDL_CameraDevice *_this)
return 0; return 0;
} }
static int static int EnqueueBuffers(SDL_CameraDevice *_this)
enqueue_buffers(SDL_CameraDevice *_this)
{ {
int i; const int fd = _this->hidden->fd;
int fd = _this->hidden->fd;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
switch (io) { switch (io) {
case IO_METHOD_READ: case IO_METHOD_READ:
break; break;
case IO_METHOD_MMAP: case IO_METHOD_MMAP:
for (i = 0; i < _this->hidden->nb_buffers; ++i) { for (int i = 0; i < _this->hidden->nb_buffers; ++i) {
if (_this->hidden->buffers[i].available == 0) { if (_this->hidden->buffers[i].available == 0) {
struct v4l2_buffer buf; struct v4l2_buffer buf;
@ -371,7 +358,7 @@ enqueue_buffers(SDL_CameraDevice *_this)
break; break;
case IO_METHOD_USERPTR: case IO_METHOD_USERPTR:
for (i = 0; i < _this->hidden->nb_buffers; ++i) { for (int i = 0; i < _this->hidden->nb_buffers; ++i) {
if (_this->hidden->buffers[i].available == 0) { if (_this->hidden->buffers[i].available == 0) {
struct v4l2_buffer buf; struct v4l2_buffer buf;
@ -392,11 +379,10 @@ enqueue_buffers(SDL_CameraDevice *_this)
return 0; return 0;
} }
static int static int PreEnqueueBuffers(SDL_CameraDevice *_this)
pre_enqueue_buffers(SDL_CameraDevice *_this)
{ {
struct v4l2_requestbuffers req; struct v4l2_requestbuffers req;
int fd = _this->hidden->fd; const int fd = _this->hidden->fd;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
switch (io) { switch (io) {
@ -404,41 +390,37 @@ pre_enqueue_buffers(SDL_CameraDevice *_this)
break; break;
case IO_METHOD_MMAP: case IO_METHOD_MMAP:
{ SDL_zero(req);
SDL_zero(req); req.count = _this->hidden->nb_buffers;
req.count = _this->hidden->nb_buffers; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP;
req.memory = V4L2_MEMORY_MMAP;
if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
if (errno == EINVAL) { if (errno == EINVAL) {
return SDL_SetError("Does not support memory mapping"); return SDL_SetError("Does not support memory mapping");
} else { } else {
return SDL_SetError("VIDIOC_REQBUFS"); return SDL_SetError("VIDIOC_REQBUFS");
}
} }
if (req.count < 2) {
return SDL_SetError("Insufficient buffer memory");
}
_this->hidden->nb_buffers = req.count;
} }
if (req.count < 2) {
return SDL_SetError("Insufficient buffer memory");
}
_this->hidden->nb_buffers = req.count;
break; break;
case IO_METHOD_USERPTR: case IO_METHOD_USERPTR:
{ SDL_zero(req);
SDL_zero(req); req.count = _this->hidden->nb_buffers;
req.count = _this->hidden->nb_buffers; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_USERPTR;
req.memory = V4L2_MEMORY_USERPTR;
if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
if (errno == EINVAL) { if (errno == EINVAL) {
return SDL_SetError("Does not support user pointer i/o"); return SDL_SetError("Does not support user pointer i/o");
} else { } else {
return SDL_SetError("VIDIOC_REQBUFS"); return SDL_SetError("VIDIOC_REQBUFS");
}
} }
} }
break; break;
@ -446,34 +428,31 @@ pre_enqueue_buffers(SDL_CameraDevice *_this)
return 0; return 0;
} }
int int StartCamera(SDL_CameraDevice *_this)
StartCamera(SDL_CameraDevice *_this)
{ {
enum v4l2_buf_type type; enum v4l2_buf_type type;
int fd = _this->hidden->fd; const int fd = _this->hidden->fd;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
if (_this->hidden->first_start == 0) { if (_this->hidden->first_start == 0) {
_this->hidden->first_start = 1; _this->hidden->first_start = 1;
} else { } else {
int old = _this->hidden->nb_buffers; const int old = _this->hidden->nb_buffers;
// TODO mmap; doesn't work with stop->start // TODO mmap; doesn't work with stop->start
#if 1 #if 1
/* Can change nb_buffers for mmap */ // Can change nb_buffers for mmap
if (pre_enqueue_buffers(_this) < 0) { if (PreEnqueueBuffers(_this) < 0) {
return -1;
}
if (old != _this->hidden->nb_buffers) {
SDL_SetError("different nb of buffers requested");
return -1; return -1;
} else if (old != _this->hidden->nb_buffers) {
return SDL_SetError("different nb of buffers requested");
} }
#endif #endif
_this->hidden->first_start = 1; _this->hidden->first_start = 1;
} }
if (enqueue_buffers(_this) < 0) { if (EnqueueBuffers(_this) < 0) {
return -1; return -1;
} }
@ -493,7 +472,7 @@ StartCamera(SDL_CameraDevice *_this)
return 0; return 0;
} }
static int alloc_buffer_read(SDL_CameraDevice *_this, size_t buffer_size) static int AllocBufferRead(SDL_CameraDevice *_this, size_t buffer_size)
{ {
_this->hidden->buffers[0].length = buffer_size; _this->hidden->buffers[0].length = buffer_size;
_this->hidden->buffers[0].start = SDL_calloc(1, buffer_size); _this->hidden->buffers[0].start = SDL_calloc(1, buffer_size);
@ -505,7 +484,7 @@ static int alloc_buffer_read(SDL_CameraDevice *_this, size_t buffer_size)
} }
static int static int
alloc_buffer_mmap(SDL_CameraDevice *_this) AllocBufferMmap(SDL_CameraDevice *_this)
{ {
int fd = _this->hidden->fd; int fd = _this->hidden->fd;
int i; int i;
@ -538,7 +517,7 @@ alloc_buffer_mmap(SDL_CameraDevice *_this)
} }
static int static int
alloc_buffer_userp(SDL_CameraDevice *_this, size_t buffer_size) AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size)
{ {
int i; int i;
for (i = 0; i < _this->hidden->nb_buffers; ++i) { for (i = 0; i < _this->hidden->nb_buffers; ++i) {
@ -708,12 +687,12 @@ GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
SDL_zero(fmt); SDL_zero(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* Preserve original settings as set by v4l2-ctl for example */ // Preserve original settings as set by v4l2-ctl for example
if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) { if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
return SDL_SetError("Error VIDIOC_G_FMT"); return SDL_SetError("Error VIDIOC_G_FMT");
} }
/* Buggy driver paranoia. */ // Buggy driver paranoia.
min = fmt.fmt.pix.width * 2; min = fmt.fmt.pix.width * 2;
if (fmt.fmt.pix.bytesperline < min) { if (fmt.fmt.pix.bytesperline < min) {
fmt.fmt.pix.bytesperline = min; fmt.fmt.pix.bytesperline = min;
@ -739,29 +718,29 @@ InitDevice(SDL_CameraDevice *_this)
int fd = _this->hidden->fd; int fd = _this->hidden->fd;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
int ret = -1; int retval = -1;
/* Select video input, video standard and tune here. */ // Select video input, video standard and tune here.
SDL_zero(cropcap); SDL_zero(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) { if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) {
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */ crop.c = cropcap.defrect; // reset to default
if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) { if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) {
switch (errno) { switch (errno) {
case EINVAL: case EINVAL:
/* Cropping not supported. */ // Cropping not supported.
break; break;
default: default:
/* Errors ignored. */ // Errors ignored.
break; break;
} }
} }
} else { } else {
/* Errors ignored. */ // Errors ignored.
} }
@ -790,7 +769,7 @@ InitDevice(SDL_CameraDevice *_this)
GetDeviceSpec(_this, &_this->spec); GetDeviceSpec(_this, &_this->spec);
if (pre_enqueue_buffers(_this) < 0) { if (PreEnqueueBuffers(_this) < 0) {
return -1; return -1;
} }
@ -807,28 +786,23 @@ InitDevice(SDL_CameraDevice *_this)
switch (io) { switch (io) {
case IO_METHOD_READ: case IO_METHOD_READ:
ret = alloc_buffer_read(_this, size); retval = AllocBufferRead(_this, size);
break; break;
case IO_METHOD_MMAP: case IO_METHOD_MMAP:
ret = alloc_buffer_mmap(_this); retval = AllocBufferMmap(_this);
break; break;
case IO_METHOD_USERPTR: case IO_METHOD_USERPTR:
ret = alloc_buffer_userp(_this, size); retval = AllocBufferUserPtr(_this, size);
break; break;
} }
} }
if (ret < 0) { return (retval < 0) ? -1 : 0;
return -1;
}
return 0;
} }
void void CloseDevice(SDL_CameraDevice *_this)
CloseDevice(SDL_CameraDevice *_this)
{ {
if (!_this) { if (!_this) {
return; return;
@ -836,7 +810,6 @@ CloseDevice(SDL_CameraDevice *_this)
if (_this->hidden) { if (_this->hidden) {
if (_this->hidden->buffers) { if (_this->hidden->buffers) {
int i;
enum io_method io = _this->hidden->io; enum io_method io = _this->hidden->io;
switch (io) { switch (io) {
@ -845,7 +818,7 @@ CloseDevice(SDL_CameraDevice *_this)
break; break;
case IO_METHOD_MMAP: case IO_METHOD_MMAP:
for (i = 0; i < _this->hidden->nb_buffers; ++i) { for (int i = 0; i < _this->hidden->nb_buffers; ++i) {
if (munmap(_this->hidden->buffers[i].start, _this->hidden->buffers[i].length) == -1) { if (munmap(_this->hidden->buffers[i].start, _this->hidden->buffers[i].length) == -1) {
SDL_SetError("munmap"); SDL_SetError("munmap");
} }
@ -853,7 +826,7 @@ CloseDevice(SDL_CameraDevice *_this)
break; break;
case IO_METHOD_USERPTR: case IO_METHOD_USERPTR:
for (i = 0; i < _this->hidden->nb_buffers; ++i) { for (int i = 0; i < _this->hidden->nb_buffers; ++i) {
SDL_free(_this->hidden->buffers[i].start); SDL_free(_this->hidden->buffers[i].start);
} }
break; break;
@ -864,7 +837,7 @@ CloseDevice(SDL_CameraDevice *_this)
if (_this->hidden->fd != -1) { if (_this->hidden->fd != -1) {
if (close(_this->hidden->fd)) { if (close(_this->hidden->fd)) {
SDL_SetError("close camera device"); SDL_SetError("close camera device"); // !!! FIXME: we probably won't ever see this error
} }
} }
SDL_free(_this->hidden); SDL_free(_this->hidden);
@ -874,13 +847,12 @@ CloseDevice(SDL_CameraDevice *_this)
} }
int int OpenDevice(SDL_CameraDevice *_this)
OpenDevice(SDL_CameraDevice *_this)
{ {
struct stat st; struct stat st;
struct v4l2_capability cap; struct v4l2_capability cap;
int fd;
enum io_method io; enum io_method io;
int fd;
_this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
if (_this->hidden == NULL) { if (_this->hidden == NULL) {
@ -891,19 +863,11 @@ OpenDevice(SDL_CameraDevice *_this)
_this->hidden->fd = -1; _this->hidden->fd = -1;
if (stat(_this->dev_name, &st) == -1) { if (stat(_this->dev_name, &st) == -1) {
SDL_SetError("Cannot identify '%s': %d, %s", _this->dev_name, errno, strerror(errno)); return SDL_SetError("Cannot identify '%s': %d, %s", _this->dev_name, errno, strerror(errno));
return -1; } else if (!S_ISCHR(st.st_mode)) {
} return SDL_SetError("%s is no device", _this->dev_name);
} else if ((fd = open(_this->dev_name, O_RDWR /* required */ | O_NONBLOCK, 0)) == -1) {
if (!S_ISCHR(st.st_mode)) { return SDL_SetError("Cannot open '%s': %d, %s", _this->dev_name, errno, strerror(errno));
SDL_SetError("%s is no device", _this->dev_name);
return -1;
}
fd = open(_this->dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
if (fd == -1) {
SDL_SetError("Cannot open '%s': %d, %s", _this->dev_name, errno, strerror(errno));
return -1;
} }
_this->hidden->fd = fd; _this->hidden->fd = fd;
@ -914,7 +878,7 @@ OpenDevice(SDL_CameraDevice *_this)
if (_this->hidden->io == IO_METHOD_READ) { if (_this->hidden->io == IO_METHOD_READ) {
_this->hidden->nb_buffers = 1; _this->hidden->nb_buffers = 1;
} else { } else {
_this->hidden->nb_buffers = 8; /* Number of image as internal buffer, */ _this->hidden->nb_buffers = 8; // Number of image as internal buffer,
} }
io = _this->hidden->io; io = _this->hidden->io;
@ -954,14 +918,10 @@ OpenDevice(SDL_CameraDevice *_this)
break; break;
} }
return 0; return 0;
} }
int int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
{ {
SDL_cameralist_item *item; SDL_cameralist_item *item;
for (item = SDL_cameralist; item; item = item->next) { for (item = SDL_cameralist; item; item = item->next) {
@ -971,54 +931,46 @@ GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
} }
} }
/* unknown instance_id */ // unknown instance_id
return -1; return -1;
} }
SDL_CameraDeviceID *GetCameraDevices(int *count) SDL_CameraDeviceID *GetCameraDevices(int *count)
{ {
/* real list of ID */ // real list of ID
int i = 0; const int num = num_cameras;
int num = num_cameras; SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*retval));
SDL_CameraDeviceID *ret;
SDL_cameralist_item *item;
ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (retval == NULL) {
if (ret == NULL) {
SDL_OutOfMemory(); SDL_OutOfMemory();
*count = 0; *count = 0;
return NULL; return NULL;
} }
for (item = SDL_cameralist; item; item = item->next) { int i = 0;
ret[i] = item->instance_id; for (SDL_cameralist_item *item = SDL_cameralist; item; item = item->next) {
i++; retval[i++] = item->instance_id;
} }
ret[num] = 0; retval[num] = 0;
*count = num; *count = num;
return ret; return retval;
} }
/* // Initializes the subsystem by finding available devices.
* Initializes the subsystem by finding available devices.
*/
int SDL_SYS_CameraInit(void) int SDL_SYS_CameraInit(void)
{ {
const char pattern[] = "/dev/video%d"; const char pattern[] = "/dev/video%d";
char path[PATH_MAX]; char path[PATH_MAX];
int i, j;
/* /*
* Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have
* permission to some or all devices. * permission to some or all devices.
*/ */
i = 0; for (int i = 0; i < MAX_CAMERA_DEVICES; i++) {
for (j = 0; j < MAX_CAMERA_DEVICES; ++j) { (void)SDL_snprintf(path, PATH_MAX, pattern, i);
(void)SDL_snprintf(path, PATH_MAX, pattern, i++);
if (MaybeAddDevice(path) == -2) { if (MaybeAddDevice(path) == -2) {
break; break;
} }
@ -1027,27 +979,22 @@ int SDL_SYS_CameraInit(void)
#ifdef SDL_USE_LIBUDEV #ifdef SDL_USE_LIBUDEV
if (SDL_UDEV_Init() < 0) { if (SDL_UDEV_Init() < 0) {
return SDL_SetError("Could not initialize UDEV"); return SDL_SetError("Could not initialize UDEV");
} } else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) {
if (SDL_UDEV_AddCallback(camera_udev_callback) < 0) {
SDL_UDEV_Quit(); SDL_UDEV_Quit();
return SDL_SetError("Could not setup Video Capture <-> udev callback"); return SDL_SetError("Could not setup Video Capture <-> udev callback");
} }
/* Force a scan to build the initial device list */ // Force a scan to build the initial device list
SDL_UDEV_Scan(); SDL_UDEV_Scan();
#endif /* SDL_USE_LIBUDEV */ #endif // SDL_USE_LIBUDEV
return num_cameras; return num_cameras;
} }
int SDL_SYS_CameraQuit(void) int SDL_SYS_CameraQuit(void)
{ {
SDL_cameralist_item *item; for (SDL_cameralist_item *item = SDL_cameralist; item; ) {
for (item = SDL_cameralist; item; ) {
SDL_cameralist_item *tmp = item->next; SDL_cameralist_item *tmp = item->next;
SDL_free(item->fname); SDL_free(item->fname);
SDL_free(item->bus_info); SDL_free(item->bus_info);
SDL_free(item); SDL_free(item);
@ -1062,7 +1009,7 @@ int SDL_SYS_CameraQuit(void)
} }
#ifdef SDL_USE_LIBUDEV #ifdef SDL_USE_LIBUDEV
static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
{ {
if (!devpath || !(udev_class & SDL_UDEV_DEVICE_VIDEO_CAPTURE)) { if (!devpath || !(udev_class & SDL_UDEV_DEVICE_VIDEO_CAPTURE)) {
return; return;
@ -1081,17 +1028,15 @@ static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class,
break; break;
} }
} }
#endif /* SDL_USE_LIBUDEV */ #endif // SDL_USE_LIBUDEV
static SDL_bool DeviceExists(const char *path, const char *bus_info) { static SDL_bool DeviceExists(const char *path, const char *bus_info) {
SDL_cameralist_item *item; for (SDL_cameralist_item *item = SDL_cameralist; item; item = item->next) {
// found same dev name
for (item = SDL_cameralist; item; item = item->next) {
/* found same dev name */
if (SDL_strcmp(path, item->fname) == 0) { if (SDL_strcmp(path, item->fname) == 0) {
return SDL_TRUE; return SDL_TRUE;
} }
/* found same bus_info */ // found same bus_info
if (SDL_strcmp(bus_info, item->bus_info) == 0) { if (SDL_strcmp(bus_info, item->bus_info) == 0) {
return SDL_TRUE; return SDL_TRUE;
} }
@ -1113,7 +1058,7 @@ static int MaybeAddDevice(const char *path)
fd = open(path, O_RDWR); fd = open(path, O_RDWR);
if (fd < 0) { if (fd < 0) {
return -2; /* stop iterating /dev/video%d */ return -2; // stop iterating /dev/video%d
} }
err = ioctl(fd, VIDIOC_QUERYCAP, &vcap); err = ioctl(fd, VIDIOC_QUERYCAP, &vcap);
close(fd); close(fd);
@ -1129,7 +1074,7 @@ static int MaybeAddDevice(const char *path)
} }
/* Add new item */ // Add new item
item = (SDL_cameralist_item *)SDL_calloc(1, sizeof(SDL_cameralist_item)); item = (SDL_cameralist_item *)SDL_calloc(1, sizeof(SDL_cameralist_item));
if (!item) { if (!item) {
SDL_free(bus_info); SDL_free(bus_info);
@ -1157,7 +1102,7 @@ static int MaybeAddDevice(const char *path)
++num_cameras; ++num_cameras;
/* !!! TODO: Send a add event? */ // !!! TODO: Send a add event?
#if DEBUG_CAMERA #if DEBUG_CAMERA
SDL_Log("Added video camera ID: %d %s (%s) (total: %d)", item->instance_id, path, bus_info, num_cameras); SDL_Log("Added video camera ID: %d %s (%s) (total: %d)", item->instance_id, path, bus_info, num_cameras);
#endif #endif
@ -1178,7 +1123,7 @@ static int MaybeRemoveDevice(const char *path)
} }
for (item = SDL_cameralist; item; item = item->next) { for (item = SDL_cameralist; item; item = item->next) {
/* found it, remove it. */ // found it, remove it.
if (SDL_strcmp(path, item->fname) == 0) { if (SDL_strcmp(path, item->fname) == 0) {
if (prev) { if (prev) {
prev->next = item->next; prev->next = item->next;
@ -1190,9 +1135,9 @@ static int MaybeRemoveDevice(const char *path)
SDL_cameralist_tail = prev; SDL_cameralist_tail = prev;
} }
/* Need to decrement the count */ // Need to decrement the count
--num_cameras; --num_cameras;
/* !!! TODO: Send a remove event? */ // !!! TODO: Send a remove event?
SDL_free(item->fname); SDL_free(item->fname);
SDL_free(item->bus_info); SDL_free(item->bus_info);
@ -1203,6 +1148,7 @@ static int MaybeRemoveDevice(const char *path)
} }
return 0; return 0;
} }
#endif /* SDL_USE_LIBUDEV */ #endif // SDL_USE_LIBUDEV
#endif // SDL_CAMERA_V4L2
#endif /* SDL_CAMERA_V4L2 */