libv4l: add video processing: whitebalance and normalize
authorhans@rhel5-devel.localdomain <hans@rhel5-devel.localdomain>
Mon, 13 Apr 2009 18:02:34 +0000 (20:02 +0200)
committerhans@rhel5-devel.localdomain <hans@rhel5-devel.localdomain>
Mon, 13 Apr 2009 18:02:34 +0000 (20:02 +0200)
From: Hans de Goede <hdegoede@redhat.com>

As the version number shows this work is the basis for a beta release of the
0.6.x series, the big change here is the addition of video processing to libv4l
currently this only does whitebalance and normalizing (which turns out
to be useless for most cams) but the basic framework for doing video
processing, and being able to control it through fake v4l2 controls using
for example v4l2ucp is there.
The initial version of this code was written by 3 of my computer science
students: Elmar Kleijn, Sjoerd Piepenbrink and Radjnies Bhansingh.
This initial hg commit is a cleaned up and somewhat bug fixed version of
their code.

Priority: normal

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
19 files changed:
lib/ChangeLog
lib/README
lib/TODO
lib/include/libv4lconvert.h
lib/libv4l2/Makefile
lib/libv4l2/libv4l2.c
lib/libv4l2/log.c
lib/libv4lconvert/Makefile
lib/libv4lconvert/control/libv4lcontrol-priv.h [new file with mode: 0644]
lib/libv4lconvert/control/libv4lcontrol.c [new file with mode: 0644]
lib/libv4lconvert/control/libv4lcontrol.h [new file with mode: 0644]
lib/libv4lconvert/flip.c
lib/libv4lconvert/libv4lconvert-priv.h
lib/libv4lconvert/libv4lconvert.c
lib/libv4lconvert/processing/bayerprocessing.c [new file with mode: 0644]
lib/libv4lconvert/processing/libv4lprocessing-priv.h [new file with mode: 0644]
lib/libv4lconvert/processing/libv4lprocessing.c [new file with mode: 0644]
lib/libv4lconvert/processing/libv4lprocessing.h [new file with mode: 0644]
lib/libv4lconvert/processing/rgbprocessing.c [new file with mode: 0644]

index 9c9a5a3..52b4296 100644 (file)
@@ -1,11 +1,16 @@
-libv4l-0.5.11
+libv4l-0.5.97
 -------------
+* As the version number shows this is a beta release of the 0.6.x series,
+  the big change here is the addition of video processing to libv4l
+  currently this only does whitebalance and normalizing (which turns out
+  to be useless for most cams) but the basic framework for doing video
+  processing, and being able to control it through fake v4l2 controls using
+  for example v4l2ucp is there.
+  The initial version of this code was written by 3 of my computer science
+  students: Elmar Kleijn, Sjoerd Piepenbrink and Radjnies Bhansingh
 * Add dependency generation to libv4l by: Gilles Gigan <gilles.gigan@gmail.com>
 * Add support to use orientation from VIDIOC_ENUMINPUT by:
   Adam Baker <linux@baker-net.org.uk>
-
-libv4l-0.5.10
--------------
 * sn9c20x cams have occasional bad jpeg frames, drop these to avoid the
   flickering effect they cause, by: Brian Johnson <brijohn@gmail.com>
 * adjust libv4l's upside down cam detection to also work with devices
index 3a20592..481d5b9 100644 (file)
@@ -15,14 +15,26 @@ libv4l consists of 3 different libraries:
 libv4lconvert
 -------------
 
-libv4lconvert offers functions to convert from any (known) pixelformat
-to V4l2_PIX_FMT_BGR24 or V4l2_PIX_FMT_YUV420.
-
-Currently the following source formats are supported:
-jpeg, mjpeg, bayer (all 4 variants: bggr, rggb, gbrg, grbg),
-spca501 (chip specific yuv 420 with interlaced components),
-spca561 (chip specific compressed gbrg bayer)
-For more details on the v4lconvert_ functions see libv4lconvert.h .
+libv4lconvert started as a library to convert from any (known) pixelformat to
+V4l2_PIX_FMT_BGR24, RGB24, YUV420 or YVU420.
+
+The list of know source formats is large and continually growing, so instead
+of keeping an (almost always outdated) list here in the README, I refer you
+to the source, see the list of defines at the top of
+libv4lconvert/libv4lconvert.c for the full list.
+For more details on the v4lconvert_ functions see libv4lconvert.h.
+
+Later on libv4lconvert was expanded to also be able to do various video
+processing functions improve webcam video quality on a software basis. So
+the name no longer 100% covers the functionality. The video processing is
+split in to 2 parts, libv4lconvert/control and libv4lconvert/processing.
+
+The control part is used to offer video controls which can be used to control
+the video processing functions made available by libv4lconvert/processing.
+These controls are stored application wide (untill reboot) by using a
+persistent shared memory object.
+
+libv4lconvert/processing offers the actual video processing functionality.
 
 
 libv4l1
index debb9c7..2bd2fc9 100644 (file)
--- a/lib/TODO
+++ b/lib/TODO
@@ -8,3 +8,6 @@
  necessary to implement CGMBUF in the kernel for each driver.
 
 -take the possibility of pitch != width into account everywhere
+
+-only report faked formats in list formats and only allow setting fake
+ formats when processing or flipping.
index 626c434..fcc31da 100644 (file)
@@ -81,6 +81,14 @@ LIBV4L_PUBLIC int v4lconvert_enum_framesizes(struct v4lconvert_data *data,
 LIBV4L_PUBLIC int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
   struct v4l2_frmivalenum *frmival);
 
+/* Pass calls to query, get and set video controls to the libv4lcontrol class */
+LIBV4L_PUBLIC int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data,
+  void *arg);
+LIBV4L_PUBLIC int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data,
+  void *arg);
+LIBV4L_PUBLIC int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data,
+  void *arg);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index c3af123..4356c09 100644 (file)
@@ -3,7 +3,7 @@ override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden
 CFLAGS := -g -O1
 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes
 
-LIBS_libv4l2  = -lpthread
+LIBS_libv4l2  = -lpthread -lrt
 
 V4L2_OBJS     = libv4l2.o log.o
 V4L2CONVERT   = v4l2convert.so
