diff options
19 files changed, 1300 insertions, 110 deletions
diff --git a/v4l2-apps/libv4l/ChangeLog b/v4l2-apps/libv4l/ChangeLog index 9c9a5a3b2..52b429687 100644 --- a/v4l2-apps/libv4l/ChangeLog +++ b/v4l2-apps/libv4l/ChangeLog @@ -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 diff --git a/v4l2-apps/libv4l/README b/v4l2-apps/libv4l/README index 3a2059224..481d5b92e 100644 --- a/v4l2-apps/libv4l/README +++ b/v4l2-apps/libv4l/README @@ -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 diff --git a/v4l2-apps/libv4l/TODO b/v4l2-apps/libv4l/TODO index debb9c78c..2bd2fc992 100644 --- a/v4l2-apps/libv4l/TODO +++ b/v4l2-apps/libv4l/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. diff --git a/v4l2-apps/libv4l/include/libv4lconvert.h b/v4l2-apps/libv4l/include/libv4lconvert.h index 626c43473..fcc31dace 100644 --- a/v4l2-apps/libv4l/include/libv4lconvert.h +++ b/v4l2-apps/libv4l/include/libv4lconvert.h @@ -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 */ diff --git a/v4l2-apps/libv4l/libv4l2/Makefile b/v4l2-apps/libv4l/libv4l2/Makefile index c3af123c8..4356c0975 100644 --- a/v4l2-apps/libv4l/libv4l2/Makefile +++ b/v4l2-apps/libv4l/libv4l2/Makefile @@ -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 diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index b6ddef6e8..91e0193ee 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/libv4l2/libv4l2.c @@ -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; diff --git a/v4l2-apps/libv4l/libv4l2/log.c b/v4l2-apps/libv4l/libv4l2/log.c index c29086ff4..251b46ecf 100644 --- a/v4l2-apps/libv4l/libv4l2/log.c +++ b/v4l2-apps/libv4l/libv4l2/log.c @@ -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 diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile index 95b9d31d9..64f6690d2 100644 --- a/v4l2-apps/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/libv4l/libv4lconvert/Makefile @@ -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/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h new file mode 100644 index 000000000..59305a237 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h @@ -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/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c new file mode 100644 index 000000000..cb4e34327 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c @@ -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/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h new file mode 100644 index 000000000..3611304d6 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h @@ -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 diff --git a/v4l2-apps/libv4l/libv4lconvert/flip.c b/v4l2-apps/libv4l/libv4lconvert/flip.c index f47afde72..9835f045c 100644 --- a/v4l2-apps/libv4l/libv4lconvert/flip.c +++ b/v4l2-apps/libv4l/libv4lconvert/flip.c @@ -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); } } diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h index 4e6130d3c..acf921a31 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h @@ -21,8 +21,12 @@ #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 @@ -79,6 +83,15 @@ #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 @@ -87,10 +100,11 @@ "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); diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c index f3191f1d9..6fbc1affa 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c @@ -29,11 +29,6 @@ #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/v4l2-apps/libv4l/libv4lconvert/processing/bayerprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/bayerprocessing.c new file mode 100644 index 000000000..f4cc9922b --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/bayerprocessing.c @@ -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/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h new file mode 100644 index 000000000..f2aae373d --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h @@ -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/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c new file mode 100644 index 000000000..d61c29275 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c @@ -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/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h new file mode 100644 index 000000000..3c50d9fb9 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h @@ -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/v4l2-apps/libv4l/libv4lconvert/processing/rgbprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/rgbprocessing.c new file mode 100644 index 000000000..4e0fc3f4a --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/rgbprocessing.c @@ -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); + } +} |