From a824952decc56b191070cff061be444b6535fe59 Mon Sep 17 00:00:00 2001
From: Themaister <maister@archlinux.us>
Date: Sat, 16 Nov 2013 13:45:23 +0100
Subject: [PATCH] Some V4L2 cleanups.

Avoids global state for buffers, and make processing loop a bit simpler.
---
 camera/video4linux2.c | 140 ++++++++++++++++++++++--------------------
 1 file changed, 73 insertions(+), 67 deletions(-)

diff --git a/camera/video4linux2.c b/camera/video4linux2.c
index 71f1f9607d..727b1b9803 100644
--- a/camera/video4linux2.c
+++ b/camera/video4linux2.c
@@ -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