index b6ddef6..91e0193 100644 (file)
@@ -676,6 +676,9 @@ int v4l2_ioctl (int fd, unsigned long int request, ...)
 
   /* Is this a capture request and do we need to take the stream lock? */
   switch (request) {
+    case VIDIOC_QUERYCTRL:
+    case VIDIOC_G_CTRL:
+    case VIDIOC_S_CTRL:
     case VIDIOC_QUERYCAP:
       is_capture_request = 1;
       break;
@@ -739,6 +742,18 @@ int v4l2_ioctl (int fd, unsigned long int request, ...)
                         &devices[index].src_fmt, &devices[index].dest_fmt);
 
   switch (request) {
+    case VIDIOC_QUERYCTRL:
+      result = v4lconvert_vidioc_queryctrl(devices[index].convert, arg);
+      break;
+
+    case VIDIOC_G_CTRL:
+      result = v4lconvert_vidioc_g_ctrl(devices[index].convert, arg);
+      break;
+
+    case VIDIOC_S_CTRL:
+      result = v4lconvert_vidioc_s_ctrl(devices[index].convert, arg);
+      break;
+
     case VIDIOC_QUERYCAP:
       {
        struct v4l2_capability *cap = arg;
index c29086f..251b46e 100644 (file)
@@ -1,5 +1,7 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+#             (C) 2008 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008 Hans de Goede <hdegoede@redhat.com>
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
index 95b9d31..64f6690 100644 (file)
@@ -12,7 +12,9 @@ endif
 
 CONVERT_OBJS  = libv4lconvert.o tinyjpeg.o sn9c10x.o sn9c20x.o pac207.o \
                mr97310a.o flip.o crop.o jidctflt.o spca561-decompress.o \
-               rgbyuv.o spca501.o sq905c.o bayer.o hm12.o
+               rgbyuv.o spca501.o sq905c.o bayer.o hm12.o \
+               control/libv4lcontrol.o processing/libv4lprocessing.o \
+               processing/rgbprocessing.o processing/bayerprocessing.o
 TARGETS       = $(CONVERT_LIB) libv4lconvert.pc
 INCLUDES      = ../include/libv4lconvert.h
 
@@ -60,7 +62,8 @@ endif
        install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig
 
 clean::
-       rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~ *.orig *.rej
+       rm -f *.a *.so* *.o *.d */*.o */*.d libv4lconvert.pc log *~ */*~
+       rm -f *.orig *.rej */*.orig */*.rej
 
 %.d:: %.c
        @set -e; rm -f $@; \
diff --git a/lib/libv4lconvert/control/libv4lcontrol-priv.h b/lib/libv4lconvert/control/libv4lcontrol-priv.h
new file mode 100644 (file)
index 0000000..59305a2
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef __LIBV4LCONTROL_PRIV_H
+#define __LIBV4LCONTROL_PRIV_H
+
+#define V4LCONTROL_SHM_SIZE 4096
+
+struct v4lcontrol_data {
+  int fd; /* Knowledge of the fd is needed in original syscalls */
+  unsigned int controls; /* Which controls to use for this device */
+  unsigned int *shm_values; /* shared memory control value store */
+};
+
+#endif
diff --git a/lib/libv4lconvert/control/libv4lcontrol.c b/lib/libv4lconvert/control/libv4lcontrol.c
new file mode 100644 (file)
index 0000000..cb4e343
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syscall.h>
+#include "libv4lcontrol.h"
+#include "libv4lcontrol-priv.h"
+
+/* These headers are not needed by us, but by linux/videodev2.h,
+   which is broken on some systems and doesn't include them itself :( */
+#include <sys/time.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+/* end broken header workaround includes */
+#include <linux/videodev2.h>
+
+static void v4lcontrol_init(struct v4lcontrol_data *data, int first_time)
+{
+  /* FIXME: Temporary spoof future communication with driver by always enabling
+     the fake controls */
+  data->controls = (1 << V4LCONTROL_WHITEBALANCE) |
+    (1 << V4LCONTROL_NORMALIZE) | (1 << V4LCONTROL_NORM_LOW_BOUND) |
+    (1 << V4LCONTROL_NORM_HIGH_BOUND);
+
+  if (first_time) {
+    /* Initialize the new shm object when created */
+    memset(data->shm_values, 0, sizeof(V4LCONTROL_SHM_SIZE));
+    data->shm_values[V4LCONTROL_WHITEBALANCE] = 1;
+    data->shm_values[V4LCONTROL_NORM_HIGH_BOUND] = 255;
+  }
+}
+
+struct v4lcontrol_data *v4lcontrol_create(int fd)
+{
+  int shm_fd;
+  int init = 0;
+  char shm_name[256];
+  struct v4l2_capability cap;
+
+  struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data));
+
+  if (!data)
+    return NULL;
+
+  syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap);
+  snprintf(shm_name, 256, "/%s:%s", cap.bus_info, cap.card);
+
+  /* Open the shared memory object identified by shm_name */
+  if ((shm_fd = shm_open(shm_name, (O_CREAT | O_EXCL | O_RDWR),
+                        (S_IREAD | S_IWRITE))) >= 0)
+    init = 1;
+  else if ((shm_fd = shm_open(shm_name, O_RDWR, (S_IREAD | S_IWRITE))) < 0)
+    goto error;
+
+  /* Set the shared memory size */
+  ftruncate(shm_fd, V4LCONTROL_SHM_SIZE);
+
+  /* Retreive a pointer to the shm object */
+  data->shm_values = mmap(NULL, V4LCONTROL_SHM_SIZE, (PROT_READ | PROT_WRITE),
+                         MAP_SHARED, shm_fd, 0);
+  close(shm_fd);
+
+  if (data->shm_values == MAP_FAILED)
+    goto error;
+
+  v4lcontrol_init(data, init); /* Set the driver defined fake controls */
+
+  data->fd = fd;
+
+  return data;
+
+error:
+  free(data);
+  return NULL;
+}
+
+void v4lcontrol_destroy(struct v4lcontrol_data *data)
+{
+  munmap(data->shm_values, V4LCONTROL_SHM_SIZE);
+  free(data);
+}
+
+/* FIXME get better CID's for normalize */
+struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = {
+{
+  .id = V4L2_CID_AUTO_WHITE_BALANCE,
+  .type = V4L2_CTRL_TYPE_BOOLEAN,
+  .name =  "Whitebalance",
+  .minimum = 0,
+  .maximum = 1,
+  .step = 1,
+  .default_value = 1,
+  .flags = 0
+},
+{
+  .id = V4L2_CID_DO_WHITE_BALANCE,
+  .type = V4L2_CTRL_TYPE_BOOLEAN,
+  .name =  "Normalize",
+  .minimum = 0,
+  .maximum = 1,
+  .step = 1,
+  .default_value = 0,
+  .flags = 0
+},
+{
+  .id = V4L2_CID_BLACK_LEVEL,
+  .type = V4L2_CTRL_TYPE_INTEGER,
+  .name =  "Normalize: low bound",
+  .minimum = 0,
+  .maximum = 127,
+  .step = 1,
+  .default_value = 0,
+  .flags = 0
+},
+{
+  .id = V4L2_CID_WHITENESS,
+  .type = V4L2_CTRL_TYPE_INTEGER,
+  .name =  "Normalize: high bound",
+  .minimum = 128,
+  .maximum = 255,
+  .step = 1,
+  .default_value = 255,
+  .flags = 0
+},
+};
+
+int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
+{
+  int i;
+  struct v4l2_queryctrl *ctrl = arg;
+
+  for (i = 0; i < V4LCONTROL_COUNT; i++)
+    if ((data->controls & (1 << i)) &&
+       ctrl->id == fake_controls[i].id) {
+      memcpy(ctrl, &fake_controls[i], sizeof(struct v4l2_queryctrl));
+      return 0;
+    }
+
+  return syscall(SYS_ioctl, data->fd, VIDIOC_QUERYCTRL, arg);
+}
+
+int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg)
+{
+  int i;
+  struct v4l2_control *ctrl = arg;
+
+  for (i = 0; i < V4LCONTROL_COUNT; i++)
+    if ((data->controls & (1 << i)) &&
+       ctrl->id == fake_controls[i].id) {
+      ctrl->value = data->shm_values[i];
+      return 0;
+    }
+
+  return syscall(SYS_ioctl, data->fd, VIDIOC_G_CTRL, arg);
+}
+
+int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
+{
+  int i;
+  struct v4l2_control *ctrl = arg;
+
+  for (i = 0; i < V4LCONTROL_COUNT; i++)
+    if ((data->controls & (1 << i)) &&
+       ctrl->id == fake_controls[i].id) {
+      if (ctrl->value > fake_controls[i].maximum ||
+         ctrl->value < fake_controls[i].minimum) {
+       errno = EINVAL;
+       return -1;
+      }
+
+      data->shm_values[i] = ctrl->value;
+      return 0;
+    }
+
+  return syscall(SYS_ioctl, data->fd, VIDIOC_S_CTRL, arg);
+}
+
+int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl)
+{
+  if (data->controls & (1 << ctrl))
+    return data->shm_values[ctrl];
+
+  return 0;
+}
diff --git a/lib/libv4lconvert/control/libv4lcontrol.h b/lib/libv4lconvert/control/libv4lcontrol.h
new file mode 100644 (file)
index 0000000..3611304
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef __LIBV4LCONTROL_H
+#define __LIBV4LCONTROL_H
+
+enum { V4LCONTROL_WHITEBALANCE, V4LCONTROL_NORMALIZE,
+  V4LCONTROL_NORM_LOW_BOUND, V4LCONTROL_NORM_HIGH_BOUND, V4LCONTROL_COUNT };
+
+struct v4lcontrol_data;
+
+struct v4lcontrol_data* v4lcontrol_create(int fd);
+void v4lcontrol_destroy(struct v4lcontrol_data *data);
+
+/* Functions used by v4lprocessing to get the control state */
+int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl);
+
+/* Functions used by v4lconvert to pass vidioc calls from libv4l2 */
+int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg);
+int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg);
+int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg);
+
+#endif
index f47afde..9835f04 100644 (file)
@@ -106,37 +106,46 @@ static void v4lconvert_rotate90_yuv420(const unsigned char *src,
     }
 }
 
