mirror of
https://github.com/libretro/RetroArch
synced 2025-01-29 18:32:44 +00:00
Some V4L2 cleanups.
Avoids global state for buffers, and make processing loop a bit simpler.
This commit is contained in:
parent
8826034a55
commit
a824952dec
@ -32,7 +32,6 @@
|
||||
#include "../compat/strl.h"
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
struct buffer
|
||||
@ -43,13 +42,17 @@ struct buffer
|
||||
|
||||
typedef struct video4linux
|
||||
{
|
||||
char dev_name[256];
|
||||
int fd;
|
||||
bool ready;
|
||||
struct buffer *buffers;
|
||||
unsigned n_buffers;
|
||||
size_t width;
|
||||
size_t height;
|
||||
|
||||
uint32_t *YCbCr_to_RGB;
|
||||
uint32_t *buffer_output;
|
||||
bool ready;
|
||||
|
||||
char dev_name[PATH_MAX];
|
||||
} video4linux_t;
|
||||
|
||||
// FIXME: Shouldn't use LUTs for this.
|
||||
@ -65,18 +68,16 @@ typedef struct video4linux
|
||||
*/
|
||||
#define YUV_SHIFT(y, cb, cr) ((y << 16) | (cb << 8) | (cr << 0))
|
||||
#define RGB_SHIFT(r, g, b) ((r << 16) | (g << 8) | (b << 0))
|
||||
static uint32_t *YCbCr_to_RGB;
|
||||
static uint32_t *buffer_output;
|
||||
|
||||
static void generate_YCbCr_to_RGB_lookup(void)
|
||||
static uint32_t *generate_YCbCr_to_RGB_lookup(void)
|
||||
{
|
||||
int y;
|
||||
int cb;
|
||||
int cr;
|
||||
|
||||
YCbCr_to_RGB = (uint32_t*)realloc(YCbCr_to_RGB, 256 * 256 * 256 * sizeof(uint32_t));
|
||||
if (!YCbCr_to_RGB)
|
||||
return;
|
||||
uint32_t *buffer = (uint32_t*)malloc(256 * 256 * 256 * sizeof(uint32_t));
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
for (y = 0; y < 256; y++)
|
||||
{
|
||||
@ -96,10 +97,12 @@ static void generate_YCbCr_to_RGB_lookup(void)
|
||||
G = max(0, min(255, G));
|
||||
B = max(0, min(255, B));
|
||||
|
||||
YCbCr_to_RGB[YUV_SHIFT(y, cb, cr)] = RGB_SHIFT(R, G, B);
|
||||
buffer[YUV_SHIFT(y, cb, cr)] = RGB_SHIFT(R, G, B);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,28 +118,27 @@ static void generate_YCbCr_to_RGB_lookup(void)
|
||||
// eventually - GL binding to texture and color conversion through shaders,
|
||||
// and this approach
|
||||
|
||||
static inline void YUV422_to_RGB(uint32_t *output, const uint8_t *input)
|
||||
static inline void YUV422_to_RGB(uint32_t *output, const uint8_t *input, const uint32_t *lut)
|
||||
{
|
||||
uint8_t y0 = input[0];
|
||||
uint8_t cb = input[1];
|
||||
uint8_t y1 = input[2];
|
||||
uint8_t cr = input[3];
|
||||
|
||||
output[0] = YCbCr_to_RGB[YUV_SHIFT(y0, cb, cr)];
|
||||
output[1] = YCbCr_to_RGB[YUV_SHIFT(y1, cb, cr)];
|
||||
output[0] = lut[YUV_SHIFT(y0, cb, cr)];
|
||||
output[1] = lut[YUV_SHIFT(y1, cb, cr)];
|
||||
}
|
||||
|
||||
static void process_image(void *data, const void *p)
|
||||
static void process_image(void *data, const uint8_t *buffer_yuv)
|
||||
{
|
||||
video4linux_t *v4l = (video4linux_t*)data;
|
||||
const uint8_t *buffer_yuv = p;
|
||||
uint8_t *buffer_dst = (uint8_t *) buffer_output;
|
||||
const uint32_t *lut = v4l->YCbCr_to_RGB;
|
||||
uint32_t *dst = v4l->buffer_output;
|
||||
size_t x, y;
|
||||
|
||||
for (y = 0; y < v4l->height; y++)
|
||||
for (y = 0; y < v4l->height; y++, dst += v4l->width, buffer_yuv += v4l->width * 2)
|
||||
for (x = 0; x < v4l->width; x += 2)
|
||||
YUV422_to_RGB((uint32_t *)(buffer_dst + (y * v4l->width + x) * 4),
|
||||
buffer_yuv + (y * v4l->width + x) * 2);
|
||||
YUV422_to_RGB(dst + x, buffer_yuv + x * 2, lut);
|
||||
}
|
||||
|
||||
static int xioctl(int fd, int request, void *args)
|
||||
@ -151,7 +153,7 @@ static int xioctl(int fd, int request, void *args)
|
||||
return r;
|
||||
}
|
||||
|
||||
static int init_mmap(void *data)
|
||||
static bool init_mmap(void *data)
|
||||
{
|
||||
struct v4l2_requestbuffers req;
|
||||
video4linux_t *v4l = (video4linux_t*)data;
|
||||
@ -167,19 +169,19 @@ static int init_mmap(void *data)
|
||||
if (errno == EINVAL)
|
||||
{
|
||||
RARCH_ERR("%s does not support memory mapping.\n", v4l->dev_name);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_ERR("xioctl of VIDIOC_REQBUFS failed.\n");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.count < 2)
|
||||
{
|
||||
RARCH_ERR("Insufficient buffer memory on %s.\n", v4l->dev_name);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
v4l->buffers = (struct buffer*)calloc(req.count, sizeof(*v4l->buffers));
|
||||
@ -187,7 +189,7 @@ static int init_mmap(void *data)
|
||||
if (!v4l->buffers)
|
||||
{
|
||||
RARCH_ERR("Out of memory allocating V4L2 buffers.\n");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (v4l->n_buffers = 0; v4l->n_buffers < req.count; v4l->n_buffers++)
|
||||
@ -203,7 +205,7 @@ static int init_mmap(void *data)
|
||||
if (xioctl(v4l->fd, VIDIOC_QUERYBUF, &buf) == -1)
|
||||
{
|
||||
RARCH_ERR("Error - xioctl VIDIOC_QUERYBUF.\n");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
v4l->buffers[v4l->n_buffers].length = buf.length;
|
||||
@ -215,14 +217,14 @@ static int init_mmap(void *data)
|
||||
if (v4l->buffers[v4l->n_buffers].start == MAP_FAILED)
|
||||
{
|
||||
RARCH_ERR("Error - mmap.\n");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int init_device(void *data)
|
||||
static bool init_device(void *data)
|
||||
{
|
||||
struct v4l2_capability cap;
|
||||
struct v4l2_cropcap cropcap;
|
||||
@ -236,25 +238,25 @@ static int init_device(void *data)
|
||||
if (errno == EINVAL)
|
||||
{
|
||||
RARCH_ERR("%s is no V4L2 device.\n", v4l->dev_name);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_ERR("Error - VIDIOC_QUERYCAP.\n");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
|
||||
{
|
||||
RARCH_ERR("%s is no video capture device.\n", v4l->dev_name);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_STREAMING))
|
||||
{
|
||||
RARCH_ERR("%s does not support streaming I/O (V4L2_CAP_STREAMING).\n", v4l->dev_name);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Select video input, video standard and tune here. */
|
||||
@ -282,7 +284,7 @@ static int init_device(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
memset (&fmt, 0, sizeof(fmt));
|
||||
memset(&fmt, 0, sizeof(fmt));
|
||||
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmt.fmt.pix.width = v4l->width;
|
||||
@ -294,7 +296,7 @@ static int init_device(void *data)
|
||||
if (xioctl(v4l->fd, VIDIOC_S_FMT, &fmt) == -1)
|
||||
{
|
||||
RARCH_ERR("Error - VIDIOC_S_FMT\n");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Note VIDIOC_S_FMT may change width and height. */
|
||||
@ -360,12 +362,29 @@ static bool v4l_start(void *data)
|
||||
return false;
|
||||
}
|
||||
|
||||
generate_YCbCr_to_RGB_lookup();
|
||||
v4l->ready = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void v4l_free(void *data)
|
||||
{
|
||||
video4linux_t *v4l = (video4linux_t*)data;
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < v4l->n_buffers; i++)
|
||||
if (munmap(v4l->buffers[i].start, v4l->buffers[i].length) == -1)
|
||||
RARCH_ERR("munmap failed.\n");
|
||||
|
||||
if (v4l->fd >= 0)
|
||||
close(v4l->fd);
|
||||
|
||||
free(v4l->YCbCr_to_RGB);
|
||||
free(v4l->buffer_output);
|
||||
|
||||
free(v4l);
|
||||
}
|
||||
|
||||
static void *v4l_init(const char *device, uint64_t caps, unsigned width, unsigned height)
|
||||
{
|
||||
struct stat st;
|
||||
@ -380,10 +399,7 @@ static void *v4l_init(const char *device, uint64_t caps, unsigned width, unsigne
|
||||
if (!v4l)
|
||||
return NULL;
|
||||
|
||||
if (device == NULL)
|
||||
strlcpy(v4l->dev_name, "/dev/video0", sizeof(v4l->dev_name));
|
||||
else
|
||||
strlcpy(v4l->dev_name, device, sizeof(v4l->dev_name));
|
||||
strlcpy(v4l->dev_name, device ? device : "/dev/video0", sizeof(v4l->dev_name));
|
||||
|
||||
v4l->width = width;
|
||||
v4l->height = height;
|
||||
@ -409,41 +425,31 @@ static void *v4l_init(const char *device, uint64_t caps, unsigned width, unsigne
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (init_device(v4l) == -1)
|
||||
if (!init_device(v4l))
|
||||
goto error;
|
||||
|
||||
buffer_output = (uint32_t*)malloc( v4l->width * v4l->height * sizeof(uint32_t));
|
||||
v4l->buffer_output = (uint32_t*)malloc(v4l->width * v4l->height * sizeof(uint32_t));
|
||||
if (!v4l->buffer_output)
|
||||
{
|
||||
RARCH_ERR("Failed to allocate output buffer.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
v4l->YCbCr_to_RGB = generate_YCbCr_to_RGB_lookup();
|
||||
if (!v4l->YCbCr_to_RGB)
|
||||
{
|
||||
RARCH_ERR("Failed to create YUV->RGB LUT.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return v4l;
|
||||
|
||||
error:
|
||||
RARCH_ERR("V4L2: Failed to initialize camera.\n");
|
||||
free(v4l);
|
||||
v4l_free(v4l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void v4l_free(void *data)
|
||||
{
|
||||
video4linux_t *v4l = (video4linux_t*)data;
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < v4l->n_buffers; i++)
|
||||
if (munmap(v4l->buffers[i].start, v4l->buffers[i].length) == -1)
|
||||
RARCH_ERR("munmap failed.\n");
|
||||
|
||||
if (v4l->fd >= 0)
|
||||
close(v4l->fd);
|
||||
free(v4l);
|
||||
|
||||
// Assumes one instance. LUT will be gone at some point anyways.
|
||||
free(YCbCr_to_RGB);
|
||||
YCbCr_to_RGB = NULL;
|
||||
|
||||
if (buffer_output)
|
||||
free(buffer_output);
|
||||
buffer_output = NULL;
|
||||
}
|
||||
|
||||
static bool preprocess_image(void *data)
|
||||
{
|
||||
video4linux_t *v4l = (video4linux_t*)data;
|
||||
@ -472,9 +478,9 @@ static bool preprocess_image(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
assert(buf.index < v4l->n_buffers);
|
||||
rarch_assert(buf.index < v4l->n_buffers);
|
||||
|
||||
process_image(v4l, v4l->buffers[buf.index].start);
|
||||
process_image(v4l, (const uint8_t*)v4l->buffers[buf.index].start);
|
||||
|
||||
if (xioctl(v4l->fd, VIDIOC_QBUF, &buf) == -1)
|
||||
RARCH_ERR("VIDIOC_QBUF\n");
|
||||
@ -494,7 +500,7 @@ static bool v4l_poll(void *data, retro_camera_frame_raw_framebuffer_t frame_raw_
|
||||
if (preprocess_image(data))
|
||||
{
|
||||
if (frame_raw_cb != NULL)
|
||||
frame_raw_cb(buffer_output, v4l->width, v4l->height, v4l->width * 4);
|
||||
frame_raw_cb(v4l->buffer_output, v4l->width, v4l->height, v4l->width * 4);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user