-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
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
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.
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 */
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
/* 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;
&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;
/*
-# (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
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
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 $@; \
--- /dev/null
+/*
+# (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
--- /dev/null
+/*
+# (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;
+}
--- /dev/null
+/*
+# (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
}
}
-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);
}
}
#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
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 {
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);
#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 \
/* 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
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"))
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);
}
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? */
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)
{
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:
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,
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);
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 */
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;
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;
height, 0);
break;
case V4L2_PIX_FMT_YVU420:
- v4lconvert_swap_uv(src, dest, src_fmt);
+ v4lconvert_swap_uv(src, dest, fmt);
break;
}
break;
height, 1);
break;
case V4L2_PIX_FMT_YUV420:
- v4lconvert_swap_uv(src, dest, src_fmt);
+ v4lconvert_swap_uv(src, dest, fmt);
break;
}
break;
errno = EINVAL;
return -1;
}
+
+ fmt->fmt.pix.pixelformat = dest_pix_fmt;
+ v4lconvert_fixup_fmt(fmt);
+
return 0;
}
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;
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);
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);
+}
--- /dev/null
+/*
+# (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);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+# (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
--- /dev/null
+/*
+# (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;
+}
--- /dev/null
+/*
+# (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
--- /dev/null
+/*
+# (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);
+ }
+}