-void v4lconvert_rotate(unsigned char *src, unsigned char *dest,
-  int width, int height, unsigned int pix_fmt, int rotate)
+void v4lconvert_rotate90(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt)
 {
-  switch (rotate) {
-  case 0:
-    break;
-  case 90:
-    switch (pix_fmt) {
-      case V4L2_PIX_FMT_RGB24:
-      case V4L2_PIX_FMT_BGR24:
-       v4lconvert_rotate90_rgbbgr24(src, dest, width, height);
-       break;
-      case V4L2_PIX_FMT_YUV420:
-      case V4L2_PIX_FMT_YVU420:
-       v4lconvert_rotate90_yuv420(src, dest, width, height);
-       break;
-    }
-    break;
-  case 180:
-    switch (pix_fmt) {
+  int tmp;
+
+  tmp = fmt->fmt.pix.width;
+  fmt->fmt.pix.width = fmt->fmt.pix.height;
+  fmt->fmt.pix.height = tmp;
+
+  switch (fmt->fmt.pix.pixelformat) {
+    case V4L2_PIX_FMT_RGB24:
+    case V4L2_PIX_FMT_BGR24:
+      v4lconvert_rotate90_rgbbgr24(src, dest, fmt->fmt.pix.width,
+                                  fmt->fmt.pix.height);
+      break;
+    case V4L2_PIX_FMT_YUV420:
+    case V4L2_PIX_FMT_YVU420:
+      v4lconvert_rotate90_yuv420(src, dest, fmt->fmt.pix.width,
+                                fmt->fmt.pix.height);
+      break;
+  }
+}
+
+void v4lconvert_flip(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt, int flags)
+{
+  /* FIXME implement separate vflipping and hflipping, for now we always
+     rotate 180 when vflip is selected! */
+  if (flags & V4LCONVERT_VFLIP) {
+    switch (fmt->fmt.pix.pixelformat) {
       case V4L2_PIX_FMT_RGB24:
       case V4L2_PIX_FMT_BGR24:
-       v4lconvert_rotate180_rgbbgr24(src, dest, width, height);
+       v4lconvert_rotate180_rgbbgr24(src, dest, fmt->fmt.pix.width,
+                                     fmt->fmt.pix.height);
        break;
       case V4L2_PIX_FMT_YUV420:
       case V4L2_PIX_FMT_YVU420:
-       v4lconvert_rotate180_yuv420(src, dest, width, height);
+       v4lconvert_rotate180_yuv420(src, dest, fmt->fmt.pix.width,
+                                   fmt->fmt.pix.height);
        break;
     }
-    break;
-  default:
-    printf("FIXME add %d degrees rotation\n", rotate);
   }
 }
index 4e6130d..acf921a 100644 (file)
 
 #include <stdio.h>
 #include "libv4lconvert.h"
+#include "control/libv4lcontrol.h"
+#include "processing/libv4lprocessing.h"
 #include "tinyjpeg.h"
 
+/* Workaround these potentially missing from videodev2.h */
+
 #ifndef V4L2_PIX_FMT_SPCA501
 #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */
 #endif
 #define V4L2_PIX_FMT_SN9C20X_I420  v4l2_fourcc('S', '9', '2', '0')
 #endif
 
+#ifndef V4L2_IN_ST_HFLIP
+#define V4L2_IN_ST_HFLIP       0x00000010 /* Frames are flipped horizontally */
+#endif
+
+#ifndef V4L2_IN_ST_VFLIP
+#define V4L2_IN_ST_VFLIP       0x00000020 /* Frames are flipped vertically */
+#endif
+
+
 #define V4LCONVERT_ERROR_MSG_SIZE 256
 #define V4LCONVERT_MAX_FRAMESIZES 16
 
   "v4l-convert: error " __VA_ARGS__)
 
 /* Card flags */
-#define V4LCONVERT_ROTATE_90  0x01
-#define V4LCONVERT_ROTATE_180 0x02
-#define V4LCONVERT_IS_UVC     0x04
-#define V4LCONVERT_IS_SN9C20X 0x08
+#define V4LCONVERT_HFLIP                 0x01
+#define V4LCONVERT_VFLIP                 0x02
+#define V4LCONVERT_JPEG_ROTATE_90_HACK   0x04
+#define V4LCONVERT_IS_UVC                0x08
+#define V4LCONVERT_IS_SN9C20X            0x10
 
 /* Pixformat flags */
 #define V4LCONVERT_COMPRESSED 0x01
@@ -104,12 +118,18 @@ struct v4lconvert_data {
   struct jdec_private *jdec;
   struct v4l2_frmsizeenum framesizes[V4LCONVERT_MAX_FRAMESIZES];
   unsigned int no_framesizes;
-  int convert_buf_size;
-  int rotate_buf_size;
+  int convert1_buf_size;
+  int convert2_buf_size;
+  int rotate90_buf_size;
+  int flip_buf_size;
   int convert_pixfmt_buf_size;
-  unsigned char *convert_buf;
-  unsigned char *rotate_buf;
+  unsigned char *convert1_buf;
+  unsigned char *convert2_buf;
+  unsigned char *rotate90_buf;
+  unsigned char *flip_buf;
   unsigned char *convert_pixfmt_buf;
+  struct v4lcontrol_data *control;
+  struct v4lprocessing_data *processing;
 };
 
 struct v4lconvert_flags_info {
@@ -210,8 +230,11 @@ void v4lconvert_hm12_to_bgr24(const unsigned char *src,
 void v4lconvert_hm12_to_yuv420(const unsigned char *src,
   unsigned char *dst, int width, int height, int yvu);
 
-void v4lconvert_rotate(unsigned char *src, unsigned char *dest,
-  int width, int height, unsigned int pix_fmt, int rotate);
+void v4lconvert_rotate90(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt);
+
+void v4lconvert_flip(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt, int flags);
 
 void v4lconvert_crop(unsigned char *src, unsigned char *dest,
   const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt);
index f3191f1..6fbc1af 100644 (file)
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0]))
 
-/* Workaround this potentially being missing from videodev2.h */
-#ifndef V4L2_IN_ST_VFLIP
-#define V4L2_IN_ST_VFLIP       0x00000020 /* Output is flipped vertically */
-#endif
-
 /* Note for proper functioning of v4lconvert_enum_fmt the first entries in
   supported_src_pixfmts must match with the entries in supported_dst_pixfmts */
 #define SUPPORTED_DST_PIXFMTS \
