camera: Reworked to operate with a driver interface, like other subsystems.

This commit is contained in:
Ryan C. Gordon
2023-12-01 10:59:13 -05:00
parent 2ad44bd162
commit cb10c80aaf
25 changed files with 608 additions and 332 deletions

View File

@@ -20,7 +20,7 @@
*/
#include "SDL_internal.h"
#ifdef SDL_CAMERA_V4L2
#ifdef SDL_CAMERA_DRIVER_V4L2
#include "../SDL_syscamera.h"
#include "../SDL_camera_c.h"
@@ -30,8 +30,6 @@
#include "../../core/linux/SDL_udev.h"
#include <limits.h> // INT_MAX
#define DEBUG_CAMERA 1
#define MAX_CAMERA_DEVICES 128 // It's doubtful someone has more than that
static int MaybeAddDevice(const char *path);
@@ -206,7 +204,7 @@ static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
}
int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
static int V4L2_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{
struct v4l2_buffer buf;
const int fd = _this->hidden->fd;
@@ -259,8 +257,7 @@ int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
return 0;
}
int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
static int V4L2_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
{
fd_set fds;
struct timeval tv;
@@ -310,7 +307,7 @@ int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame)
}
int StopCamera(SDL_CameraDevice *_this)
static int V4L2_StopCamera(SDL_CameraDevice *_this)
{
enum v4l2_buf_type type;
const int fd = _this->hidden->fd;
@@ -428,7 +425,7 @@ static int PreEnqueueBuffers(SDL_CameraDevice *_this)
return 0;
}
int StartCamera(SDL_CameraDevice *_this)
static int V4L2_StartCamera(SDL_CameraDevice *_this)
{
enum v4l2_buf_type type;
@@ -476,15 +473,10 @@ static int AllocBufferRead(SDL_CameraDevice *_this, size_t buffer_size)
{
_this->hidden->buffers[0].length = buffer_size;
_this->hidden->buffers[0].start = SDL_calloc(1, buffer_size);
if (!_this->hidden->buffers[0].start) {
return SDL_OutOfMemory();
}
return 0;
return _this->hidden->buffers[0].start ? 0 : -1;
}
static int
AllocBufferMmap(SDL_CameraDevice *_this)
static int AllocBufferMmap(SDL_CameraDevice *_this)
{
int fd = _this->hidden->fd;
int i;
@@ -516,8 +508,7 @@ AllocBufferMmap(SDL_CameraDevice *_this)
return 0;
}
static int
AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size)
static int AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size)
{
int i;
for (i = 0; i < _this->hidden->nb_buffers; ++i) {
@@ -525,41 +516,38 @@ AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size)
_this->hidden->buffers[i].start = SDL_calloc(1, buffer_size);
if (!_this->hidden->buffers[i].start) {
return SDL_OutOfMemory();
return -1;
}
}
return 0;
}
static Uint32
format_v4l2_2_sdl(Uint32 fmt)
static Uint32 format_v4l2_to_sdl(Uint32 fmt)
{
switch (fmt) {
#define CASE(x, y) case x: return y
#define CASE(x, y) case x: return y
CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2);
CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN);
#undef CASE
#undef CASE
default:
SDL_Log("Unknown format V4L2_PIX_FORMAT '%d'", fmt);
return SDL_PIXELFORMAT_UNKNOWN;
}
}
static Uint32
format_sdl_2_v4l2(Uint32 fmt)
static Uint32 format_sdl_to_v4l2(Uint32 fmt)
{
switch (fmt) {
#define CASE(y, x) case x: return y
#define CASE(y, x) case x: return y
CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2);
CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN);
#undef CASE
#undef CASE
default:
return 0;
}
}
int
GetNumFormats(SDL_CameraDevice *_this)
static int V4L2_GetNumFormats(SDL_CameraDevice *_this)
{
int fd = _this->hidden->fd;
int i = 0;
@@ -574,8 +562,7 @@ GetNumFormats(SDL_CameraDevice *_this)
return i;
}
int
GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
static int V4L2_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
{
int fd = _this->hidden->fd;
struct v4l2_fmtdesc fmtdesc;
@@ -584,7 +571,7 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
fmtdesc.index = index;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0) {
*format = format_v4l2_2_sdl(fmtdesc.pixelformat);
*format = format_v4l2_to_sdl(fmtdesc.pixelformat);
#if DEBUG_CAMERA
if (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED) {
@@ -600,8 +587,7 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format)
return -1;
}
int
GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
static int V4L2_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
{
int fd = _this->hidden->fd;
int i = 0;
@@ -609,7 +595,7 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
SDL_zero(frmsizeenum);
frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmsizeenum.pixel_format = format_sdl_2_v4l2(format);
frmsizeenum.pixel_format = format_sdl_to_v4l2(format);
while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) {
frmsizeenum.index++;
if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
@@ -624,8 +610,7 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format)
return i;
}
int
GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
static int V4L2_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height)
{
int fd = _this->hidden->fd;
struct v4l2_frmsizeenum frmsizeenum;
@@ -633,7 +618,7 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
SDL_zero(frmsizeenum);
frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmsizeenum.pixel_format = format_sdl_2_v4l2(format);
frmsizeenum.pixel_format = format_sdl_to_v4l2(format);
while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) {
frmsizeenum.index++;
@@ -663,12 +648,8 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int
return -1;
}
#if DEBUG_VIDEO_CAPTURE_CAPTURE
static void
dbg_v4l2_pixelformat(const char *str, int f) {
static void dbg_v4l2_pixelformat(const char *str, int f)
{
SDL_Log("%s V4L2_format=%d %c%c%c%c", str, f,
(f >> 0) & 0xff,
(f >> 8) & 0xff,
@@ -677,8 +658,7 @@ dbg_v4l2_pixelformat(const char *str, int f) {
}
#endif
int
GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
static int V4L2_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
{
struct v4l2_format fmt;
int fd = _this->hidden->fd;
@@ -705,13 +685,12 @@ GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec)
//spec->width = fmt.fmt.pix.width;
//spec->height = fmt.fmt.pix.height;
_this->hidden->driver_pitch = fmt.fmt.pix.bytesperline;
//spec->format = format_v4l2_2_sdl(fmt.fmt.pix.pixelformat);
//spec->format = format_v4l2_to_sdl(fmt.fmt.pix.pixelformat);
return 0;
}
int
InitDevice(SDL_CameraDevice *_this)
static int V4L2_InitDevice(SDL_CameraDevice *_this)
{
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
@@ -753,7 +732,7 @@ InitDevice(SDL_CameraDevice *_this)
fmt.fmt.pix.height = _this->spec.height;
fmt.fmt.pix.pixelformat = format_sdl_2_v4l2(_this->spec.format);
fmt.fmt.pix.pixelformat = format_sdl_to_v4l2(_this->spec.format);
// fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
@@ -767,7 +746,7 @@ InitDevice(SDL_CameraDevice *_this)
}
}
GetDeviceSpec(_this, &_this->spec);
V4L2_GetDeviceSpec(_this, &_this->spec);
if (PreEnqueueBuffers(_this) < 0) {
return -1;
@@ -776,7 +755,7 @@ InitDevice(SDL_CameraDevice *_this)
{
_this->hidden->buffers = SDL_calloc(_this->hidden->nb_buffers, sizeof(*_this->hidden->buffers));
if (!_this->hidden->buffers) {
return SDL_OutOfMemory();
return -1;
}
}
@@ -802,7 +781,7 @@ InitDevice(SDL_CameraDevice *_this)
return (retval < 0) ? -1 : 0;
}
void CloseDevice(SDL_CameraDevice *_this)
static void V4L2_CloseDevice(SDL_CameraDevice *_this)
{
if (!_this) {
return;
@@ -846,8 +825,7 @@ void CloseDevice(SDL_CameraDevice *_this)
}
}
int OpenDevice(SDL_CameraDevice *_this)
static int V4L2_OpenDevice(SDL_CameraDevice *_this)
{
struct stat st;
struct v4l2_capability cap;
@@ -856,7 +834,6 @@ int OpenDevice(SDL_CameraDevice *_this)
_this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
if (_this->hidden == NULL) {
SDL_OutOfMemory();
return -1;
}
@@ -921,7 +898,7 @@ int OpenDevice(SDL_CameraDevice *_this)
return 0;
}
int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
static int V4L2_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
{
SDL_cameralist_item *item;
for (item = SDL_cameralist; item; item = item->next) {
@@ -935,15 +912,13 @@ int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size)
return -1;
}
SDL_CameraDeviceID *GetCameraDevices(int *count)
static SDL_CameraDeviceID *V4L2_GetDevices(int *count)
{
// real list of ID
const int num = num_cameras;
SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*retval));
if (retval == NULL) {
SDL_OutOfMemory();
*count = 0;
return NULL;
}
@@ -959,55 +934,6 @@ SDL_CameraDeviceID *GetCameraDevices(int *count)
}
// Initializes the subsystem by finding available devices.
int SDL_SYS_CameraInit(void)
{
const char pattern[] = "/dev/video%d";
char path[PATH_MAX];
/*
* Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have
* permission to some or all devices.
*/
for (int i = 0; i < MAX_CAMERA_DEVICES; i++) {
(void)SDL_snprintf(path, PATH_MAX, pattern, i);
if (MaybeAddDevice(path) == -2) {
break;
}
}
#ifdef SDL_USE_LIBUDEV
if (SDL_UDEV_Init() < 0) {
return SDL_SetError("Could not initialize UDEV");
} else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) {
SDL_UDEV_Quit();
return SDL_SetError("Could not setup Video Capture <-> udev callback");
}
// Force a scan to build the initial device list
SDL_UDEV_Scan();
#endif // SDL_USE_LIBUDEV
return num_cameras;
}
int SDL_SYS_CameraQuit(void)
{
for (SDL_cameralist_item *item = SDL_cameralist; item; ) {
SDL_cameralist_item *tmp = item->next;
SDL_free(item->fname);
SDL_free(item->bus_info);
SDL_free(item);
item = tmp;
}
num_cameras = 0;
SDL_cameralist = NULL;
SDL_cameralist_tail = NULL;
return SDL_FALSE;
}
#ifdef SDL_USE_LIBUDEV
static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
{
@@ -1150,5 +1076,78 @@ static int MaybeRemoveDevice(const char *path)
}
#endif // SDL_USE_LIBUDEV
static void V4L2_Deinitialize(void)
{
for (SDL_cameralist_item *item = SDL_cameralist; item; ) {
SDL_cameralist_item *tmp = item->next;
SDL_free(item->fname);
SDL_free(item->bus_info);
SDL_free(item);
item = tmp;
}
num_cameras = 0;
SDL_cameralist = NULL;
SDL_cameralist_tail = NULL;
}
static void V4L2_DetectDevices(void)
{
}
static SDL_bool V4L2_Init(SDL_CameraDriverImpl *impl)
{
// !!! FIXME: move to DetectDevices
const char pattern[] = "/dev/video%d";
char path[PATH_MAX];
/*
* Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have
* permission to some or all devices.
*/
for (int i = 0; i < MAX_CAMERA_DEVICES; i++) {
(void)SDL_snprintf(path, PATH_MAX, pattern, i);
if (MaybeAddDevice(path) == -2) {
break;
}
}
#ifdef SDL_USE_LIBUDEV
if (SDL_UDEV_Init() < 0) {
return SDL_SetError("Could not initialize UDEV");
} else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) {
SDL_UDEV_Quit();
return SDL_SetError("Could not setup Video Capture <-> udev callback");
}
// Force a scan to build the initial device list
SDL_UDEV_Scan();
#endif // SDL_USE_LIBUDEV
impl->DetectDevices = V4L2_DetectDevices;
impl->OpenDevice = V4L2_OpenDevice;
impl->CloseDevice = V4L2_CloseDevice;
impl->InitDevice = V4L2_InitDevice;
impl->GetDeviceSpec = V4L2_GetDeviceSpec;
impl->StartCamera = V4L2_StartCamera;
impl->StopCamera = V4L2_StopCamera;
impl->AcquireFrame = V4L2_AcquireFrame;
impl->ReleaseFrame = V4L2_ReleaseFrame;
impl->GetNumFormats = V4L2_GetNumFormats;
impl->GetFormat = V4L2_GetFormat;
impl->GetNumFrameSizes = V4L2_GetNumFrameSizes;
impl->GetFrameSize = V4L2_GetFrameSize;
impl->GetDeviceName = V4L2_GetDeviceName;
impl->GetDevices = V4L2_GetDevices;
impl->Deinitialize = V4L2_Deinitialize;
return SDL_TRUE;
}
CameraBootStrap V4L2_bootstrap = {
"v4l2", "SDL Video4Linux2 camera driver", V4L2_Init, SDL_FALSE
};
#endif // SDL_CAMERA_V4L2