@@ -77,10 +72,10 @@ static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = {
 
 /* List of cams which need special flags */
 static const struct v4lconvert_flags_info v4lconvert_flags[] = {
-  { 0x0471, 0x0325, V4LCONVERT_ROTATE_180 }, /* Philips SPC200NC */
-  { 0x0471, 0x0326, V4LCONVERT_ROTATE_180 }, /* Philips SPC300NC */
-  { 0x0471, 0x032d, V4LCONVERT_ROTATE_180 }, /* Philips SPC210NC */
-  { 0x093a, 0x2476, V4LCONVERT_ROTATE_180 }, /* Genius E-M 112 */
+  { 0x0471, 0x0325, V4LCONVERT_HFLIP|V4LCONVERT_VFLIP }, /* Philips SPC200NC */
+  { 0x0471, 0x0326, V4LCONVERT_HFLIP|V4LCONVERT_VFLIP }, /* Philips SPC300NC */
+  { 0x0471, 0x032d, V4LCONVERT_HFLIP|V4LCONVERT_VFLIP }, /* Philips SPC210NC */
+  { 0x093a, 0x2476, V4LCONVERT_HFLIP|V4LCONVERT_VFLIP }, /* Genius E-M 112 */
 };
 
 /* List of well known resolutions which we can get by cropping somewhat larger
@@ -214,10 +209,10 @@ struct v4lconvert_data *v4lconvert_create(int fd)
   data->flags = v4lconvert_get_flags(data->fd);
   if ((syscall(SYS_ioctl, fd, VIDIOC_G_INPUT, &input.index) == 0) &&
       (syscall(SYS_ioctl, fd, VIDIOC_ENUMINPUT, &input) == 0)) {
-    /* Don't yet support independent HFLIP and VFLIP so getting
-     * image the right way up is highest priority. */
+    if (input.status & V4L2_IN_ST_HFLIP)
+      data->flags |= V4LCONVERT_HFLIP;
     if (input.status & V4L2_IN_ST_VFLIP)
-      data->flags |= V4LCONVERT_ROTATE_180;
+      data->flags |= V4LCONVERT_VFLIP;
   }
   if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) {
     if (!strcmp((char *)cap.driver, "uvcvideo"))
@@ -226,18 +221,35 @@ struct v4lconvert_data *v4lconvert_create(int fd)
       data->flags |= V4LCONVERT_IS_SN9C20X;
   }
 
+  data->control = v4lcontrol_create(fd);
+  if (!data->control) {
+    free(data);
+    return NULL;
+  }
+
+  data->processing = v4lprocessing_create(data->control);
+  if (!data->processing) {
+    v4lcontrol_destroy(data->control);
+    free(data);
+    return NULL;
+  }
+
   return data;
 }
 
 void v4lconvert_destroy(struct v4lconvert_data *data)
 {
+  v4lprocessing_destroy(data->processing);
+  v4lcontrol_destroy(data->control);
   if (data->jdec) {
     unsigned char *comps[3] = { NULL, NULL, NULL };
     tinyjpeg_set_components(data->jdec, comps, 3);
     tinyjpeg_free(data->jdec);
   }
-  free(data->convert_buf);
-  free(data->rotate_buf);
+  free(data->convert1_buf);
+  free(data->convert2_buf);
+  free(data->rotate90_buf);
+  free(data->flip_buf);
   free(data->convert_pixfmt_buf);
   free(data);
 }
@@ -490,7 +502,7 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data,
      src_fmt->fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat)
     return 1; /* Formats differ */
 
-  if (!(data->flags & (V4LCONVERT_ROTATE_90|V4LCONVERT_ROTATE_180)))
+  if (!(data->flags & (V4LCONVERT_HFLIP|V4LCONVERT_VFLIP)))
     return 0; /* Formats identical and we don't need flip */
 
   /* Formats are identical, but we need flip, do we support the dest_fmt? */
@@ -500,6 +512,32 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data,
     return 1; /* Needs flip and thus conversion */
 }
 
+static int v4lconvert_processing_needs_double_conversion(
+  unsigned int src_pix_fmt, unsigned int dest_pix_fmt)
+{
+  switch (src_pix_fmt) {
+    case V4L2_PIX_FMT_RGB24:
+    case V4L2_PIX_FMT_BGR24:
+    case V4L2_PIX_FMT_SPCA561:
+    case V4L2_PIX_FMT_SN9C10X:
+    case V4L2_PIX_FMT_PAC207:
+    case V4L2_PIX_FMT_MR97310A:
+    case V4L2_PIX_FMT_SQ905C:
+    case V4L2_PIX_FMT_SBGGR8:
+    case V4L2_PIX_FMT_SGBRG8:
+    case V4L2_PIX_FMT_SGRBG8:
+    case V4L2_PIX_FMT_SRGGB8:
+      return 0;
+  }
+  switch (dest_pix_fmt) {
+    case V4L2_PIX_FMT_RGB24:
+    case V4L2_PIX_FMT_BGR24:
+      return 0;
+  }
+
+  return 1;
+}
+
 static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data,
   int needed, unsigned char **buf, int *buf_size)
 {
@@ -519,14 +557,14 @@ static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data,
 
 static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
   unsigned char *src, int src_size, unsigned char *dest,
-  const struct v4l2_format *src_fmt, unsigned int dest_pix_fmt)
+  struct v4l2_format *fmt, unsigned int dest_pix_fmt)
 {
   unsigned int header_width, header_height;
   int result = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
   unsigned char *components[3];
-  unsigned int src_pix_fmt = src_fmt->fmt.pix.pixelformat;
-  unsigned int width  = src_fmt->fmt.pix.width;
-  unsigned int height = src_fmt->fmt.pix.height;
+  unsigned int src_pix_fmt = fmt->fmt.pix.pixelformat;
+  unsigned int width  = fmt->fmt.pix.width;
+  unsigned int height = fmt->fmt.pix.height;
 
   switch (src_pix_fmt) {
     case V4L2_PIX_FMT_PJPG:
@@ -554,12 +592,14 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
       if (header_width != width || header_height != height) {
        /* Check for (pixart) rotated JPEG */
        if (header_width == height && header_height == width) {
-         if (!(data->flags & V4LCONVERT_ROTATE_90)) {
+         if (!(data->flags & V4LCONVERT_JPEG_ROTATE_90_HACK)) {
            V4LCONVERT_ERR("JPEG needs 90 degree rotation\n");
-           data->flags |= V4LCONVERT_ROTATE_90;
+           data->flags |= V4LCONVERT_JPEG_ROTATE_90_HACK;
            errno = EAGAIN;
            return -1;
          }
+         fmt->fmt.pix.width = header_width;
+         fmt->fmt.pix.height = header_height;
        } else {
          V4LCONVERT_ERR("unexpected width / height in JPEG header"
                         "expected: %ux%u, header: %ux%u\n", width, height,
@@ -690,6 +730,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
     case V4L2_PIX_FMT_SQ905C:
     {
       unsigned char *tmpbuf;
+      struct v4l2_format tmpfmt = *fmt;
 
       tmpbuf = v4lconvert_alloc_buffer(data, width * height,
            &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size);
@@ -699,27 +740,31 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
       switch (src_pix_fmt) {
        case V4L2_PIX_FMT_SPCA561:
          v4lconvert_decode_spca561(src, tmpbuf, width, height);
-         src_pix_fmt = V4L2_PIX_FMT_SGBRG8;
+         tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG8;
          break;
        case V4L2_PIX_FMT_SN9C10X:
          v4lconvert_decode_sn9c10x(src, tmpbuf, width, height);
-         src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+         tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
          break;
        case V4L2_PIX_FMT_PAC207:
          v4lconvert_decode_pac207(src, tmpbuf, width, height);
-         src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+         tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
          break;
        case V4L2_PIX_FMT_MR97310A:
          v4lconvert_decode_mr97310a(src, tmpbuf, width, height);
-         src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+         tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
          break;
        case V4L2_PIX_FMT_SQ905C:
          v4lconvert_decode_sq905c(src, tmpbuf, width, height);
-         src_pix_fmt = V4L2_PIX_FMT_SRGGB8;
+         tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
          break;
       }
-      src = tmpbuf;
+      /* Do processing on the tmp buffer, because doing it on bayer data is
+        cheaper, and bayer == rgb and our dest_fmt may be yuv */
+      v4lprocessing_processing(data->processing, tmpbuf, &tmpfmt);
       /* Deliberate fall through to raw bayer fmt code! */
+      src_pix_fmt = tmpfmt.fmt.pix.pixelformat;
+      src = tmpbuf;
     }
 
     /* Raw bayer formats */
@@ -749,10 +794,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
        v4lconvert_swap_rgb(src, dest, width, height);
        break;
       case V4L2_PIX_FMT_YUV420:
-       v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 0);
+       v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0);
        break;
       case V4L2_PIX_FMT_YVU420:
-       v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 1);
+       v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1);
        break;
       }
       break;
@@ -763,10 +808,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
        v4lconvert_swap_rgb(src, dest, width, height);
        break;
       case V4L2_PIX_FMT_YUV420:
-       v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 0);
+       v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0);
        break;
       case V4L2_PIX_FMT_YVU420:
-       v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 1);
+       v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1);
        break;
       }
       break;
@@ -782,7 +827,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                                   height, 0);
        break;
       case V4L2_PIX_FMT_YVU420:
-       v4lconvert_swap_uv(src, dest, src_fmt);
+       v4lconvert_swap_uv(src, dest, fmt);
        break;
       }
       break;
@@ -798,7 +843,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                                   height, 1);
        break;
       case V4L2_PIX_FMT_YUV420:
-       v4lconvert_swap_uv(src, dest, src_fmt);
+       v4lconvert_swap_uv(src, dest, fmt);
        break;
       }
       break;
@@ -861,6 +906,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
       errno = EINVAL;
       return -1;
   }
+
+  fmt->fmt.pix.pixelformat = dest_pix_fmt;
+  v4lconvert_fixup_fmt(fmt);
+
   return 0;
 }
 
@@ -869,8 +918,11 @@ int v4lconvert_convert(struct v4lconvert_data *data,
   const struct v4l2_format *dest_fmt, /* in */
   unsigned char *src, int src_size, unsigned char *dest, int dest_size)
 {
-  int res, dest_needed, temp_needed, convert = 0, rotate = 0, crop = 0;
-  unsigned char *convert_dest = dest, *rotate_src = src, *rotate_dest = dest;
+  int res, dest_needed, temp_needed, processing, convert = 0, crop = 0;
+  unsigned char *convert1_dest = dest;
+  unsigned char *convert2_src = src, *convert2_dest = dest;
+  unsigned char *rotate90_src = src, *rotate90_dest = dest;
+  unsigned char *flip_src = src, *flip_dest = dest;
   unsigned char *crop_src = src;
   struct v4l2_format my_src_fmt = *src_fmt;
   struct v4l2_format my_dest_fmt = *dest_fmt;
@@ -915,52 +967,98 @@ int v4lconvert_convert(struct v4lconvert_data *data,
     return -1;
   }
 
-  if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat)
-    convert = 1;
+  processing = v4lprocessing_pre_processing(data->processing);
 
-  if (data->flags & V4LCONVERT_ROTATE_90)
-    rotate += 90;
-  if (data->flags & V4LCONVERT_ROTATE_180)
-    rotate += 180;
+  /* Sometimes we need foo -> rgb -> bar as video processing (whitebalance,
+     etc.) can only be done on rgb data */
+  if (processing && v4lconvert_processing_needs_double_conversion(
+                                            my_src_fmt.fmt.pix.pixelformat,
+                                            my_dest_fmt.fmt.pix.pixelformat))
+    convert = 2;
+  else if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat)
+    convert = 1;
 
   if (my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width ||
       my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height)
     crop = 1;
 
-  /* convert_pixfmt -> rotate -> crop, all steps are optional */
-  if (convert && (rotate || crop)) {
-    convert_dest = v4lconvert_alloc_buffer(data, temp_needed,
-                    &data->convert_buf, &data->convert_buf_size);
-    if (!convert_dest)
+  /* convert_pixfmt (only if convert == 2) -> processing -> convert_pixfmt ->
+     rotate -> flip -> crop, all steps are optional */
+  if (convert == 2) {
+    convert1_dest = v4lconvert_alloc_buffer(data,
+                    my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3,
+                    &data->convert1_buf, &data->convert1_buf_size);
+    if (!convert1_dest)
       return -1;
 
-    rotate_src = crop_src = convert_dest;
+    convert2_src = convert1_dest;
   }
 
-  if (rotate && crop) {
-    rotate_dest = v4lconvert_alloc_buffer(data, temp_needed,
-                   &data->rotate_buf, &data->rotate_buf_size);
-    if (!rotate_dest)
+  if (convert && (data->flags & (V4LCONVERT_JPEG_ROTATE_90_HACK |
+                           V4LCONVERT_VFLIP | V4LCONVERT_HFLIP) || crop)) {
+    convert2_dest = v4lconvert_alloc_buffer(data, temp_needed,
+                    &data->convert2_buf, &data->convert2_buf_size);
+    if (!convert2_dest)
       return -1;
 
-    crop_src = rotate_dest;
+    rotate90_src = flip_src = crop_src = convert2_dest;
   }
 
-  if (convert) {
-    res = v4lconvert_convert_pixfmt(data, src, src_size, convert_dest,
+  if ((data->flags & V4LCONVERT_JPEG_ROTATE_90_HACK) &&
+      ((data->flags & (V4LCONVERT_VFLIP | V4LCONVERT_HFLIP)) || crop)) {
+    rotate90_dest = v4lconvert_alloc_buffer(data, temp_needed,
+                   &data->rotate90_buf, &data->rotate90_buf_size);
+    if (!rotate90_dest)
+      return -1;
+
+    flip_src = crop_src = rotate90_dest;
+  }
+
+  if ((data->flags & (V4LCONVERT_VFLIP | V4LCONVERT_HFLIP)) && crop) {
+    flip_dest = v4lconvert_alloc_buffer(data, temp_needed, &data->flip_buf,
+                                       &data->flip_buf_size);
+    if (!flip_dest)
+      return -1;
+
+    crop_src = flip_dest;
+  }
+
+  /* Done setting sources / dest and allocating intermediate buffers,
+     real conversion / processing / ... starts here. */
+  if (convert == 2) {
+    res = v4lconvert_convert_pixfmt(data, src, src_size, convert1_dest,
                                    &my_src_fmt,
+                                   V4L2_PIX_FMT_RGB24);
+    if (res)
+      return res;
+
+    src_size = my_src_fmt.fmt.pix.sizeimage;
+  }
+
+  if (processing)
+    v4lprocessing_processing(data->processing, convert2_src, &my_src_fmt);
+
+  if (convert) {
+    res = v4lconvert_convert_pixfmt(data, convert2_src, src_size,
+                                   convert2_dest, &my_src_fmt,
                                    my_dest_fmt.fmt.pix.pixelformat);
     if (res)
       return res;
 
-    my_src_fmt.fmt.pix.pixelformat = my_dest_fmt.fmt.pix.pixelformat;
-    v4lconvert_fixup_fmt(&my_src_fmt);
+    src_size = my_src_fmt.fmt.pix.sizeimage;
   }
 
-  if (rotate)
-    v4lconvert_rotate(rotate_src, rotate_dest,
-                     my_src_fmt.fmt.pix.width, my_src_fmt.fmt.pix.height,
-                     my_src_fmt.fmt.pix.pixelformat, rotate);
+  /* We call processing here again in case the source format was not
+     rgb, but the dest is. v4lprocessing checks it self it only actually
+     does the processing once per frame. */
+  if (processing)
+    v4lprocessing_processing(data->processing, rotate90_src, &my_src_fmt);
+
+  if (data->flags & V4LCONVERT_JPEG_ROTATE_90_HACK)
+    v4lconvert_rotate90(rotate90_src, rotate90_dest, &my_src_fmt);
+
+  if (data->flags & (V4LCONVERT_VFLIP | V4LCONVERT_HFLIP))
+    v4lconvert_flip(flip_src, flip_dest, &my_src_fmt, data->flags);
 
   if (crop)
     v4lconvert_crop(crop_src, dest, &my_src_fmt, &my_dest_fmt);
@@ -1135,3 +1233,15 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
 
   return res;
 }
+
+int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data, void *arg) {
+  return v4lcontrol_vidioc_queryctrl(data->control, arg);
+}
+
+int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data, void *arg) {
+  return v4lcontrol_vidioc_g_ctrl(data->control, arg);
+}
+
+int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, void *arg){
+  return v4lcontrol_vidioc_s_ctrl(data->control, arg);
+}
diff --git a/lib/libv4lconvert/processing/bayerprocessing.c b/lib/libv4lconvert/processing/bayerprocessing.c
new file mode 100644 (file)
index 0000000..f4cc992
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "libv4lprocessing-priv.h"
+#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
+
+void bayer_normalize_analyse(unsigned char *buf, int width, int height,
+  struct v4lprocessing_data *data)
+{
+  int value, max = 0, min = 255;
+  unsigned char *buf_end = buf + width * height;
+
+  while (buf < buf_end) {
+    value = *buf++;
+    if (max < value)
+      max = value;
+    if (min > value)
+      min = value;
+  }
+
+  data->comp1 = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min);
+  data->offset1 = min;
+  data->offset2 = data->norm_low_bound;
+}
+
+void bayer_whitebalance_analyse(unsigned char *src_buffer, int width,
+  int height, unsigned int pix_fmt, struct v4lprocessing_data *data)
+{
+  int i, j, x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+  float green_avg, x_avg, y_avg, avg_avg;
+  unsigned char *buf = src_buffer;
+
+  int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 ||
+    pix_fmt == V4L2_PIX_FMT_SGRBG8;
+
+  for (i = 0; i < height; i += 2) {
+    for (j = 0; j < width; j += 2) {
+      x1 += *buf++;
+      x2 += *buf++;
+    }
+    for (j = 0; j < width; j += 2) {
+      y1 += *buf++;
+      y2 += *buf++;
+    }
+  }
+
+  if (start_with_green) {
+    green_avg = (x1 + y2) / 2;
+    x_avg = x2;
+    y_avg = y1;
+  } else {
+    green_avg = (x2 + y1) / 2;
+    x_avg = x1;
+    y_avg = y2;
+  }
+
+  avg_avg = (green_avg + x_avg + y_avg) / 3;
+
+  data->comp1 = (avg_avg / green_avg) * 65536;
+  data->comp2 = (avg_avg / x_avg) * 65536;
+  data->comp3 = (avg_avg / y_avg) * 65536;
+}
+
+void bayer_normalize_whitebalance_analyse(unsigned char *buf, int width,
+  int height, unsigned int pix_fmt, struct v4lprocessing_data *data)
+{
+  int i, j, value, max = 0, min = 255, n_fac, x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+  float green_avg, x_avg, y_avg, avg_avg;
+
+  int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 ||
+    pix_fmt == V4L2_PIX_FMT_SGRBG8;
+
+  for (i = 0; i < height; i += 2) {
+    for (j = 0; j < width; j += 2) {
+      x1 += *buf;
+      value = *buf++;
+      if (max < value)
+       max = value;
+      if (min > value)
+       min = value;
+      x2 += *buf;
+      value = *buf++;
+      if (max < value)
+       max = value;
+      if (min > value)
+       min = value;
+    }
+    for (j = 0; j < width; j += 2) {
+      y1 += *buf;
+      value = *buf++;
+      if (max < value)
+       max = value;
+      if (min > value)
+       min = value;
+      y2 += *buf;
+      value = *buf++;
+      if (max < value)
+       max = value;
+      if (min > value)
+       min = value;
+    }
+  }
+
+  if (start_with_green) {
+    green_avg = (x1 + y2) / 2;
+    x_avg = x2;
+    y_avg = y1;
+  } else {
+    green_avg = (x2 + y1) / 2;
+    x_avg = x1;
+    y_avg = y2;
+  }
+
+  n_fac = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min);
+
+  avg_avg = (green_avg + x_avg + y_avg) / 3;
+
+  data->comp1 = (avg_avg / green_avg) * n_fac;
+  data->comp2 = (avg_avg / x_avg) * n_fac;
+  data->comp3 = (avg_avg / y_avg) * n_fac;
+
+  data->offset1 = min;
+  data->offset2 = data->norm_low_bound;
+}
+
+#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color)))
+#define TOP(color) (unsigned char)(((color)>0xff)?0xff:(color))
+
+void bayer_normalize(unsigned char *buf, int width,
+  int height, struct v4lprocessing_data *data)
+{
+  int value;
+  unsigned char *buf_end = buf + width * height;
+
+  while (buf < buf_end) {
+    value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2;
+    *buf++ = CLIP(value);
+  }
+}
+
+void bayer_whitebalance(unsigned char *src_buffer, int width,
+  int height, unsigned int pix_fmt,
+  struct v4lprocessing_data *data)
+{
+  int i, j, value;
+  int limit = width * height;
+  unsigned char *buf = src_buffer;
+
+  int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 ||
+    pix_fmt == V4L2_PIX_FMT_SGRBG8;
+
+  if (start_with_green) {
+    for (i = 0; i < height; i += 2) {
+      for (j = 0; j < width; j += 2) {
+       value = (*buf * data->comp1) >> 16;
+       *buf++ = TOP(value);
+       value = (*buf * data->comp2) >> 16;
+       *buf++ = TOP(value);
+      }
+      for (j = 0; j < width; j += 2) {
+       value = (*buf * data->comp3) >> 16;
+       *buf++ = TOP(value);
+       value = (*buf * data->comp1) >> 16;
+       *buf++ = TOP(value);
+      }
+    }
+  } else {
+    for (i = 0; i < height; i += 2) {
+      for (j = 0; j < width; j += 2) {
+       value = (*buf * data->comp2) >> 16;
+       *buf++ = TOP(value);
+       value = (*buf * data->comp1) >> 16;
+       *buf++ = TOP(value);
+      }
+      for (j = 0; j < width; j += 2) {
+       value = (*buf * data->comp1) >> 16;
+       *buf++ = TOP(value);
+       value = (*buf * data->comp3) >> 16;
+       *buf++ = TOP(value);
+      }
+    }
+  }
+}
+
+void bayer_normalize_whitebalance(unsigned char *src_buffer, int width,
+  int height, unsigned int pix_fmt,
+  struct v4lprocessing_data *data)
+{
+  int i, j, value;
+  int limit = width * height;
+  unsigned char *buf = src_buffer;
+
+  int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 ||
+    pix_fmt == V4L2_PIX_FMT_SGRBG8;
+
+  if (start_with_green) {
+    for (i = 0; i < height; i += 2) {
+      for (j = 0; j < width; j += 2) {
+       value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+       value = ((data->comp2 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+      }
+      for (j = 0; j < width; j += 2) {
+       value = ((data->comp3 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+       value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+      }
+    }
+  } else {
+    for (i = 0; i < height; i += 2) {
+      for (j = 0; j < width; j += 2) {
+       value = ((data->comp2 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+       value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+      }
+      for (j = 0; j < width; j += 2) {
+       value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+       value = ((data->comp3 * (*buf - data->offset1)) >> 16) + data->offset2;
+       *buf++ = CLIP(value);
+      }
+    }
+  }
+}
diff --git a/lib/libv4lconvert/processing/libv4lprocessing-priv.h b/lib/libv4lconvert/processing/libv4lprocessing-priv.h
new file mode 100644 (file)
index 0000000..f2aae37
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef __LIBV4LPROCESSING_PRIV_H
+#define __LIBV4LPROCESSING_PRIV_H
+
+#include "../control/libv4lcontrol.h"
+
+#define V4L2PROCESSING_PROCESS_NONE                              0x00
+#define V4L2PROCESSING_PROCESS_NORMALIZE                         0x01
+#define V4L2PROCESSING_PROCESS_WHITEBALANCE                      0x02
+#define V4L2PROCESSING_PROCESS_NORMALIZE_WHITEBALANCE            0x03
+#define V4L2PROCESSING_PROCESS_RGB_NORMALIZE                     0x01
+#define V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE                  0x02
+#define V4L2PROCESSING_PROCESS_RGB_NORMALIZE_WHITEBALANCE        0x03
+#define V4L2PROCESSING_PROCESS_BAYER_NORMALIZE                   0x11
+#define V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE                0x12
+#define V4L2PROCESSING_PROCESS_BAYER_NORMALIZE_WHITEBALANCE      0x13
+
+#define V4L2PROCESSING_UPDATE_RATE                               10
+
+struct v4lprocessing_data {
+  struct v4lcontrol_data *control;
+  int do_process;
+  /* Provides the current type of processing */
+  int process;
+  int norm_low_bound;
+  int norm_high_bound;
+  /* Counts the number of processed frames until a
+     V4L2PROCESSING_UPDATE_RATE overflow happens */
+  int processing_data_update;
+  /* Multiplication factors and offsets from the analyse functions */
+  int comp1;
+  int comp2;
+  int comp3;
+  int comp4;
+  int offset1;
+  int offset2;
+};
+
+/* Processing Bayer */
+void bayer_normalize_analyse(unsigned char *src_buffer, int width, int height,
+  struct v4lprocessing_data *data);
+void bayer_whitebalance_analyse(unsigned char *src_buffer, int width,
+  int height, unsigned int pix_fmt,
+  struct v4lprocessing_data *data);
+void bayer_normalize_whitebalance_analyse(unsigned char *src_buffer,
+  int width, int height, unsigned int pix_fmt,
+  struct v4lprocessing_data *data);
+void bayer_normalize(unsigned char *src_buffer, int width, int height,
+  struct v4lprocessing_data *data);
+void bayer_whitebalance(unsigned char *src_buffer, int width, int height,
+  unsigned int pix_fmt, struct v4lprocessing_data *data);
+void bayer_normalize_whitebalance(unsigned char *src_buffer, int width,
+  int height, unsigned int pix_fmt,
+  struct v4lprocessing_data *data);
+
+/* Processing RGB */
+void rgb_normalize_analyse(unsigned char *src_buffer, int width, int height,
+  struct v4lprocessing_data *data);
+void rgb_whitebalance_analyse(unsigned char *src_buffer, int width, int height,
+  struct v4lprocessing_data *data);
+void rgb_normalize_whitebalance_analyse(unsigned char *src_buffer,
+  int width, int height, struct v4lprocessing_data *data);
+void rgb_normalize(unsigned char *src_buffer, int width, int height,
+  struct v4lprocessing_data *data);
+void rgb_whitebalance(unsigned char *src_buffer, int width, int height,
+  struct v4lprocessing_data *data);
+void rgb_normalize_whitebalance(unsigned char *src_buffer, int width,
+  int height, struct v4lprocessing_data *data);
+
+#endif
diff --git a/lib/libv4lconvert/processing/libv4lprocessing.c b/lib/libv4lconvert/processing/libv4lprocessing.c
new file mode 100644 (file)
index 0000000..d61c292
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include <unistd.h>
+#include "libv4lprocessing.h"
+#include "libv4lprocessing-priv.h"
+#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
+
+struct v4lprocessing_data *v4lprocessing_create(struct v4lcontrol_data* control)
+{
+  struct v4lprocessing_data *data =
+    calloc(1, sizeof(struct v4lprocessing_data));
+
+  if (!data)
+    return NULL;
+
+  data->control = control;
+
+  return data;
+}
+
+void v4lprocessing_destroy(struct v4lprocessing_data *data)
+{
+  free(data);
+}
+
+static int v4lprocessing_get_process(struct v4lprocessing_data *data,
+  unsigned int pix_fmt)
+{
+  int process = V4L2PROCESSING_PROCESS_NONE;
+
+  switch(pix_fmt) {
+    case V4L2_PIX_FMT_SBGGR8:
+    case V4L2_PIX_FMT_SGBRG8:
+    case V4L2_PIX_FMT_SGRBG8:
+    case V4L2_PIX_FMT_SRGGB8:
+      if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORMALIZE)) {
+       process |= V4L2PROCESSING_PROCESS_BAYER_NORMALIZE;
+      }
+      if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE)) {
+       process |= V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE;
+      }
+      break;
+    case V4L2_PIX_FMT_RGB24:
+    case V4L2_PIX_FMT_BGR24:
+      if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORMALIZE)) {
+       process |= V4L2PROCESSING_PROCESS_RGB_NORMALIZE;
+      }
+      if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE)) {
+       process |= V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE;
+      }
+      break;
+  }
+
+  return process;
+}
+
+static void v4lprocessing_update_processing_data(
+  struct v4lprocessing_data *data,
+  unsigned int pix_fmt, unsigned char *buf, unsigned int width,
+  unsigned int height)
+{
+  switch (data->process) {
+    case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE:
+      bayer_normalize_analyse(buf, width, height, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE:
+      bayer_whitebalance_analyse(buf, width, height, pix_fmt, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE_WHITEBALANCE:
+      bayer_normalize_whitebalance_analyse(buf, width, height, pix_fmt, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_RGB_NORMALIZE:
+      rgb_normalize_analyse(buf, width, height, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE:
+      rgb_whitebalance_analyse(buf, width, height, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_RGB_NORMALIZE_WHITEBALANCE:
+      rgb_normalize_whitebalance_analyse(buf, width, height, data);
+      break;
+  }
+}
+
+int v4lprocessing_pre_processing(struct v4lprocessing_data *data)
+{
+  data->do_process =
+    v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE) ||
+    v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORMALIZE);
+
+  return data->do_process;
+}
+
+void v4lprocessing_processing(struct v4lprocessing_data *data,
+  unsigned char *buf, const struct v4l2_format *fmt)
+{
+  int low_bound, high_bound, process;
+
+  if (!data->do_process)
+    return;
+
+  process = v4lprocessing_get_process(data, fmt->fmt.pix.pixelformat);
+  if (process == V4L2PROCESSING_PROCESS_NONE) {
+    data->process = process;
+    return;
+  }
+
+  low_bound = v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORM_LOW_BOUND);
+  high_bound = v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORM_HIGH_BOUND);
+
+  if (process != data->process || low_bound != data->norm_low_bound ||
+      high_bound != data->norm_high_bound) {
+    data->process = process;
+    data->norm_low_bound  = low_bound;
+    data->norm_high_bound = high_bound;
+    data->processing_data_update = V4L2PROCESSING_UPDATE_RATE;
+  }
+
+  if (data->processing_data_update == V4L2PROCESSING_UPDATE_RATE) {
+    data->processing_data_update = 0;
+    v4lprocessing_update_processing_data(data, fmt->fmt.pix.pixelformat, buf, fmt->fmt.pix.width, fmt->fmt.pix.height);
+  } else
+    data->processing_data_update++;
+
+  switch (data->process) {
+    case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE:
+      bayer_normalize(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE:
+      bayer_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, fmt->fmt.pix.pixelformat, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE_WHITEBALANCE:
+      bayer_normalize_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, fmt->fmt.pix.pixelformat,
+       data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_RGB_NORMALIZE:
+      rgb_normalize(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE:
+      rgb_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data);
+      break;
+
+    case V4L2PROCESSING_PROCESS_RGB_NORMALIZE_WHITEBALANCE:
+      rgb_normalize_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data);
+      break;
+  }
+  data->do_process = 0;
+}
diff --git a/lib/libv4lconvert/processing/libv4lprocessing.h b/lib/libv4lconvert/processing/libv4lprocessing.h
new file mode 100644 (file)
index 0000000..3c50d9f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef __LIBV4LPROCESSING_H
+#define __LIBV4LPROCESSING_H
+
+/* These headers are not needed by us, but by linux/videodev2.h,
+   which is broken on some systems and doesn't include them itself :( */
+#include <sys/time.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+/* end broken header workaround includes */
+#include <linux/videodev2.h>
+
+struct v4lprocessing_data;
+struct v4lcontrol_data;
+
+struct v4lprocessing_data *v4lprocessing_create(struct v4lcontrol_data *data);
+void v4lprocessing_destroy(struct v4lprocessing_data *data);
+
+/* Prepare to process 1 frame, returns 1 if processing is necesary,
+   return 0 if no processing will be done */
+int v4lprocessing_pre_processing(struct v4lprocessing_data *data);
+
+/* Do the actual processing, this is a nop if v4lprocessing_pre_processing()
+   returned 0, or if called more then 1 time after a single
+   v4lprocessing_pre_processing() call. */
+void v4lprocessing_processing(struct v4lprocessing_data *data,
+  unsigned char *buf, const struct v4l2_format *fmt);
+
+#endif
diff --git a/lib/libv4lconvert/processing/rgbprocessing.c b/lib/libv4lconvert/processing/rgbprocessing.c
new file mode 100644 (file)
index 0000000..4e0fc3f
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "libv4lprocessing-priv.h"
+
+void rgb_normalize_analyse(unsigned char *buf, int width, int height,
+  struct v4lprocessing_data *data)
+{
+  int value, max = 0, min = 255;
+  unsigned char *buf_end = buf + width * height * 3;
+
+  while (buf < buf_end) {
+    value = *buf++;
+    if (max < value)
+      max = value;
+    if (min > value)
+      min = value;
+  }
+
+  data->comp1 = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min);
+  data->offset1 = min;
+  data->offset2 = data->norm_low_bound;
+}
+
+void rgb_whitebalance_analyse(unsigned char *buf, int width, int height,
+  struct v4lprocessing_data *data)
+{
+  int value, x = 0, y = 0, z = 0;
+  float x_avg, y_avg, z_avg, avg_avg;
+  unsigned char *buf_end = buf + width * height * 3;
+
+  while (buf < buf_end) {
+    x += *buf++;
+    y += *buf++;
+    z += *buf++;
+  }
+
+  x_avg = x;
+  y_avg = y;
+  z_avg = z;
+  avg_avg = (x_avg + y_avg + z_avg) / 3;
+
+  data->comp1 = (avg_avg / x_avg) * 65536;
+  data->comp2 = (avg_avg / y_avg) * 65536;
+  data->comp3 = (avg_avg / z_avg) * 65536;
+}
+
+void rgb_normalize_whitebalance_analyse(unsigned char *buf,
+  int width, int height, struct v4lprocessing_data *data)
+{
+  int value, max = 0, min = 255;
+  int n_fac, wb_max, x = 0, y = 0, z = 0;
+  float x_avg, y_avg, z_avg, avg_avg;
+  unsigned char *buf_end = buf + width * height * 3;
+
+  while (buf < buf_end) {
+    x += *buf;
+    value = *buf++;
+    if (max < value)
+      max = value;
+    if (min > value)
+      min = value;
+    y += *buf;
+    value = *buf++;
+    if (max < value)
+      max = value;
+    if (min > value)
+      min = value;
+    z += *buf;
+    value = *buf++;
+    if (max < value)
+      max = value;
+    if (min > value)
+      min = value;
+  }
+
+  x_avg = x;
+  y_avg = y;
+  z_avg = z;
+  avg_avg = (x_avg + y_avg + z_avg) / 3;
+
+  n_fac = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min);
+
+  data->comp1 = (avg_avg / x_avg) * n_fac;
+  data->comp2 = (avg_avg / y_avg) * n_fac;
+  data->comp3 = (avg_avg / z_avg) * n_fac;
+
+  data->offset1 = min;
+  data->offset2 = data->norm_low_bound;
+}
+
+#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color)))
+#define TOP(color) (unsigned char)(((color)>0xff)?0xff:(color))
+
+void rgb_normalize(unsigned char *buf, int width, int height,
+  struct v4lprocessing_data *data)
+{
+  int value;
+  unsigned char *buf_end = buf + width * height * 3;
+
+  while (buf < buf_end) {
+    value = ((data->comp1 * (*buf - data->offset1)) >> 16) +
+      data->offset2;
+    *buf++ = CLIP(value);
+  }
+}
+
+void rgb_whitebalance(unsigned char *buf, int width, int height,
+  struct v4lprocessing_data *data)
+{
+  int value;
+  unsigned char *buf_end = buf + width * height * 3;
+
+  while (buf < buf_end) {
+    value = (*buf * data->comp1) >> 16;
+    *buf++ = TOP(value);
+    value = (*buf * data->comp2) >> 16;
+    *buf++ = TOP(value);
+    value = (*buf * data->comp3) >> 16;
+    *buf++ = TOP(value);
+  }
+}
+
+void rgb_normalize_whitebalance(unsigned char *buf, int width, int height,
+  struct v4lprocessing_data *data)
+{
+  int i, value;
+  int limit = width * height * 3;
+  unsigned char *buf_end = buf + width * height * 3;
+
+  while (buf < buf_end) {
+    value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2;
+    *buf++ = CLIP(value);
+    value = ((data->comp2 * (*buf - data->offset1)) >> 16) + data->offset2;
+    *buf++ = CLIP(value);
+    value = ((data->comp3 * (*buf - data->offset1)) >> 16) + data->offset2;
+    *buf++ = CLIP(value);
+  }
+}