summaryrefslogtreecommitdiff
path: root/v4l2-apps/libv4l/libv4lconvert
diff options
context:
space:
mode:
Diffstat (limited to 'v4l2-apps/libv4l/libv4lconvert')
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/Makefile31
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/bayer.c17
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h54
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c504
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h70
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/crop.c288
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/flip.c175
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/helper-funcs.h79
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/helper.c224
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/hm12.c159
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h129
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c1067
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h112
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/mr97310a.c172
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c666
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c1477
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/processing/autogain.c140
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/processing/gamma.c57
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h60
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c181
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h43
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c142
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/rgbyuv.c202
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/sn9c20x.c137
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/spca501.c36
-rw-r--r--v4l2-apps/libv4l/libv4lconvert/sq905c.c217
26 files changed, 6096 insertions, 343 deletions
diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile
index 641d19d6e..036649e86 100644
--- a/v4l2-apps/libv4l/libv4lconvert/Makefile
+++ b/v4l2-apps/libv4l/libv4lconvert/Makefile
@@ -3,6 +3,8 @@ override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden
CFLAGS := -g -O1
CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes
+LIBS_libv4lconvert = -lrt -lm
+
ifeq ($(LINKTYPE),static)
CONVERT_LIB = libv4lconvert.a
else
@@ -10,9 +12,13 @@ CONVERT_LIB = libv4lconvert.so
override CPPFLAGS += -fPIC
endif
-CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o flip.o \
- jidctflt.o spca561-decompress.o rgbyuv.o spca501.o bayer.o
-TARGETS = $(CONVERT_LIB) libv4lconvert.pc
+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 helper.o \
+ control/libv4lcontrol.o processing/libv4lprocessing.o \
+ processing/whitebalance.o processing/autogain.o \
+ processing/gamma.o
+TARGETS = $(CONVERT_LIB) libv4lconvert.pc ov511-decomp ov518-decomp
INCLUDES = ../include/libv4lconvert.h
ifeq ($(LIB_RELEASE),)
@@ -27,8 +33,12 @@ ifeq ($(LIBDIR),)
LIBDIR = $(PREFIX)/lib
endif
+override CPPFLAGS += -DLIBDIR=\"$(LIBDIR)\"
+
all: $(TARGETS)
+-include $(CONVERT_OBJS:.o=.d)
+
$(CONVERT_LIB): $(CONVERT_OBJS)
libv4lconvert.pc:
@@ -39,32 +49,37 @@ libv4lconvert.pc:
@echo 'Description: v4l format conversion library' >> libv4lconvert.pc
@echo 'Version: '$(V4L2_LIB_VERSION) >> libv4lconvert.pc
@echo 'Libs: -L$${libdir} -lv4lconvert' >> libv4lconvert.pc
+ @echo 'Libs.private: -lrt -lm' >> libv4lconvert.pc
@echo 'Cflags: -I$${prefix}/include' >> libv4lconvert.pc
install: all
mkdir -p $(DESTDIR)$(PREFIX)/include
install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include
- mkdir -p $(DESTDIR)$(LIBDIR)
+ mkdir -p $(DESTDIR)$(LIBDIR)/libv4l
ifeq ($(LINKTYPE),static)
- mkdir -p $(DESTDIR)$(LIBDIR)
install -m 644 $(CONVERT_LIB) $(DESTDIR)$(LIBDIR)
else
install -m 755 $(CONVERT_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR)
cd $(DESTDIR)$(LIBDIR) && \
ln -f -s $(CONVERT_LIB).$(LIB_RELEASE) $(CONVERT_LIB)
endif
+ install -m 755 *-decomp $(DESTDIR)$(LIBDIR)/libv4l
mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig
install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig
clean::
- rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~
+ rm -f *.a *.so* *.o *.d */*.o */*.d libv4lconvert.pc log *~ */*~
+ rm -f *.orig *.rej */*.orig */*.rej DEADJOE */DEADJOE *-decomp
%.o: %.c
- $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $<
+ $(CC) -Wp,-MMD,"$*.d",-MQ,"$@",-MP -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
%.so:
- $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^
+ $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*)
ln -f -s $@.$(LIB_RELEASE) $@
%.a:
$(AR) cqs $@ $^
+
+ov511-decomp: ov511-decomp.o
+ov518-decomp: ov518-decomp.o
diff --git a/v4l2-apps/libv4l/libv4lconvert/bayer.c b/v4l2-apps/libv4l/libv4lconvert/bayer.c
index ca7bb486f..033ee2724 100644
--- a/v4l2-apps/libv4l/libv4lconvert/bayer.c
+++ b/v4l2-apps/libv4l/libv4lconvert/bayer.c
@@ -433,16 +433,23 @@ static void v4lconvert_border_bayer_line_to_y(
}
}
-void v4lconvert_bayer_to_yuv420(const unsigned char *bayer,
- unsigned char *yuv, int width, int height, unsigned int pixfmt)
+void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv,
+ int width, int height, unsigned int src_pixfmt, int yvu)
{
int blue_line = 0, start_with_green = 0, x, y;
unsigned char *ydst = yuv;
- unsigned char *udst = yuv + width * height;
- unsigned char *vdst = udst + width * height / 4;
+ unsigned char *udst, *vdst;
+
+ if (yvu) {
+ vdst = yuv + width * height;
+ udst = vdst + width * height / 4;
+ } else {
+ udst = yuv + width * height;
+ vdst = udst + width * height / 4;
+ }
/* First calculate the u and v planes 2x2 pixels at a time */
- switch (pixfmt) {
+ switch (src_pixfmt) {
case V4L2_PIX_FMT_SBGGR8:
for (y = 0; y < height; y += 2) {
for (x = 0; x < width; x += 2) {
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..6211ee22a
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h
@@ -0,0 +1,54 @@
+/*
+# (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
+
+#define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01
+
+struct v4lcontrol_flags_info;
+
+struct v4lcontrol_data {
+ int fd; /* Device fd */
+ int flags; /* Flags for this device */
+ int priv_flags; /* Internal use only flags */
+ int controls; /* Which controls to use for this device */
+ unsigned int *shm_values; /* shared memory control value store */
+ unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */
+ const struct v4lcontrol_flags_info *flags_info;
+};
+
+struct v4lcontrol_flags_info {
+ unsigned short vendor_id;
+ unsigned short product_id;
+ unsigned short product_mask;
+ const char *dmi_board_vendor;
+ const char *dmi_board_name;
+/* We could also use the USB manufacturer and product strings some devices have
+ const char *manufacturer;
+ const char *product; */
+ int flags;
+ int default_gamma;
+};
+
+#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..4e600351b
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c
@@ -0,0 +1,504 @@
+/*
+# (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 "libv4lcontrol.h"
+#include "libv4lcontrol-priv.h"
+#include "../libv4lsyscall-priv.h"
+#include <linux/videodev2.h>
+
+#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0]))
+
+/* Workaround these potentially missing from videodev2.h */
+#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
+
+
+/* List of cams which need special flags */
+static const struct v4lcontrol_flags_info v4lcontrol_flags[] = {
+/* First: Upside down devices */
+ /* Philips SPC200NC */
+ { 0x0471, 0x0325, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+ /* Philips SPC300NC */
+ { 0x0471, 0x0326, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+ /* Philips SPC210NC */
+ { 0x0471, 0x032d, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+ /* Genius E-M 112 (also want whitebalance by default) */
+ { 0x093a, 0x2476, 0, NULL, NULL,
+ V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED|V4LCONTROL_WANTS_WB, 1500 },
+ /* Asus N50Vn laptop */
+ { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vn ",
+ V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+/* Second: devices which should use some software processing by default */
+ /* Pac207 based devices */
+ { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
+ { 0x093a, 0x2460, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
+ { 0x145f, 0x013a, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
+ { 0x2001, 0xf115, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
+ /* Pac7302 based devices */
+ { 0x093a, 0x2620, 0x0f, NULL, NULL,
+ V4LCONTROL_ROTATED_90_JPEG|V4LCONTROL_WANTS_WB },
+ /* Pac7311 based devices */
+ { 0x093a, 0x2600, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB },
+ /* sq905 devices */
+ { 0x2770, 0x9120, 0, NULL, NULL, V4LCONTROL_WANTS_WB },
+ /* spca561 revison 12a devices */
+ { 0x041e, 0x403b, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
+ { 0x046d, 0x0928, 7, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
+ /* logitech quickcam express stv06xx + pb0100 */
+ { 0x046d, 0x0840, 0, NULL, NULL, V4LCONTROL_WANTS_WB },
+ /* logitech quickcam messenger variants, st6422 */
+ { 0x046d, 0x08f0, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
+ { 0x046d, 0x08f5, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
+ { 0x046d, 0x08f6, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
+ { 0x046d, 0x08da, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
+};
+
+static const struct v4l2_queryctrl fake_controls[];
+
+static void v4lcontrol_init_flags(struct v4lcontrol_data *data)
+{
+ struct stat st;
+ FILE *f;
+ char sysfs_name[512];
+ unsigned short vendor_id = 0;
+ unsigned short product_id = 0;
+ char dmi_board_vendor[512] = "";
+ char dmi_board_name[512]= "";
+ int i, minor;
+ char c, *s, buf[32];
+ struct v4l2_input input;
+
+ if ((SYS_IOCTL(data->fd, VIDIOC_G_INPUT, &input.index) == 0) &&
+ (SYS_IOCTL(data->fd, VIDIOC_ENUMINPUT, &input) == 0)) {
+ if (input.status & V4L2_IN_ST_HFLIP)
+ data->flags |= V4LCONTROL_HFLIPPED;
+ if (input.status & V4L2_IN_ST_VFLIP)
+ data->flags |= V4LCONTROL_VFLIPPED;
+ }
+
+ if (fstat(data->fd, &st) || !S_ISCHR(st.st_mode)) {
+ return; /* Should never happen */
+ }
+
+ /* <Sigh> find ourselve in sysfs */
+ for (i = 0; i < 256; i++) {
+ snprintf(sysfs_name, sizeof(sysfs_name),
+ "/sys/class/video4linux/video%d/dev", i);
+ f = fopen(sysfs_name, "r");
+ if (!f)
+ continue;
+
+ s = fgets(buf, sizeof(buf), f);
+ fclose(f);
+
+ if (s && sscanf(buf, "%*d:%d%c", &minor, &c) == 2 && c == '\n' &&
+ minor == minor(st.st_rdev))
+ break;
+ }
+ if (i == 256)
+ return; /* Not found, sysfs not mounted? */
+
+ /* Get vendor and product ID */
+ snprintf(sysfs_name, sizeof(sysfs_name),
+ "/sys/class/video4linux/video%d/device/modalias", i);
+ f = fopen(sysfs_name, "r");
+ if (f) {
+ s = fgets(buf, sizeof(buf), f);
+ fclose(f);
+
+ if (!s ||
+ sscanf(s, "usb:v%4hxp%4hx%c", &vendor_id, &product_id, &c) != 3 ||
+ c != 'd')
+ return; /* Not an USB device */
+ } else {
+ /* Try again assuming the device link points to the usb
+ device instead of the usb interface (bug in older versions
+ of gspca) */
+
+ /* Get product ID */
+ snprintf(sysfs_name, sizeof(sysfs_name),
+ "/sys/class/video4linux/video%d/device/idVendor", i);
+ f = fopen(sysfs_name, "r");
+ if (!f)
+ return; /* Not an USB device (or no sysfs) */
+
+ s = fgets(buf, sizeof(buf), f);
+ fclose(f);
+
+ if (!s || sscanf(s, "%04hx%c", &vendor_id, &c) != 2 || c != '\n')
+ return; /* Should never happen */
+
+ /* Get product ID */
+ snprintf(sysfs_name, sizeof(sysfs_name),
+ "/sys/class/video4linux/video%d/device/idProduct", i);
+ f = fopen(sysfs_name, "r");
+ if (!f)
+ return; /* Should never happen */
+
+ s = fgets(buf, sizeof(buf), f);
+ fclose(f);
+
+ if (!s || sscanf(s, "%04hx%c", &product_id, &c) != 2 || c != '\n')
+ return; /* Should never happen */
+ }
+
+ /* Get DMI board vendor and name */
+ f = fopen("/sys/devices/virtual/dmi/id/board_vendor", "r");
+ if (f) {
+ s = fgets(dmi_board_vendor, sizeof(dmi_board_vendor), f);
+ if (s)
+ s[strlen(s) - 1] = 0;
+ fclose(f);
+ }
+
+ f = fopen("/sys/devices/virtual/dmi/id/board_name", "r");
+ if (f) {
+ s = fgets(dmi_board_name, sizeof(dmi_board_name), f);
+ if (s)
+ s[strlen(s) - 1] = 0;
+ fclose(f);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(v4lcontrol_flags); i++)
+ if (v4lcontrol_flags[i].vendor_id == vendor_id &&
+ v4lcontrol_flags[i].product_id ==
+ (product_id & ~v4lcontrol_flags[i].product_mask) &&
+ (v4lcontrol_flags[i].dmi_board_vendor == NULL ||
+ !strcmp(v4lcontrol_flags[i].dmi_board_vendor, dmi_board_vendor)) &&
+ (v4lcontrol_flags[i].dmi_board_name == NULL ||
+ !strcmp(v4lcontrol_flags[i].dmi_board_name, dmi_board_name))) {
+ data->flags |= v4lcontrol_flags[i].flags;
+ data->flags_info = &v4lcontrol_flags[i];
+ break;
+ }
+}
+
+struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
+{
+ int shm_fd;
+ int i, rc, init = 0;
+ char *s, shm_name[256];
+ struct v4l2_capability cap;
+ struct v4l2_queryctrl ctrl;
+
+ struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data));
+
+ if (!data)
+ return NULL;
+
+ data->fd = fd;
+
+ v4lcontrol_init_flags(data);
+
+ /* Allow overriding through environment */
+ if ((s = getenv("LIBV4LCONTROL_FLAGS")))
+ data->flags = strtol(s, NULL, 0);
+
+ ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
+ if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl) == 0)
+ data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL;
+
+ /* If the device always needs conversion, we can add fake controls at no cost
+ (no cost when not activated by the user that is) */
+ if (always_needs_conversion || v4lcontrol_needs_conversion(data)) {
+ for (i = 0; i < V4LCONTROL_AUTO_ENABLE_COUNT; i++) {
+ ctrl.id = fake_controls[i].id;
+ rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+ if (rc == -1 || (rc == 0 && (ctrl.flags & V4L2_CTRL_FLAG_DISABLED)))
+ data->controls |= 1 << i;
+ }
+ }
+
+ if (data->flags & V4LCONTROL_WANTS_AUTOGAIN)
+ data->controls |= 1 << V4LCONTROL_AUTOGAIN |
+ 1 << V4LCONTROL_AUTOGAIN_TARGET;
+
+ /* Allow overriding through environment */
+ if ((s = getenv("LIBV4LCONTROL_CONTROLS")))
+ data->controls = strtol(s, NULL, 0);
+
+ if (data->controls == 0)
+ return data; /* No need to create a shared memory segment */
+
+ SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
+ snprintf(shm_name, 256, "/%s:%s", cap.bus_info, cap.card);
+
+ /* / is not allowed inside shm names */
+ for (i = 1; shm_name[i]; i++)
+ if (shm_name[i] == '/')
+ shm_name[i] = '-';
+
+ /* 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;
+
+ if (init) {
+ /* Initialize the new shm object we created */
+ memset(data->shm_values, 0, sizeof(V4LCONTROL_SHM_SIZE));
+
+ for (i = 0; i < V4LCONTROL_COUNT; i++)
+ data->shm_values[i] = fake_controls[i].default_value;
+
+ if (data->flags & V4LCONTROL_WANTS_WB)
+ data->shm_values[V4LCONTROL_WHITEBALANCE] = 1;
+
+ if (data->flags_info && data->flags_info->default_gamma)
+ data->shm_values[V4LCONTROL_GAMMA] = data->flags_info->default_gamma;
+ }
+
+ return data;
+
+error:
+ free(data);
+ return NULL;
+}
+
+void v4lcontrol_destroy(struct v4lcontrol_data *data)
+{
+ if (data->controls)
+ munmap(data->shm_values, V4LCONTROL_SHM_SIZE);
+ free(data);
+}
+
+/* FIXME get better CID's for normalize */
+static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = {
+{
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Whitebalance (software)",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0
+},
+{
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal flip (sw)",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0
+},
+{
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical flip (sw)",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0
+},
+{
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma (software)",
+ .minimum = 500, /* == 0.5 */
+ .maximum = 3000, /* == 3.0 */
+ .step = 1,
+ .default_value = 1000, /* == 1.0 */
+ .flags = 0
+},
+{}, /* Dummy place holder for V4LCONTROL_AUTO_ENABLE_COUNT */
+{
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain (software)",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .flags = 0
+},
+{
+ .id = V4L2_CTRL_CLASS_USER + 0x2000, /* FIXME */
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Auto Gain target",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 100,
+ .flags = 0
+},
+};
+
+static void v4lcontrol_copy_queryctrl(struct v4lcontrol_data *data,
+ struct v4l2_queryctrl *ctrl, int i)
+{
+ memcpy(ctrl, &fake_controls[i], sizeof(struct v4l2_queryctrl));
+
+ /* Hmm, not pretty */
+ if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE &&
+ (data->flags & V4LCONTROL_WANTS_WB))
+ ctrl->default_value = 1;
+
+ if (ctrl->id == V4L2_CID_GAMMA && data->flags_info &&
+ data->flags_info->default_gamma)
+ ctrl->default_value = data->flags_info->default_gamma;
+}
+
+int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
+{
+ int i;
+ struct v4l2_queryctrl *ctrl = arg;
+ int retval;
+ __u32 orig_id=ctrl->id;
+
+ /* if we have an exact match return it */
+ for (i = 0; i < V4LCONTROL_COUNT; i++)
+ if ((data->controls & (1 << i)) &&
+ ctrl->id == fake_controls[i].id) {
+ v4lcontrol_copy_queryctrl(data, ctrl, i);
+ return 0;
+ }
+
+ /* find out what the kernel driver would respond. */
+ retval = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, arg);
+
+ if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) &&
+ (orig_id & V4L2_CTRL_FLAG_NEXT_CTRL)) {
+ /* If the hardware has no more controls check if we still have any
+ fake controls with a higher id then the hardware's highest */
+ if (retval)
+ ctrl->id = V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ /* If any of our controls have an id > orig_id but less than
+ ctrl->id then return that control instead. Note we do not
+ break when we have a match, but keep iterating, so that
+ we end up with the fake ctrl with the lowest CID > orig_id. */
+ for (i = 0; i < V4LCONTROL_COUNT; i++)
+ if ((data->controls & (1 << i)) &&
+ (fake_controls[i].id > (orig_id & ~V4L2_CTRL_FLAG_NEXT_CTRL)) &&
+ (fake_controls[i].id <= ctrl->id)) {
+ v4lcontrol_copy_queryctrl(data, ctrl, i);
+ retval = 0;
+ }
+ }
+
+ return retval;
+}
+
+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 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 SYS_IOCTL(data->fd, VIDIOC_S_CTRL, arg);
+}
+
+int v4lcontrol_get_flags(struct v4lcontrol_data *data)
+{
+ return data->flags;
+}
+
+int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl)
+{
+ if (data->controls & (1 << ctrl)) {
+ /* Special case for devices with flipped input */
+ if ((ctrl == V4LCONTROL_HFLIP && (data->flags & V4LCONTROL_HFLIPPED)) ||
+ (ctrl == V4LCONTROL_VFLIP && (data->flags & V4LCONTROL_VFLIPPED)))
+ return !data->shm_values[ctrl];
+
+ return data->shm_values[ctrl];
+ }
+
+ return 0;
+}
+
+int v4lcontrol_controls_changed(struct v4lcontrol_data *data)
+{
+ int res;
+
+ if (!data->controls)
+ return 0;
+
+ res = memcmp(data->shm_values, data->old_values,
+ V4LCONTROL_COUNT * sizeof(unsigned int));
+
+ memcpy(data->old_values, data->shm_values,
+ V4LCONTROL_COUNT * sizeof(unsigned int));
+
+ return res;
+}
+
+/* See the comment about this in libv4lconvert.h */
+int v4lcontrol_needs_conversion(struct v4lcontrol_data *data) {
+ return data->flags || data->controls;
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h
new file mode 100644
index 000000000..e29fde3d4
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h
@@ -0,0 +1,70 @@
+/*
+# (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_H
+#define __LIBV4LCONTROL_H
+
+/* Flags */
+#define V4LCONTROL_HFLIPPED 0x01
+#define V4LCONTROL_VFLIPPED 0x02
+#define V4LCONTROL_ROTATED_90_JPEG 0x04
+#define V4LCONTROL_WANTS_WB 0x08
+#define V4LCONTROL_WANTS_AUTOGAIN 0x10
+
+/* Masks */
+#define V4LCONTROL_WANTS_WB_AUTOGAIN (V4LCONTROL_WANTS_WB | V4LCONTROL_WANTS_AUTOGAIN)
+
+/* Controls */
+enum {
+ V4LCONTROL_WHITEBALANCE,
+ V4LCONTROL_HFLIP,
+ V4LCONTROL_VFLIP,
+ V4LCONTROL_GAMMA,
+ /* All fake controls above here are auto enabled when not present in hw */
+ V4LCONTROL_AUTO_ENABLE_COUNT,
+ V4LCONTROL_AUTOGAIN,
+ V4LCONTROL_AUTOGAIN_TARGET,
+ V4LCONTROL_COUNT
+ };
+
+struct v4lcontrol_data;
+
+struct v4lcontrol_data* v4lcontrol_create(int fd, int always_needs_conversion);
+void v4lcontrol_destroy(struct v4lcontrol_data *data);
+
+/* Functions used by v4lprocessing to get the control state */
+int v4lcontrol_get_flags(struct v4lcontrol_data *data);
+int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl);
+/* Check if the controls have changed since the last time this function
+ was called */
+int v4lcontrol_controls_changed(struct v4lcontrol_data *data);
+/* Check if we must go through the conversion path (and thus alloc conversion
+ buffers, etc. in libv4l2). Note this always return 1 if we *may* need
+ rotate90 / flipping / processing, as if we actually need this may change
+ on the fly while the stream is active. */
+int v4lcontrol_needs_conversion(struct v4lcontrol_data *data);
+
+/* 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/crop.c b/v4l2-apps/libv4l/libv4lconvert/crop.c
new file mode 100644
index 000000000..46c87f0ce
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/crop.c
@@ -0,0 +1,288 @@
+/*
+
+# RGB and YUV crop routines
+
+# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+# 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 <string.h>
+#include "libv4lconvert-priv.h"
+
+
+static void v4lconvert_reduceandcrop_rgbbgr24(
+ unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x, y;
+ int startx = src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width;
+ int starty = src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height;
+
+ src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx;
+
+ for (y = 0; y < dest_fmt->fmt.pix.height; y++) {
+ unsigned char *mysrc = src;
+ for (x = 0; x < dest_fmt->fmt.pix.width; x++) {
+ *(dest++) = *(mysrc++);
+ *(dest++) = *(mysrc++);
+ *(dest++) = *(mysrc++);
+ mysrc += 3; /* skip one pixel */
+ }
+ src += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */
+ }
+}
+
+static void v4lconvert_crop_rgbbgr24(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x;
+ int startx = (src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2;
+ int starty = (src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2;
+
+ src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx;
+
+ for (x = 0; x < dest_fmt->fmt.pix.height; x++) {
+ memcpy(dest, src, dest_fmt->fmt.pix.width * 3);
+ src += src_fmt->fmt.pix.bytesperline;
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+}
+
+static void v4lconvert_reduceandcrop_yuv420(
+ unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x,y;
+ int dest_height_half = dest_fmt->fmt.pix.height / 2;
+ int dest_width_half = dest_fmt->fmt.pix.width / 2;
+ int startx = (src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width) & ~1;
+ int starty = (src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height) & ~1;
+ unsigned char *mysrc, *mysrc2;
+
+ /* Y */
+ mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx;
+ for (y = 0; y < dest_fmt->fmt.pix.height; y++){
+ mysrc2 = mysrc;
+ for (x = 0; x < dest_fmt->fmt.pix.width; x++){
+ *(dest++) = *mysrc2;
+ mysrc2 += 2; /* skip one pixel */
+ }
+ mysrc += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */
+ }
+
+ /* U */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline +
+ (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (y = 0; y < dest_height_half; y++){
+ mysrc2 = mysrc;
+ for (x = 0; x < dest_width_half; x++){
+ *(dest++) = *mysrc2;
+ mysrc2 += 2; /* skip one pixel */
+ }
+ mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */
+ }
+
+ /* V */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4
+ + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (y = 0; y < dest_height_half; y++){
+ mysrc2 = mysrc;
+ for (x = 0; x < dest_width_half; x++){
+ *(dest++) = *mysrc2;
+ mysrc2 += 2; /* skip one pixel */
+ }
+ mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */
+ }
+}
+
+static void v4lconvert_crop_yuv420(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int x;
+ int startx = ((src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2) & ~1;
+ int starty = ((src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2) & ~1;
+ unsigned char *mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx;
+
+ /* Y */
+ for (x = 0; x < dest_fmt->fmt.pix.height; x++) {
+ memcpy(dest, mysrc, dest_fmt->fmt.pix.width);
+ mysrc += src_fmt->fmt.pix.bytesperline;
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+
+ /* U */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline +
+ (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) {
+ memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2);
+ mysrc += src_fmt->fmt.pix.bytesperline / 2;
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+
+ /* V */
+ mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4
+ + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2;
+ for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) {
+ memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2);
+ mysrc += src_fmt->fmt.pix.bytesperline / 2;
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+}
+
+/* Ok, so this is not really cropping, but more the reverse, whatever */
+static void v4lconvert_add_border_rgbbgr24(
+ unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int y;
+ int borderx = (dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2;
+ int bordery = (dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2;
+
+ for (y = 0; y < bordery; y++) {
+ memset(dest, 0, dest_fmt->fmt.pix.width * 3);
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+
+ for (y = 0; y < src_fmt->fmt.pix.height; y++) {
+ memset(dest, 0, borderx * 3);
+ dest += borderx * 3;
+
+ memcpy(dest, src, src_fmt->fmt.pix.width * 3);
+ src += src_fmt->fmt.pix.bytesperline;
+ dest += src_fmt->fmt.pix.width * 3;
+
+ memset(dest, 0, borderx * 3);
+ dest += dest_fmt->fmt.pix.bytesperline -
+ (borderx + src_fmt->fmt.pix.width) * 3;
+ }
+
+ for (y = 0; y < bordery; y++) {
+ memset(dest, 0, dest_fmt->fmt.pix.width * 3);
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+}
+
+static void v4lconvert_add_border_yuv420(
+ unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ int y;
+ int borderx = ((dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2) & ~1;
+ int bordery = ((dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2) & ~1;
+
+ /* Y */
+ for (y = 0; y < bordery; y++) {
+ memset(dest, 16, dest_fmt->fmt.pix.width);
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+
+ for (y = 0; y < src_fmt->fmt.pix.height; y++) {
+ memset(dest, 16, borderx);
+ dest += borderx;
+
+ memcpy(dest, src, src_fmt->fmt.pix.width);
+ src += src_fmt->fmt.pix.bytesperline;
+ dest += src_fmt->fmt.pix.width;
+
+ memset(dest, 16, borderx);
+ dest += dest_fmt->fmt.pix.bytesperline -
+ (borderx + src_fmt->fmt.pix.width);
+ }
+
+ for (y = 0; y < bordery; y++) {
+ memset(dest, 16, dest_fmt->fmt.pix.width);
+ dest += dest_fmt->fmt.pix.bytesperline;
+ }
+
+ /* U */
+ for (y = 0; y < bordery / 2; y++) {
+ memset(dest, 128, dest_fmt->fmt.pix.width / 2);
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ memset(dest, 128, borderx / 2);
+ dest += borderx / 2;
+
+ memcpy(dest, src, src_fmt->fmt.pix.width / 2);
+ src += src_fmt->fmt.pix.bytesperline / 2;
+ dest += src_fmt->fmt.pix.width / 2;
+
+ memset(dest, 128, borderx / 2);
+ dest += (dest_fmt->fmt.pix.bytesperline -
+ (borderx + src_fmt->fmt.pix.width)) / 2;
+ }
+
+ for (y = 0; y < bordery / 2; y++) {
+ memset(dest, 128, dest_fmt->fmt.pix.width / 2);
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+
+ /* V */
+ for (y = 0; y < bordery / 2; y++) {
+ memset(dest, 128, dest_fmt->fmt.pix.width / 2);
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ memset(dest, 128, borderx / 2);
+ dest += borderx / 2;
+
+ memcpy(dest, src, src_fmt->fmt.pix.width / 2);
+ src += src_fmt->fmt.pix.bytesperline / 2;
+ dest += src_fmt->fmt.pix.width / 2;
+
+ memset(dest, 128, borderx / 2);
+ dest += (dest_fmt->fmt.pix.bytesperline -
+ (borderx + src_fmt->fmt.pix.width)) / 2;
+ }
+
+ for (y = 0; y < bordery / 2; y++) {
+ memset(dest, 128, dest_fmt->fmt.pix.width / 2);
+ dest += dest_fmt->fmt.pix.bytesperline / 2;
+ }
+}
+
+void v4lconvert_crop(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt)
+{
+ switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width &&
+ src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height)
+ v4lconvert_add_border_rgbbgr24(src, dest, src_fmt, dest_fmt);
+ else if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width &&
+ src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height)
+ v4lconvert_reduceandcrop_rgbbgr24(src, dest, src_fmt, dest_fmt);
+ else
+ v4lconvert_crop_rgbbgr24(src, dest, src_fmt, dest_fmt);
+ break;
+
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width &&
+ src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height)
+ v4lconvert_add_border_yuv420(src, dest, src_fmt, dest_fmt);
+ else if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width &&
+ src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height)
+ v4lconvert_reduceandcrop_yuv420(src, dest, src_fmt, dest_fmt);
+ else
+ v4lconvert_crop_yuv420(src, dest, src_fmt, dest_fmt);
+ break;
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/flip.c b/v4l2-apps/libv4l/libv4lconvert/flip.c
index cd3468a89..8c4d8233c 100644
--- a/v4l2-apps/libv4l/libv4lconvert/flip.c
+++ b/v4l2-apps/libv4l/libv4lconvert/flip.c
@@ -20,10 +20,102 @@
*/
+#include <string.h>
#include "libv4lconvert-priv.h"
-void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int width, int height)
+static void v4lconvert_vflip_rgbbgr24(unsigned char *src, unsigned char *dest,
+ struct v4l2_format *fmt)
+{
+ int y;
+
+ src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline;
+ for (y = 0; y < fmt->fmt.pix.height; y++) {
+ src -= fmt->fmt.pix.bytesperline;
+ memcpy(dest, src, fmt->fmt.pix.width * 3);
+ dest += fmt->fmt.pix.width * 3;
+ }
+}
+
+static void v4lconvert_vflip_yuv420(unsigned char *src, unsigned char *dest,
+ struct v4l2_format *fmt)
+{
+ int y;
+
+ /* First flip the Y plane */
+ src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline;
+ for (y = 0; y < fmt->fmt.pix.height; y++) {
+ src -= fmt->fmt.pix.bytesperline;
+ memcpy(dest, src, fmt->fmt.pix.width);
+ dest += fmt->fmt.pix.width;
+ }
+
+ /* Now flip the U plane */
+ src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline * 5 / 4;
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ src -= fmt->fmt.pix.bytesperline / 2;
+ memcpy(dest, src, fmt->fmt.pix.width / 2);
+ dest += fmt->fmt.pix.width / 2;
+ }
+
+ /* Last flip the V plane */
+ src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 2;
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ src -= fmt->fmt.pix.bytesperline / 2;
+ memcpy(dest, src, fmt->fmt.pix.width / 2);
+ dest += fmt->fmt.pix.width / 2;
+ }
+}
+
+static void v4lconvert_hflip_rgbbgr24(unsigned char *src, unsigned char *dest,
+ struct v4l2_format *fmt)
+{
+ int x, y;
+
+ for (y = 0; y < fmt->fmt.pix.height; y++) {
+ src += fmt->fmt.pix.width * 3;
+ for (x = 0; x < fmt->fmt.pix.width; x++) {
+ src -= 3;
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest += 3;
+ }
+ src += fmt->fmt.pix.bytesperline;
+ }
+}
+
+static void v4lconvert_hflip_yuv420(unsigned char *src, unsigned char *dest,
+ struct v4l2_format *fmt)
+{
+ int x, y;
+
+ /* First flip the Y plane */
+ for (y = 0; y < fmt->fmt.pix.height; y++) {
+ src += fmt->fmt.pix.width;
+ for (x = 0; x < fmt->fmt.pix.width; x++)
+ *dest++ = *--src;
+ src += fmt->fmt.pix.bytesperline;
+ }
+
+ /* Now flip the U plane */
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ src += fmt->fmt.pix.width / 2;
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++)
+ *dest++ = *--src;
+ src += fmt->fmt.pix.bytesperline / 2;
+ }
+
+ /* Last flip the V plane */
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ src += fmt->fmt.pix.width / 2;
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++)
+ *dest++ = *--src;
+ src += fmt->fmt.pix.bytesperline / 2;
+ }
+}
+
+static void v4lconvert_rotate180_rgbbgr24(const unsigned char *src,
+ unsigned char *dst, int width, int height)
{
int i;
@@ -38,8 +130,8 @@ void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst,
}
}
-void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+static void v4lconvert_rotate180_yuv420(const unsigned char *src,
+ unsigned char *dst, int width, int height)
{
int i;
@@ -59,8 +151,8 @@ void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst,
*dst++ = *src--;
}
-void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight)
+static void v4lconvert_rotate90_rgbbgr24(const unsigned char *src,
+ unsigned char *dst, int destwidth, int destheight)
{
int x, y;
#define srcwidth destheight
@@ -75,8 +167,8 @@ void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst,
}
}
-void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight)
+static void v4lconvert_rotate90_yuv420(const unsigned char *src,
+ unsigned char *dst, int destwidth, int destheight)
{
int x, y;
@@ -105,3 +197,70 @@ void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst,
*dst++ = src[offset];
}
}
+
+void v4lconvert_rotate90(unsigned char *src, unsigned char *dest,
+ struct v4l2_format *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 hflip, int vflip)
+{
+ if (vflip && hflip) {
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ 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, fmt->fmt.pix.width,
+ fmt->fmt.pix.height);
+ break;
+ }
+ } else if (hflip) {
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_hflip_rgbbgr24(src, dest, fmt);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_hflip_yuv420(src, dest, fmt);
+ break;
+ }
+ } else if (vflip) {
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_vflip_rgbbgr24(src, dest, fmt);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_vflip_yuv420(src, dest, fmt);
+ break;
+ }
+ }
+
+ /* Our newly written data has no padding */
+ v4lconvert_fixup_fmt(fmt);
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h b/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h
new file mode 100644
index 000000000..8ce12343c
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h
@@ -0,0 +1,79 @@
+/* Utility functions for decompression helpers
+ *
+ * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+static int v4lconvert_helper_write(int fd, const void *b, size_t count,
+ char *progname)
+{
+ const unsigned char *buf = b;
+ size_t ret, written = 0;
+
+ while (written < count) {
+ ret = write(fd, buf + written, count - written);
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EPIPE) /* Main program has quited */
+ exit(0);
+
+ fprintf(stderr, "%s: error writing: %s\n", progname, strerror(errno));
+ return -1;
+ }
+ written += ret;
+ }
+
+ return 0;
+}
+
+static int v4lconvert_helper_read(int fd, void *b, size_t count,
+ char *progname)
+{
+ unsigned char *buf = b;
+ size_t ret, r = 0;
+
+ while (r < count) {
+ ret = read(fd, buf + r, count - r);
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+
+ fprintf(stderr, "%s: error reading: %s\n", progname, strerror(errno));
+ return -1;
+ }
+ if (ret == 0) /* EOF */
+ exit(0);
+
+ r += ret;
+ }
+
+ return 0;
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/helper.c b/v4l2-apps/libv4l/libv4lconvert/helper.c
new file mode 100644
index 000000000..c1d55c2e5
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/helper.c
@@ -0,0 +1,224 @@
+/*
+# (C) 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include "libv4lconvert-priv.h"
+
+#define READ_END 0
+#define WRITE_END 1
+
+/* <sigh> Unfortunately I've failed in contact some Authors of decompression
+ code of out of tree drivers. So I've no permission to relicense their code
+ their code from GPL to LGPL. To work around this, these decompression
+ algorithms are put in separate executables and we pipe data through these
+ to decompress.
+
+ The "protocol" is very simple:
+
+ From libv4l to the helper the following is send:
+ int width
+ int height
+ int flags
+ int data length
+ unsigned char[] data (data length long)
+
+ From the helper to libv4l the following is send:
+ int data length (-1 in case of a decompression error)
+ unsigned char[] data (not present when a decompression error happened)
+*/
+
+static int v4lconvert_helper_start(struct v4lconvert_data *data,
+ const char *helper)
+{
+ if (pipe(data->decompress_in_pipe)) {
+ V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno));
+ goto error;
+ }
+
+ if (pipe(data->decompress_out_pipe)) {
+ V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno));
+ goto error_close_in_pipe;
+ }
+
+ data->decompress_pid = fork();
+ if (data->decompress_pid == -1) {
+ V4LCONVERT_ERR("with helper fork: %s\n", strerror(errno));
+ goto error_close_out_pipe;
+ }
+
+ if (data->decompress_pid == 0) {
+ /* We are the child */
+
+ /* Closed unused read / write end of the pipes */
+ close(data->decompress_out_pipe[WRITE_END]);
+ close(data->decompress_in_pipe[READ_END]);
+
+ /* Connect stdin / out to the pipes */
+ if (dup2(data->decompress_out_pipe[READ_END], STDIN_FILENO) == -1) {
+ perror("libv4lconvert: error with helper dup2");
+ exit(1);
+ }
+ if (dup2(data->decompress_in_pipe[WRITE_END], STDOUT_FILENO) == -1) {
+ perror("libv4lconvert: error with helper dup2");
+ exit(1);
+ }
+
+ /* And execute the helper */
+ execl(helper, helper, NULL);
+
+ /* We should never get here */
+ perror("libv4lconvert: error starting helper");
+ exit(1);
+ } else {
+ /* Closed unused read / write end of the pipes */
+ close(data->decompress_out_pipe[READ_END]);
+ close(data->decompress_in_pipe[WRITE_END]);
+ }
+
+ return 0;
+
+error_close_out_pipe:
+ close(data->decompress_out_pipe[READ_END]);
+ close(data->decompress_out_pipe[WRITE_END]);
+error_close_in_pipe:
+ close(data->decompress_in_pipe[READ_END]);
+ close(data->decompress_in_pipe[WRITE_END]);
+error:
+ return -1;
+}
+
+/* IMPROVE ME: we could block SIGPIPE here using pthread_sigmask()
+ and then in case of EPIPE consume the signal using
+ sigtimedwait (we need to check if a blocked signal wasn't present
+ before the write, otherwise we will consume that) and
+ after consuming the signal try to restart the helper.
+
+ Note we currently do not do this, as SIGPIPE only happens if the
+ decompressor crashes, which in case of an embedded decompressor
+ would mean end of program, so by not handling SIGPIPE we treat
+ external decompressors identical. */
+static int v4lconvert_helper_write(struct v4lconvert_data *data,
+ const void *b, size_t count)
+{
+ const unsigned char *buf = b;
+ const int *i = b;
+ size_t ret, written = 0;
+
+ while (written < count) {
+ ret = write(data->decompress_out_pipe[WRITE_END], buf + written,
+ count - written);
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+
+ V4LCONVERT_ERR("writing to helper: %s\n", strerror(errno));
+ return -1;
+ }
+ written += ret;
+ }
+
+ return 0;
+}
+
+static int v4lconvert_helper_read(struct v4lconvert_data *data, void *b,
+ size_t count)
+{
+ unsigned char *buf = b;
+ size_t ret, r = 0;
+
+ while (r < count) {
+ ret = read(data->decompress_in_pipe[READ_END], buf + r, count - r);
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+
+ V4LCONVERT_ERR("reading from helper: %s\n", strerror(errno));
+ return -1;
+ }
+ if (ret == 0) {
+ V4LCONVERT_ERR("reading from helper: unexpected EOF\n");
+ return -1;
+ }
+ r += ret;
+ }
+
+ return 0;
+}
+
+int v4lconvert_helper_decompress(struct v4lconvert_data *data,
+ const char *helper, const unsigned char *src, int src_size,
+ unsigned char *dest, int dest_size, int width, int height, int flags)
+{
+ int r;
+
+ if (data->decompress_pid == -1) {
+ if (v4lconvert_helper_start(data, helper))
+ return -1;
+ }
+
+ if (v4lconvert_helper_write(data, &width, sizeof(int)))
+ return -1;
+
+ if (v4lconvert_helper_write(data, &height, sizeof(int)))
+ return -1;
+
+ if (v4lconvert_helper_write(data, &flags, sizeof(int)))
+ return -1;
+
+ if (v4lconvert_helper_write(data, &src_size, sizeof(int)))
+ return -1;
+
+ if (v4lconvert_helper_write(data, src, src_size))
+ return -1;
+
+ if (v4lconvert_helper_read(data, &r, sizeof(int)))
+ return -1;
+
+ if (r < 0) {
+ V4LCONVERT_ERR("decompressing frame data\n");
+ return -1;
+ }
+
+ if (dest_size < r) {
+ V4LCONVERT_ERR("destination buffer to small\n");
+ return -1;
+ }
+
+ return v4lconvert_helper_read(data, dest, r);
+}
+
+void v4lconvert_helper_cleanup(struct v4lconvert_data *data)
+{
+ int status;
+
+ if (data->decompress_pid != -1) {
+ kill(data->decompress_pid, SIGTERM);
+ waitpid(data->decompress_pid, &status, 0);
+
+ close(data->decompress_out_pipe[WRITE_END]);
+ close(data->decompress_in_pipe[READ_END]);
+
+ data->decompress_pid = -1;
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/hm12.c b/v4l2-apps/libv4l/libv4lconvert/hm12.c
new file mode 100644
index 000000000..f711627b4
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/hm12.c
@@ -0,0 +1,159 @@
+/*
+
+cx2341x HM12 conversion routines
+
+(C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+
+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 "libv4lconvert-priv.h"
+#include <string.h>
+
+/* The HM12 format is used in the Conexant cx23415/6/8 MPEG encoder devices.
+ It is a macroblock format with separate Y and UV planes, each plane
+ consisting of 16x16 values. All lines are always 720 bytes long. If the
+ width of the image is less than 720, then the remainder is padding.
+
+ The height has to be a multiple of 32 in order to get correct chroma
+ values.
+
+ It is basically a by-product of the MPEG encoding inside the device,
+ which is available for raw video as a 'bonus feature'.
+ */
+
+#define CLIP(color) \
+ (unsigned char)(((color) > 0xff) ? 0xff : (((color) < 0) ? 0 : (color)))
+
+static const int stride = 720;
+
+static void v4lconvert_hm12_to_rgb(const unsigned char *src, unsigned char *dest,
+ int width, int height, int rgb)
+{
+ unsigned int y, x, i, j;
+ const unsigned char *y_base = src;
+ const unsigned char *uv_base = src + stride * height;
+ const unsigned char *src_y;
+ const unsigned char *src_uv;
+ int mb_size = 256;
+ int r = rgb ? 0 : 2;
+ int b = 2 - r;
+
+ for (y = 0; y < height; y += 16) {
+ int mb_y = (y / 16) * (stride / 16);
+ int mb_uv = (y / 32) * (stride / 16);
+ int maxy = (height - y < 16 ? height - y : 16);
+
+ for (x = 0; x < width; x += 16, mb_y++, mb_uv++) {
+ int maxx = (width - x < 16 ? width - x : 16);
+
+ src_y = y_base + mb_y * mb_size;
+ src_uv = uv_base + mb_uv * mb_size;
+
+ if (y & 0x10)
+ src_uv += mb_size / 2;
+
+ for (i = 0; i < maxy; i++) {
+ int idx = (x + (y + i) * width) * 3;
+
+ for (j = 0; j < maxx; j++) {
+ int y = src_y[j];
+ int u = src_uv[j & ~1];
+ int v = src_uv[j | 1];
+ int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
+ int rg = (((u - 128) << 1) + (u - 128) +
+ ((v - 128) << 2) + ((v - 128) << 1)) >> 3;
+ int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
+
+ dest[idx+r] = CLIP(y + v1);
+ dest[idx+1] = CLIP(y - rg);
+ dest[idx+b] = CLIP(y + u1);
+ idx += 3;
+ }
+ src_y += 16;
+ if (i & 1)
+ src_uv += 16;
+ }
+ }
+ }
+}
+
+void v4lconvert_hm12_to_rgb24(const unsigned char *src, unsigned char *dest,
+ int width, int height)
+{
+ v4lconvert_hm12_to_rgb(src, dest, width, height, 1);
+}
+
+void v4lconvert_hm12_to_bgr24(const unsigned char *src, unsigned char *dest,
+ int width, int height)
+{
+ v4lconvert_hm12_to_rgb(src, dest, width, height, 0);
+}
+
+static void de_macro_uv(unsigned char *dstu, unsigned char *dstv,
+ const unsigned char *src, int w, int h)
+{
+ unsigned int y, x, i, j;
+
+ for (y = 0; y < h; y += 16) {
+ for (x = 0; x < w; x += 8) {
+ const unsigned char *src_uv = src + y * stride + x * 32;
+ int maxy = (h - y < 16 ? h - y : 16);
+ int maxx = (w - x < 8 ? w - x : 8);
+
+ for (i = 0; i < maxy; i++) {
+ int idx = x + (y + i) * w;
+
+ for (j = 0; j < maxx; j++) {
+ dstu[idx+j] = src_uv[2 * j];
+ dstv[idx+j] = src_uv[2 * j + 1];
+ }
+ src_uv += 16;
+ }
+ }
+ }
+}
+
+static void de_macro_y(unsigned char *dst, const unsigned char *src,
+ int w, int h)
+{
+ unsigned int y, x, i;
+
+ for (y = 0; y < h; y += 16) {
+ for (x = 0; x < w; x += 16) {
+ const unsigned char *src_y = src + y * stride + x * 16;
+ int maxy = (h - y < 16 ? h - y : 16);
+ int maxx = (w - x < 16 ? w - x : 16);
+
+ for (i = 0; i < maxy; i++) {
+ memcpy(dst + x + (y + i) * w, src_y, maxx);
+ src_y += 16;
+ }
+ }
+ }
+}
+
+void v4lconvert_hm12_to_yuv420(const unsigned char *src, unsigned char *dest,
+ int width, int height, int yvu)
+{
+ de_macro_y(dest, src, width, height);
+ dest += width * height;
+ src += stride * height;
+ if (yvu)
+ de_macro_uv(dest + width * height / 4, dest, src, width / 2, height / 2);
+ else
+ de_macro_uv(dest, dest + width * height / 4, src, width / 2, height / 2);
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h
index 0c4eff6ce..2cf8f6ba4 100644
--- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h
+++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h
@@ -20,9 +20,14 @@
#define __LIBV4LCONVERT_PRIV_H
#include <stdio.h>
+#include <sys/types.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
@@ -43,6 +48,14 @@
#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7')
#endif
+#ifndef V4L2_PIX_FMT_MR97310A
+#define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M','3','1','0')
+#endif
+
+#ifndef V4L2_PIX_FMT_SQ905C
+#define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C')
+#endif
+
#ifndef V4L2_PIX_FMT_PJPG
#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G')
#endif
@@ -63,6 +76,24 @@
#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U')
#endif
+#ifndef V4L2_PIX_FMT_HM12
+#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2')
+#endif
+
+#ifndef V4L2_PIX_FMT_SN9C20X_I420
+#define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0')
+#endif
+
+#ifndef V4L2_PIX_FMT_OV511
+#define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1')
+#endif
+
+#ifndef V4L2_PIX_FMT_OV518
+#define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
+#endif
+
+#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0]))
+
#define V4LCONVERT_ERROR_MSG_SIZE 256
#define V4LCONVERT_MAX_FRAMESIZES 16
@@ -71,25 +102,40 @@
"v4l-convert: error " __VA_ARGS__)
/* Card flags */
-#define V4LCONVERT_UPSIDE_DOWN 0x01
+#define V4LCONVERT_IS_UVC 0x01
+#define V4LCONVERT_IS_SN9C20X 0x02
/* Pixformat flags */
-#define V4LCONVERT_COMPRESSED 0x01
+#define V4LCONVERT_COMPRESSED 0x01 /* Compressed format */
+#define V4LCONVERT_NEEDS_CONVERSION 0x02 /* Apps likely wont know this */
struct v4lconvert_data {
int fd;
int flags; /* bitfield */
+ int control_flags; /* bitfield */
int supported_src_formats; /* bitfield */
unsigned int no_formats;
char error_msg[V4LCONVERT_ERROR_MSG_SIZE];
struct jdec_private *jdec;
struct v4l2_frmsizeenum framesizes[V4LCONVERT_MAX_FRAMESIZES];
unsigned int no_framesizes;
-};
-
-struct v4lconvert_flags_info {
- const char *card;
- int flags;
+ int convert1_buf_size;
+ int convert2_buf_size;
+ int rotate90_buf_size;
+ int flip_buf_size;
+ int convert_pixfmt_buf_size;
+ 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;
+
+ /* Data for external decompression helpers code */
+ pid_t decompress_pid;
+ int decompress_in_pipe[2]; /* Data from helper to us */
+ int decompress_out_pipe[2]; /* Data from us to helper */
};
struct v4lconvert_pixfmt {
@@ -97,11 +143,16 @@ struct v4lconvert_pixfmt {
int flags;
};
+void v4lconvert_fixup_fmt(struct v4l2_format *fmt);
+
+void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, int bgr, int yvu);
+
void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dst,
int width, int height);
@@ -110,7 +161,7 @@ void v4lconvert_yuyv_to_bgr24(const unsigned char *src, unsigned char *dst,
int width, int height);
void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst,
int width, int height);
@@ -118,20 +169,32 @@ void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst,
void v4lconvert_yvyu_to_bgr24(const unsigned char *src, unsigned char *dst,
int width, int height);
-void v4lconvert_yvyu_to_yuv420(const unsigned char *src, unsigned char *dst,
+void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dst,
int width, int height);
+void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dst,
+ int width, int height);
+
+void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dst,
+ int width, int height, int yvu);
+
void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst,
int width, int height);
+void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dst,
+ const struct v4l2_format *src_fmt);
+
void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+ int width, int height, int yvu);
+
+void v4lconvert_sn9c20x_to_yuv420(const unsigned char *src, unsigned char *dst,
+ int width, int height, int yvu);
void v4lconvert_decode_spca561(const unsigned char *src, unsigned char *dst,
int width, int height);
@@ -142,25 +205,43 @@ void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst,
void v4lconvert_decode_pac207(const unsigned char *src, unsigned char *dst,
int width, int height);
+void v4lconvert_decode_mr97310a(const unsigned char *src, unsigned char *dst,
+ int width, int height);
+
+void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst,
+ int width, int height);
+
void v4lconvert_bayer_to_rgb24(const unsigned char *bayer,
unsigned char *rgb, int width, int height, unsigned int pixfmt);
void v4lconvert_bayer_to_bgr24(const unsigned char *bayer,
unsigned char *rgb, int width, int height, unsigned int pixfmt);
-void v4lconvert_bayer_to_yuv420(const unsigned char *bayer,
- unsigned char *yuv, int width, int height, unsigned int pixfmt);
+void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv,
+ int width, int height, unsigned int src_pixfmt, int yvu);
-void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight);
+void v4lconvert_hm12_to_rgb24(const unsigned char *src,
+ unsigned char *dst, int width, int height);
-void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst,
- int destwidth, int destheight);
+void v4lconvert_hm12_to_bgr24(const unsigned char *src,
+ unsigned char *dst, int width, int height);
-void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst,
- int width, int height);
+void v4lconvert_hm12_to_yuv420(const unsigned char *src,
+ unsigned char *dst, int width, int height, int yvu);
-void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height);
+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 hflip, int vflip);
+
+void v4lconvert_crop(unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt);
+
+int v4lconvert_helper_decompress(struct v4lconvert_data *data,
+ const char *helper, const unsigned char *src, int src_size,
+ unsigned char *dest, int dest_size, int width, int height, int command);
+
+void v4lconvert_helper_cleanup(struct v4lconvert_data *data);
#endif
diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
index 93bc67c7e..1389c58bc 100644
--- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
+++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c
@@ -19,56 +19,69 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#include <syscall.h>
#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include "libv4lconvert.h"
#include "libv4lconvert-priv.h"
+#include "libv4lsyscall-priv.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
-#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0]))
/* 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 \
{ V4L2_PIX_FMT_RGB24, 0 }, \
{ V4L2_PIX_FMT_BGR24, 0 }, \
- { V4L2_PIX_FMT_YUV420, 0 }
+ { V4L2_PIX_FMT_YUV420, 0 }, \
+ { V4L2_PIX_FMT_YVU420, 0 }
static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
- unsigned int pixelformat);
+ unsigned int pixelformat, int index);
/* Note uncompressed formats must go first so that they are prefered by
v4lconvert_try_format for low resolutions */
static const struct v4lconvert_pixfmt supported_src_pixfmts[] = {
SUPPORTED_DST_PIXFMTS,
- { V4L2_PIX_FMT_YUYV, 0 },
- { V4L2_PIX_FMT_YVYU, 0 },
- { V4L2_PIX_FMT_SBGGR8, 0 },
- { V4L2_PIX_FMT_SGBRG8, 0 },
- { V4L2_PIX_FMT_SGRBG8, 0 },
- { V4L2_PIX_FMT_SRGGB8, 0 },
- { V4L2_PIX_FMT_SPCA501, 0 },
- { V4L2_PIX_FMT_SPCA505, 0 },
- { V4L2_PIX_FMT_SPCA508, 0 },
- { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED },
- { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_YUYV, 0 },
+ { V4L2_PIX_FMT_YVYU, 0 },
+ { V4L2_PIX_FMT_UYVY, 0 },
+ { V4L2_PIX_FMT_SN9C20X_I420, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_SBGGR8, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_SGBRG8, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_SGRBG8, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_SRGGB8, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_SPCA501, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_SPCA505, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_SPCA508, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_HM12, V4LCONVERT_NEEDS_CONVERSION },
+ { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_SN9C10X, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_PAC207, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_MR97310A, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_SQ905C, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_OV511, V4LCONVERT_COMPRESSED },
+ { V4L2_PIX_FMT_OV518, V4LCONVERT_COMPRESSED },
};
static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = {
SUPPORTED_DST_PIXFMTS
};
-/* List of cams which need special flags */
-static const struct v4lconvert_flags_info v4lconvert_flags[] = {
- { "SPC 200NC ", V4LCONVERT_UPSIDE_DOWN },
- { "SPC 300NC ", V4LCONVERT_UPSIDE_DOWN },
- { "USB Camera (0471:0325)", V4LCONVERT_UPSIDE_DOWN }, /* SPC200NC */
- { "USB Camera (0471:0326)", V4LCONVERT_UPSIDE_DOWN }, /* SPC300NC */
- { "USB Camera (093a:2476)", V4LCONVERT_UPSIDE_DOWN }, /* Genius E-M 112 */
+/* List of well known resolutions which we can get by cropping somewhat larger
+ resolutions */
+static const int v4lconvert_crop_res[][2] = {
+ /* low res VGA resolutions, can be made by software cropping SIF resolutions
+ for cam/drivers which do not support this in hardware */
+ { 320, 240 },
+ { 160, 120 },
+ /* Some CIF cams (with vv6410 sensor) have slightly larger then usual CIF
+ resolutions, make regular CIF resolutions available on these by sw crop */
+ { 352, 288 },
+ { 176, 144 },
};
struct v4lconvert_data *v4lconvert_create(int fd)
@@ -76,12 +89,16 @@ struct v4lconvert_data *v4lconvert_create(int fd)
int i, j;
struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data));
struct v4l2_capability cap;
+ /* This keeps tracks of devices which have only formats for which apps
+ most likely will need conversion and we can thus safely add software
+ processing controls without a performance impact. */
+ int always_needs_conversion = 1;
if (!data)
return NULL;
data->fd = fd;
- data->jdec = NULL;
+ data->decompress_pid = -1;
/* Check supported formats */
for (i = 0; ; i++) {
@@ -89,27 +106,47 @@ struct v4lconvert_data *v4lconvert_create(int fd)
fmt.index = i;
- if (syscall(SYS_ioctl, fd, VIDIOC_ENUM_FMT, &fmt))
+ if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, &fmt))
break;
for (j = 0; j < ARRAY_SIZE(supported_src_pixfmts); j++)
if (fmt.pixelformat == supported_src_pixfmts[j].fmt) {
data->supported_src_formats |= 1 << j;
+ v4lconvert_get_framesizes(data, fmt.pixelformat, j);
+ if (!supported_src_pixfmts[j].flags)
+ always_needs_conversion = 0;
break;
}
- v4lconvert_get_framesizes(data, fmt.pixelformat);
+ if (j == ARRAY_SIZE(supported_src_pixfmts))
+ always_needs_conversion = 0;
}
data->no_formats = i;
/* Check if this cam has any special flags */
- if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) {
- for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++)
- if (!strcmp((const char *)v4lconvert_flags[i].card, (char *)cap.card)) {
- data->flags = v4lconvert_flags[i].flags;
- break;
- }
+ if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP, &cap) == 0) {
+ if (!strcmp((char *)cap.driver, "uvcvideo"))
+ data->flags |= V4LCONVERT_IS_UVC;
+ else if (!strcmp((char *)cap.driver, "sn9c20x"))
+ data->flags |= V4LCONVERT_IS_SN9C20X;
+
+ if ((cap.capabilities & 0xff) & ~V4L2_CAP_VIDEO_CAPTURE)
+ always_needs_conversion = 0;
+ }
+
+ data->control = v4lcontrol_create(fd, always_needs_conversion);
+ if (!data->control) {
+ free(data);
+ return NULL;
+ }
+ data->control_flags = v4lcontrol_get_flags(data->control);
+
+ data->processing = v4lprocessing_create(fd, data->control);
+ if (!data->processing) {
+ v4lcontrol_destroy(data->control);
+ free(data);
+ return NULL;
}
return data;
@@ -117,15 +154,23 @@ struct v4lconvert_data *v4lconvert_create(int fd)
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);
}
+ v4lconvert_helper_cleanup(data);
+ free(data->convert1_buf);
+ free(data->convert2_buf);
+ free(data->rotate90_buf);
+ free(data->flip_buf);
+ free(data->convert_pixfmt_buf);
free(data);
}
-static int v4lconvert_supported_dst_format(unsigned int pixelformat)
+int v4lconvert_supported_dst_format(unsigned int pixelformat)
{
int i;
@@ -136,6 +181,12 @@ static int v4lconvert_supported_dst_format(unsigned int pixelformat)
return i != ARRAY_SIZE(supported_dst_pixfmts);
}
+int v4lconvert_supported_dst_fmt_only(struct v4lconvert_data *data)
+{
+ return v4lcontrol_needs_conversion(data->control) &&
+ data->supported_src_formats;
+}
+
/* See libv4lconvert.h for description of in / out parameters */
int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
{
@@ -143,17 +194,22 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
unsigned int faked_fmts[ARRAY_SIZE(supported_dst_pixfmts)];
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- fmt->index < data->no_formats ||
- !data->supported_src_formats)
- return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FMT, fmt);
+ (!v4lconvert_supported_dst_fmt_only(data) &&
+ fmt->index < data->no_formats))
+ return SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, fmt);
for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++)
- if (!(data->supported_src_formats & (1 << i))) {
+ if (v4lconvert_supported_dst_fmt_only(data) ||
+ !(data->supported_src_formats & (1 << i))) {
faked_fmts[no_faked_fmts] = supported_dst_pixfmts[i].fmt;
no_faked_fmts++;
}
- i = fmt->index - data->no_formats;
+ if (!v4lconvert_supported_dst_fmt_only(data))
+ i = fmt->index - data->no_formats;
+ else
+ i = fmt->index;
+
if (i >= no_faked_fmts) {
errno = EINVAL;
return -1;
@@ -171,8 +227,65 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
return 0;
}
-/* See libv4lconvert.h for description of in / out parameters */
-int v4lconvert_try_format(struct v4lconvert_data *data,
+/* Find out what format to use based on the (cached) results of enum
+ framesizes instead of doing a zillion try_fmt calls. This function
+ currently is intended for use with UVC cams only. This is esp.
+ important for UVC based cams as doing try_fmt there actually causes I/O */
+static int v4lconvert_do_try_format_uvc(struct v4lconvert_data *data,
+ struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
+{
+ int i;
+ unsigned int closest_fmt_size_diff = -1;
+ int best_framesize = 0;/* Just use the first format if no small enough one */
+ int best_format = 0;
+
+ for (i = 0; i < data->no_framesizes; i++) {
+ if (data->framesizes[i].discrete.width <= dest_fmt->fmt.pix.width &&
+ data->framesizes[i].discrete.height <= dest_fmt->fmt.pix.height) {
+ int size_x_diff = dest_fmt->fmt.pix.width -
+ data->framesizes[i].discrete.width;
+ int size_y_diff = dest_fmt->fmt.pix.height -
+ data->framesizes[i].discrete.height;
+ unsigned int size_diff = size_x_diff * size_x_diff +
+ size_y_diff * size_y_diff;
+
+ if (size_diff < closest_fmt_size_diff) {
+ closest_fmt_size_diff = size_diff;
+ best_framesize = i;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) {
+ /* is this format supported? */
+ if (!(data->framesizes[best_framesize].pixel_format & (1 << i)))
+ continue;
+
+ if (!best_format ||
+ supported_src_pixfmts[i].fmt == dest_fmt->fmt.pix.pixelformat ||
+ ((data->framesizes[best_framesize].discrete.width > 180 ||
+ data->framesizes[best_framesize].discrete.height > 148) &&
+ (supported_src_pixfmts[i].flags & V4LCONVERT_COMPRESSED)))
+ best_format = supported_src_pixfmts[i].fmt;
+ }
+
+ dest_fmt->fmt.pix.width = data->framesizes[best_framesize].discrete.width;
+ dest_fmt->fmt.pix.height = data->framesizes[best_framesize].discrete.height;
+ dest_fmt->fmt.pix.field = V4L2_FIELD_NONE; /* UVC has no fields */
+ /* Not pretty, the pwc driver doesn't fill these in try_fmt either though,
+ so we should be able to get away with this. */
+ dest_fmt->fmt.pix.bytesperline = 0;
+ dest_fmt->fmt.pix.sizeimage = 0;
+ dest_fmt->fmt.pix.colorspace = 0;
+ dest_fmt->fmt.pix.priv = 0;
+
+ *src_fmt = *dest_fmt;
+ src_fmt->fmt.pix.pixelformat = best_format;
+
+ return 0;
+}
+
+static int v4lconvert_do_try_format(struct v4lconvert_data *data,
struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
int i;
@@ -180,14 +293,8 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
unsigned int desired_pixfmt = dest_fmt->fmt.pix.pixelformat;
struct v4l2_format try_fmt, closest_fmt = { .type = 0 };
- /* Can we do conversion to the requested format & type? */
- if (!v4lconvert_supported_dst_format(desired_pixfmt) ||
- dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- int ret = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt);
- if (src_fmt)
- *src_fmt = *dest_fmt;
- return ret;
- }
+ if (data->flags & V4LCONVERT_IS_UVC)
+ return v4lconvert_do_try_format_uvc(data, dest_fmt, src_fmt);
for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) {
/* is this format supported? */
@@ -197,7 +304,7 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
try_fmt = *dest_fmt;
try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;
- if (!syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, &try_fmt))
+ if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, &try_fmt))
{
if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) {
int size_x_diff = abs((int)try_fmt.fmt.pix.width -
@@ -209,7 +316,7 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
if (size_diff < closest_fmt_size_diff ||
(size_diff == closest_fmt_size_diff &&
(supported_src_pixfmts[i].fmt == desired_pixfmt ||
- ((try_fmt.fmt.pix.width > 176 || try_fmt.fmt.pix.height > 144) &&
+ ((try_fmt.fmt.pix.width > 180 || try_fmt.fmt.pix.height > 148) &&
(supported_src_pixfmts[i].flags & V4LCONVERT_COMPRESSED))))) {
closest_fmt_size_diff = size_diff;
closest_fmt = try_fmt;
@@ -218,35 +325,142 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
}
}
- if (closest_fmt.type == 0) {
- int ret = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt);
+ if (closest_fmt.type == 0)
+ return -1;
+
+ *dest_fmt = closest_fmt;
+ if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt)
+ dest_fmt->fmt.pix.pixelformat = desired_pixfmt;
+ *src_fmt = closest_fmt;
+
+ return 0;
+}
+
+void v4lconvert_fixup_fmt(struct v4l2_format *fmt)
+{
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * 3;
+ fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ fmt->fmt.pix.bytesperline = fmt->fmt.pix.width;
+ fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3 / 2;
+ break;
+ }
+}
+
+/* See libv4lconvert.h for description of in / out parameters */
+int v4lconvert_try_format(struct v4lconvert_data *data,
+ struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
+{
+ int i, result;
+ unsigned int desired_width = dest_fmt->fmt.pix.width;
+ unsigned int desired_height = dest_fmt->fmt.pix.height;
+ struct v4l2_format try_src, try_dest, try2_src, try2_dest;
+
+ if (dest_fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ v4lconvert_supported_dst_fmt_only(data) &&
+ !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat))
+ dest_fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
+
+ try_dest = *dest_fmt;
+
+ /* Can we do conversion to the requested format & type? */
+ if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
+ dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ v4lconvert_do_try_format(data, &try_dest, &try_src)) {
+ result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt);
if (src_fmt)
*src_fmt = *dest_fmt;
- return ret;
+ return result;
}
- *dest_fmt = closest_fmt;
+ /* In case of a non exact resolution match, try again with a slightly larger
+ resolution as some weird devices are not able to crop of the number of
+ extra (border) pixels most sensors have compared to standard resolutions,
+ which we will then just crop of in software */
+ if (try_dest.fmt.pix.width != desired_width ||
+ try_dest.fmt.pix.height != desired_height) {
+ try2_dest = *dest_fmt;
+ try2_dest.fmt.pix.width = desired_width + 7;
+ try2_dest.fmt.pix.height = desired_height + 1;
+ result = v4lconvert_do_try_format(data, &try2_dest, &try2_src);
+ if (result == 0 &&
+ try2_dest.fmt.pix.width >= desired_width &&
+ try2_dest.fmt.pix.width <= desired_width + 7 &&
+ try2_dest.fmt.pix.height >= desired_height &&
+ try2_dest.fmt.pix.height <= desired_height + 1) {
+ /* Success! */
+ try2_dest.fmt.pix.width = desired_width;
+ try2_dest.fmt.pix.height = desired_height;
+ try_dest = try2_dest;
+ try_src = try2_src;
+ }
+ }
- /* Are we converting? */
- if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt) {
- dest_fmt->fmt.pix.pixelformat = desired_pixfmt;
- switch (dest_fmt->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- dest_fmt->fmt.pix.bytesperline = dest_fmt->fmt.pix.width * 3;
- dest_fmt->fmt.pix.sizeimage = dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height * 3;
- break;
- case V4L2_PIX_FMT_YUV420:
- dest_fmt->fmt.pix.bytesperline = dest_fmt->fmt.pix.width;
- dest_fmt->fmt.pix.sizeimage = (dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height * 3) / 2;
+ /* In case of a non exact resolution match, see if this is a well known
+ resolution some apps are hardcoded too and try to give the app what it
+ asked for by cropping a slightly larger resolution or adding a small
+ black border to a slightly smaller resolution */
+ if (try_dest.fmt.pix.width != desired_width ||
+ try_dest.fmt.pix.height != desired_height) {
+ for (i = 0; i < ARRAY_SIZE(v4lconvert_crop_res); i++) {
+ if (v4lconvert_crop_res[i][0] == desired_width &&
+ v4lconvert_crop_res[i][1] == desired_height) {
+ try2_dest = *dest_fmt;
+
+ /* Note these are chosen so that cropping to vga res just works for
+ vv6410 sensor cams, which have 356x292 and 180x148 */
+ try2_dest.fmt.pix.width = desired_width * 113 / 100;
+ try2_dest.fmt.pix.height = desired_height * 124 / 100;
+ result = v4lconvert_do_try_format(data, &try2_dest, &try2_src);
+ if (result == 0 &&
+ (/* Add a small black border of max 16 pixels */
+ (try2_dest.fmt.pix.width >= desired_width - 16 &&
+ try2_dest.fmt.pix.width <= desired_width &&
+ try2_dest.fmt.pix.height >= desired_height - 16 &&
+ try2_dest.fmt.pix.height <= desired_height) ||
+ /* Standard cropping to max 80% of actual width / height */
+ (try2_dest.fmt.pix.width >= desired_width &&
+ try2_dest.fmt.pix.width <= desired_width * 5 / 4 &&
+ try2_dest.fmt.pix.height >= desired_height &&
+ try2_dest.fmt.pix.height <= desired_height * 5 / 4) ||
+ /* Downscale 2x + cropping to max 80% of actual width / height */
+ (try2_dest.fmt.pix.width >= desired_width * 2 &&
+ try2_dest.fmt.pix.width <= desired_width * 5 / 2 &&
+ try2_dest.fmt.pix.height >= desired_height * 2 &&
+ try2_dest.fmt.pix.height <= desired_height * 5 / 2))) {
+ /* Success! */
+ try2_dest.fmt.pix.width = desired_width;
+ try2_dest.fmt.pix.height = desired_height;
+ try_dest = try2_dest;
+ try_src = try2_src;
+ }
break;
+ }
}
}
+ /* Some applications / libs (*cough* gstreamer *cough*) will not work
+ correctly with planar YUV formats when the width is not a multiple of 8
+ or the height is not a multiple of 2. With RGB formats these apps require
+ the width to be a multiple of 4. We apply the same rounding to all
+ formats to not end up with 2 close but different resolutions. */
+ try_dest.fmt.pix.width &= ~7;
+ try_dest.fmt.pix.height &= ~1;
+
+ /* Are we converting / cropping ? */
+ if(try_src.fmt.pix.width != try_dest.fmt.pix.width ||
+ try_src.fmt.pix.height != try_dest.fmt.pix.height ||
+ try_src.fmt.pix.pixelformat != try_dest.fmt.pix.pixelformat)
+ v4lconvert_fixup_fmt(&try_dest);
+
+ *dest_fmt = try_dest;
if (src_fmt)
- *src_fmt = closest_fmt;
+ *src_fmt = try_src;
return 0;
}
@@ -256,63 +470,71 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data,
const struct v4l2_format *src_fmt, /* in */
const struct v4l2_format *dest_fmt) /* in */
{
- if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt)))
- return 1; /* Formats differ */
-
- if (!(data->flags & V4LCONVERT_UPSIDE_DOWN))
- return 0; /* Formats identical and we don't need flip */
+ if (src_fmt->fmt.pix.width != dest_fmt->fmt.pix.width ||
+ src_fmt->fmt.pix.height != dest_fmt->fmt.pix.height ||
+ src_fmt->fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat ||
+ (v4lcontrol_needs_conversion(data->control) &&
+ v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)))
+ return 1;
- /* Formats are identical, but we need flip, do we support the dest_fmt? */
- if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat))
- return 0; /* Needs flip but we cannot do it :( */
- else
- return 1; /* Needs flip and thus conversion */
+ return 0;
}
-int v4lconvert_convert(struct v4lconvert_data *data,
- const struct v4l2_format *src_fmt, /* in */
- const struct v4l2_format *dest_fmt, /* in */
- unsigned char *src, int src_size, unsigned char *_dest, int dest_size)
+static int v4lconvert_processing_needs_double_conversion(
+ unsigned int src_pix_fmt, unsigned int dest_pix_fmt)
{
- unsigned int header_width, header_height;
- int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE;
- unsigned char *components[3];
- unsigned char *dest = _dest;
-
- /* Special case when no conversion is needed */
- if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) {
- int to_copy = MIN(dest_size, src_size);
- memcpy(dest, src, to_copy);
- return to_copy;
+ 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;
}
-
- /* sanity check, is the dest buffer large enough? */
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
case V4L2_PIX_FMT_BGR24:
- needed = dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3;
- break;
- case V4L2_PIX_FMT_YUV420:
- needed = (dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3) / 2;
- break;
- default:
- V4LCONVERT_ERR("Unknown dest format in conversion\n");
- errno = EINVAL;
- return -1;
+ return 0;
}
- if (dest_size < needed) {
- V4LCONVERT_ERR("destination buffer too small\n");
- errno = EFAULT;
- return -1;
- }
+ return 1;
+}
- if (data->flags & V4LCONVERT_UPSIDE_DOWN) {
- rotate = 180;
- dest = alloca(needed);
+static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data,
+ int needed, unsigned char **buf, int *buf_size)
+{
+ if (*buf_size < needed) {
+ free(*buf);
+ *buf = malloc(needed);
+ if (*buf == NULL) {
+ *buf_size = 0;
+ V4LCONVERT_ERR("could not allocate memory\n");
+ errno = ENOMEM;
+ return NULL;
+ }
+ *buf_size = needed;
}
+ return *buf;
+}
- switch (src_fmt->fmt.pix.pixelformat) {
+static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
+ unsigned char *src, int src_size, unsigned char *dest, int dest_size,
+ 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 = 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:
jpeg_flags |= TINYJPEG_FLAGS_PIXART_JPEG;
/* Fall through */
@@ -335,31 +557,36 @@ int v4lconvert_convert(struct v4lconvert_data *data,
}
tinyjpeg_get_size(data->jdec, &header_width, &header_height);
- if (header_width != dest_fmt->fmt.pix.width ||
- header_height != dest_fmt->fmt.pix.height) {
+ if (header_width != width || header_height != height) {
/* Check for (pixart) rotated JPEG */
- if (header_width == dest_fmt->fmt.pix.height ||
- header_height == dest_fmt->fmt.pix.width) {
- if (!rotate)
- dest = alloca(needed);
- rotate += 90;
+ if (header_width == height && header_height == width) {
+ if (!(data->control_flags & V4LCONTROL_ROTATED_90_JPEG)) {
+ V4LCONVERT_ERR("JPEG needs 90° rotation, please report "
+ "this to <hdegoede@redhat.com>\n");
+ errno = EIO;
+ 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",
- dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height,
- header_width, header_height);
+ "expected: %ux%u, header: %ux%u\n", width, height,
+ header_width, header_height);
errno = EIO;
return -1;
}
+ } else if ((data->control_flags & V4LCONTROL_ROTATED_90_JPEG)) {
+ fprintf(stderr, "libv4lconvert: expected 90° rotated JPEG, but got "
+ "normal JPEG, please report this to <hdegoede@redhat.com>\n");
+ V4LCONVERT_ERR("expected 90° rotated JPEG, but got normal JPEG\n");
+ errno = EAGAIN;
+ data->control_flags &= ~V4LCONTROL_ROTATED_90_JPEG;
+ return -1;
}
components[0] = dest;
- components[1] = components[0] + dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height;
- components[2] = components[1] + (dest_fmt->fmt.pix.width *
- dest_fmt->fmt.pix.height) / 4;
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
tinyjpeg_set_components(data->jdec, components, 1);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_RGB24);
@@ -368,7 +595,15 @@ int v4lconvert_convert(struct v4lconvert_data *data,
tinyjpeg_set_components(data->jdec, components, 1);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_BGR24);
break;
- default:
+ case V4L2_PIX_FMT_YUV420:
+ components[1] = components[0] + width * height;
+ components[2] = components[1] + width * height / 4;
+ tinyjpeg_set_components(data->jdec, components, 3);
+ result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ components[2] = components[0] + width * height;
+ components[1] = components[2] + width * height / 4;
tinyjpeg_set_components(data->jdec, components, 3);
result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P);
break;
@@ -379,8 +614,9 @@ int v4lconvert_convert(struct v4lconvert_data *data,
are best thrown away to avoid flashes in the video stream. Tell
the upper layer this is an intermediate fault and it should try
again with a new buffer by setting errno to EAGAIN */
- if (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_PJPG) {
- V4LCONVERT_ERR("Error decompressing JPEG: %s",
+ if (src_pix_fmt == V4L2_PIX_FMT_PJPG ||
+ data->flags & V4LCONVERT_IS_SN9C20X) {
+ V4LCONVERT_ERR("decompressing JPEG: %s",
tinyjpeg_get_errorstring(data->jdec));
errno = EAGAIN;
return -1;
@@ -394,223 +630,451 @@ int v4lconvert_convert(struct v4lconvert_data *data,
}
break;
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- switch (dest_fmt->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_RGB24:
- v4lconvert_bayer_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
- break;
- case V4L2_PIX_FMT_BGR24:
- v4lconvert_bayer_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
- break;
- default:
- v4lconvert_bayer_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat);
- break;
- }
- break;
-
- /* YUYV line by line formats */
+ /* Custom cam specific YUV formats */
case V4L2_PIX_FMT_SPCA501:
case V4L2_PIX_FMT_SPCA505:
case V4L2_PIX_FMT_SPCA508:
+ case V4L2_PIX_FMT_SN9C20X_I420:
+ case V4L2_PIX_FMT_OV511:
+ case V4L2_PIX_FMT_OV518:
{
- unsigned char tmpbuf[dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height *
- 3 / 2];
- unsigned char *my_dst = (dest_fmt->fmt.pix.pixelformat !=
- V4L2_PIX_FMT_YUV420) ? tmpbuf : dest;
+ unsigned char *d;
+ int d_size;
+ int yvu = 0;
+
+ if (dest_pix_fmt != V4L2_PIX_FMT_YUV420 &&
+ dest_pix_fmt != V4L2_PIX_FMT_YVU420) {
+ d = v4lconvert_alloc_buffer(data, width * height * 3 / 2,
+ &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size);
+ if (!d)
+ return -1;
+ d_size = width * height * 3 / 2;
+ } else {
+ d = dest;
+ d_size = dest_size;
+ }
+
+ if (dest_pix_fmt == V4L2_PIX_FMT_YVU420)
+ yvu = 1;
- switch (src_fmt->fmt.pix.pixelformat) {
+ switch (src_pix_fmt) {
case V4L2_PIX_FMT_SPCA501:
- v4lconvert_spca501_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_spca501_to_yuv420(src, d, width, height, yvu);
break;
case V4L2_PIX_FMT_SPCA505:
- v4lconvert_spca505_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_spca505_to_yuv420(src, d, width, height, yvu);
break;
case V4L2_PIX_FMT_SPCA508:
- v4lconvert_spca508_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_spca508_to_yuv420(src, d, width, height, yvu);
+ break;
+ case V4L2_PIX_FMT_SN9C20X_I420:
+ v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu);
+ break;
+ case V4L2_PIX_FMT_OV511:
+ if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov511-decomp",
+ src, src_size, d, d_size, width, height, yvu)) {
+ /* Corrupt frame, better get another one */
+ errno = EAGAIN;
+ return -1;
+ }
+ break;
+ case V4L2_PIX_FMT_OV518:
+ if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov518-decomp",
+ src, src_size, d, d_size, width, height, yvu)) {
+ /* Corrupt frame, better get another one */
+ errno = EAGAIN;
+ return -1;
+ }
break;
}
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yuv420_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_rgb24(data->convert_pixfmt_buf, dest, width,
+ height, yvu);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yuv420_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_bgr24(data->convert_pixfmt_buf, dest, width,
+ height, yvu);
break;
}
break;
}
+ /* Conexant cx2341x raw video macroblock format */
+ case V4L2_PIX_FMT_HM12:
+ switch (dest_pix_fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ v4lconvert_hm12_to_rgb24(src, dest, width, height);
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ v4lconvert_hm12_to_bgr24(src, dest, width, height);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_hm12_to_yuv420(src, dest, width, height, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_hm12_to_yuv420(src, dest, width, height, 1);
+ break;
+ }
+ break;
+
/* compressed bayer formats */
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:
{
- unsigned char tmpbuf[dest_fmt->fmt.pix.width*dest_fmt->fmt.pix.height];
- unsigned int bayer_fmt = 0;
+ unsigned char *tmpbuf;
+ struct v4l2_format tmpfmt = *fmt;
- switch (src_fmt->fmt.pix.pixelformat) {
+ tmpbuf = v4lconvert_alloc_buffer(data, width * height,
+ &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size);
+ if (!tmpbuf)
+ return -1;
+
+ switch (src_pix_fmt) {
case V4L2_PIX_FMT_SPCA561:
- v4lconvert_decode_spca561(src, tmpbuf, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- bayer_fmt = V4L2_PIX_FMT_SGBRG8;
+ v4lconvert_decode_spca561(src, tmpbuf, width, height);
+ tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG8;
break;
case V4L2_PIX_FMT_SN9C10X:
- v4lconvert_decode_sn9c10x(src, tmpbuf, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- bayer_fmt = V4L2_PIX_FMT_SBGGR8;
+ v4lconvert_decode_sn9c10x(src, tmpbuf, width, height);
+ tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
break;
case V4L2_PIX_FMT_PAC207:
- v4lconvert_decode_pac207(src, tmpbuf, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
- bayer_fmt = V4L2_PIX_FMT_SBGGR8;
+ v4lconvert_decode_pac207(src, tmpbuf, width, height);
+ tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ break;
+ case V4L2_PIX_FMT_MR97310A:
+ v4lconvert_decode_mr97310a(src, tmpbuf, width, height);
+ tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ break;
+ case V4L2_PIX_FMT_SQ905C:
+ v4lconvert_decode_sq905c(src, tmpbuf, width, height);
+ tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
break;
}
+ /* Do processing on the tmp buffer, because doing it on bayer data is
+ cheaper, and bayer == rgb and our dest_fmt may be yuv */
+ tmpfmt.fmt.pix.bytesperline = width;
+ tmpfmt.fmt.pix.sizeimage = width * height;
+ v4lprocessing_processing(data->processing, tmpbuf, &tmpfmt);
+ /* Deliberate fall through to raw bayer fmt code! */
+ src_pix_fmt = tmpfmt.fmt.pix.pixelformat;
+ src = tmpbuf;
+ }
- switch (dest_fmt->fmt.pix.pixelformat) {
+ /* Raw bayer formats */
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_bayer_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, bayer_fmt);
+ v4lconvert_bayer_to_rgb24(src, dest, width, height, src_pix_fmt);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_bayer_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, bayer_fmt);
+ v4lconvert_bayer_to_bgr24(src, dest, width, height, src_pix_fmt);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_bayer_to_yuv420(src, dest, width, height, src_pix_fmt, 0);
break;
- default:
- v4lconvert_bayer_to_yuv420(tmpbuf, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height, bayer_fmt);
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_bayer_to_yuv420(src, dest, width, height, src_pix_fmt, 1);
break;
}
break;
- }
case V4L2_PIX_FMT_RGB24:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_BGR24:
- v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_swap_rgb(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
- printf("FIXME add rgb24 -> yuv420 conversion\n");
+ v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1);
break;
}
break;
+
case V4L2_PIX_FMT_BGR24:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_swap_rgb(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
- printf("FIXME add bgr24 -> yuv420 conversion\n");
+ v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1);
break;
}
break;
case V4L2_PIX_FMT_YUV420:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yuv420_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_rgb24(src, dest, width,
+ height, 0);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yuv420_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_bgr24(src, dest, width,
+ height, 0);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_swap_uv(src, dest, fmt);
break;
}
break;
- case V4L2_PIX_FMT_YUYV:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YVU420:
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yuyv_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_rgb24(src, dest, width,
+ height, 1);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yuyv_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuv420_to_bgr24(src, dest, width,
+ height, 1);
break;
- default:
- v4lconvert_yuyv_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_swap_uv(src, dest, fmt);
break;
}
break;
- case V4L2_PIX_FMT_YVYU:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_yvyu_to_rgb24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuyv_to_rgb24(src, dest, width, height);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_yvyu_to_bgr24(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yuyv_to_bgr24(src, dest, width, height);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height, 0);
break;
- default:
- v4lconvert_yvyu_to_yuv420(src, dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height, 1);
break;
}
break;
- default:
- V4LCONVERT_ERR("Unknown src format in conversion\n");
- errno = EINVAL;
- return -1;
- }
-
- /* Note when rotating dest is our temporary buffer to which our conversion
- was done and _dest is the real dest! If the formats are identical no
- conversion has been done! */
- if (rotate && dest_fmt->fmt.pix.pixelformat == src_fmt->fmt.pix.pixelformat)
- dest = src;
-
- switch (rotate) {
- case 0:
- break;
- case 90:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YVYU:
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
+ v4lconvert_yvyu_to_rgb24(src, dest, width, height);
+ break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_rotate90_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_yvyu_to_bgr24(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
- v4lconvert_rotate90_yuv420(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ /* Note we use yuyv_to_yuv420 not v4lconvert_yvyu_to_yuv420,
+ with the last argument reversed to make it have as we want */
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height, 1);
break;
- }
- break;
- case 180:
- switch (dest_fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_yuyv_to_yuv420(src, dest, width, height, 0);
+ break;
+ }
+ break;
+
+ case V4L2_PIX_FMT_UYVY:
+ switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
+ v4lconvert_uyvy_to_rgb24(src, dest, width, height);
+ break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_rotate180_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_uyvy_to_bgr24(src, dest, width, height);
break;
case V4L2_PIX_FMT_YUV420:
- v4lconvert_rotate180_yuv420(dest, _dest, dest_fmt->fmt.pix.width,
- dest_fmt->fmt.pix.height);
+ v4lconvert_uyvy_to_yuv420(src, dest, width, height, 0);
break;
- }
- break;
- default:
- printf("FIXME add %d degrees rotation\n", rotate);
+ case V4L2_PIX_FMT_YVU420:
+ v4lconvert_uyvy_to_yuv420(src, dest, width, height, 1);
+ break;
+ }
+ break;
+
+ default:
+ V4LCONVERT_ERR("Unknown src format in conversion\n");
+ errno = EINVAL;
+ return -1;
}
- return needed;
+ fmt->fmt.pix.pixelformat = dest_pix_fmt;
+ v4lconvert_fixup_fmt(fmt);
+
+ return 0;
+}
+
+int v4lconvert_convert(struct v4lconvert_data *data,
+ const struct v4l2_format *src_fmt, /* in */
+ 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, processing, convert = 0;
+ int rotate90, vflip, hflip, crop;
+ unsigned char *convert1_dest = dest;
+ int convert1_dest_size = dest_size;
+ unsigned char *convert2_src = src, *convert2_dest = dest;
+ int convert2_dest_size = dest_size;
+ 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;
+
+ processing = v4lprocessing_pre_processing(data->processing);
+ rotate90 = data->control_flags & V4LCONTROL_ROTATED_90_JPEG;
+ hflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_HFLIP);
+ vflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_VFLIP);
+ crop = my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width ||
+ my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height;
+
+ if (/* If no conversion/processing is needed */
+ (src_fmt->fmt.pix.pixelformat == dest_fmt->fmt.pix.pixelformat &&
+ !processing && !rotate90 && !hflip && !vflip && !crop) ||
+ /* or if we should do processing/rotating/flipping but the app tries to
+ use the native cam format, we just return an unprocessed frame copy */
+ !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) {
+ int to_copy = MIN(dest_size, src_size);
+ memcpy(dest, src, to_copy);
+ return to_copy;
+ }
+
+ /* When field is V4L2_FIELD_ALTERNATE, each buffer only contains half the
+ lines */
+ if (my_src_fmt.fmt.pix.field == V4L2_FIELD_ALTERNATE) {
+ my_src_fmt.fmt.pix.height /= 2;
+ my_dest_fmt.fmt.pix.height /= 2;
+ }
+
+ /* sanity check, is the dest buffer large enough? */
+ switch (my_dest_fmt.fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3;
+ temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ dest_needed =
+ my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3 / 2;
+ temp_needed =
+ my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3 / 2;
+ break;
+ default:
+ V4LCONVERT_ERR("Unknown dest format in conversion\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (dest_size < dest_needed) {
+ V4LCONVERT_ERR("destination buffer too small\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+
+ /* 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;
+
+ /* 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;
+
+ convert1_dest_size =
+ my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3;
+ convert2_src = convert1_dest;
+ }
+
+ if (convert && (rotate90 || hflip || vflip || crop)) {
+ convert2_dest = v4lconvert_alloc_buffer(data, temp_needed,
+ &data->convert2_buf, &data->convert2_buf_size);
+ if (!convert2_dest)
+ return -1;
+
+ convert2_dest_size = temp_needed;
+ rotate90_src = flip_src = crop_src = convert2_dest;
+ }
+
+ if (rotate90 && (hflip || vflip || 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 ((vflip || 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, convert1_dest_size,
+ &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, convert2_dest_size,
+ &my_src_fmt,
+ my_dest_fmt.fmt.pix.pixelformat);
+ if (res)
+ return res;
+
+ src_size = my_src_fmt.fmt.pix.sizeimage;
+
+ /* 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, convert2_dest, &my_src_fmt);
+ }
+
+ if (rotate90)
+ v4lconvert_rotate90(rotate90_src, rotate90_dest, &my_src_fmt);
+
+ if (hflip || vflip)
+ v4lconvert_flip(flip_src, flip_dest, &my_src_fmt, hflip, vflip);
+
+ if (crop)
+ v4lconvert_crop(crop_src, dest, &my_src_fmt, &my_dest_fmt);
+
+ return dest_needed;
}
const char *v4lconvert_get_error_message(struct v4lconvert_data *data)
@@ -619,19 +1083,19 @@ const char *v4lconvert_get_error_message(struct v4lconvert_data *data)
}
static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
- unsigned int pixelformat)
+ unsigned int pixelformat, int index)
{
int i, j, match;
struct v4l2_frmsizeenum frmsize = { .pixel_format = pixelformat };
for (i = 0; ; i++) {
frmsize.index = i;
- if (syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize))
+ if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize))
break;
/* We got a framesize, check we don't have the same one already */
match = 0;
- for (j = 0; j < data->no_framesizes && !match; j++) {
+ for (j = 0; j < data->no_framesizes; j++) {
if (frmsize.type != data->framesizes[j].type)
continue;
@@ -648,6 +1112,8 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
match = 1;
break;
}
+ if (match)
+ break;
}
/* Add this framesize if it is not already in our list */
if (!match) {
@@ -657,6 +1123,9 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
return;
}
data->framesizes[data->no_framesizes].type = frmsize.type;
+ /* We use the pixel_format member to store a bitmask of all
+ supported src_formats which can do this size */
+ data->framesizes[data->no_framesizes].pixel_format = 1 << index;
switch(frmsize.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
data->framesizes[data->no_framesizes].discrete = frmsize.discrete;
@@ -668,14 +1137,21 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
}
data->no_framesizes++;
}
+ else
+ data->framesizes[j].pixel_format |= 1 << index;
}
}
int v4lconvert_enum_framesizes(struct v4lconvert_data *data,
struct v4l2_frmsizeenum *frmsize)
{
- if (!v4lconvert_supported_dst_format(frmsize->pixel_format))
- return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize);
+ if (!v4lconvert_supported_dst_format(frmsize->pixel_format)) {
+ if (v4lconvert_supported_dst_fmt_only(data)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize);
+ }
if (frmsize->index >= data->no_framesizes) {
errno = EINVAL;
@@ -686,6 +1162,9 @@ int v4lconvert_enum_framesizes(struct v4lconvert_data *data,
switch(frmsize->type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
frmsize->discrete = data->framesizes[frmsize->index].discrete;
+ /* Apply the same rounding algorithm as v4lconvert_try_format */
+ frmsize->discrete.width &= ~7;
+ frmsize->discrete.height &= ~1;
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE:
@@ -702,8 +1181,16 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
int res;
struct v4l2_format src_fmt, dest_fmt;
- if (!v4lconvert_supported_dst_format(frmival->pixel_format))
- return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+ if (!v4lconvert_supported_dst_format(frmival->pixel_format)) {
+ if (v4lconvert_supported_dst_fmt_only(data)) {
+ errno = EINVAL;
+ return -1;
+ }
+ res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+ if (res)
+ V4LCONVERT_ERR("%s\n", strerror(errno));
+ return res;
+ }
/* Check which format we will be using to convert to frmival->pixel_format */
memset(&dest_fmt, 0, sizeof(dest_fmt));
@@ -711,13 +1198,30 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
dest_fmt.fmt.pix.pixelformat = frmival->pixel_format;
dest_fmt.fmt.pix.width = frmival->width;
dest_fmt.fmt.pix.height = frmival->height;
- if ((res = v4lconvert_try_format(data, &dest_fmt, &src_fmt)))
+ if ((res = v4lconvert_try_format(data, &dest_fmt, &src_fmt))) {
+ if (res)
+ V4LCONVERT_ERR("trying format: %s\n", strerror(errno));
return res;
+ }
/* Check the requested format is supported exactly as requested */
if (dest_fmt.fmt.pix.pixelformat != frmival->pixel_format ||
dest_fmt.fmt.pix.width != frmival->width ||
dest_fmt.fmt.pix.height != frmival->height) {
+ int frmival_pixformat = frmival->pixel_format;
+ int dest_pixformat = dest_fmt.fmt.pix.pixelformat;
+ V4LCONVERT_ERR("Could not find matching framesize for: %c%c%c%c %dx%d "
+ "closest match: %c%c%c%c %dx%d\n",
+ frmival_pixformat & 0xff,
+ (frmival_pixformat >> 8) & 0xff,
+ (frmival_pixformat >> 16) & 0xff,
+ frmival_pixformat >> 24,
+ frmival->width, frmival->height,
+ dest_pixformat & 0xff,
+ (dest_pixformat >> 8) & 0xff,
+ (dest_pixformat >> 16) & 0xff,
+ dest_pixformat >> 24,
+ dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height);
errno = EINVAL;
return -1;
}
@@ -726,7 +1230,24 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
frmival->pixel_format = src_fmt.fmt.pix.pixelformat;
frmival->width = src_fmt.fmt.pix.width;
frmival->height = src_fmt.fmt.pix.height;
- res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+ res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+ if (res) {
+ int dest_pixfmt = dest_fmt.fmt.pix.pixelformat;
+ int src_pixfmt = src_fmt.fmt.pix.pixelformat;
+ V4LCONVERT_ERR("Could not enum frameival index: %d for: %c%c%c%c %dx%d "
+ "using src: %c%c%c%c %dx%d, error: %s\n",
+ frmival->index,
+ dest_pixfmt & 0xff,
+ (dest_pixfmt >> 8) & 0xff,
+ (dest_pixfmt >> 16) & 0xff,
+ dest_pixfmt >> 24,
+ dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height,
+ src_pixfmt & 0xff,
+ (src_pixfmt >> 8) & 0xff,
+ (src_pixfmt >> 16) & 0xff,
+ src_pixfmt >> 24,
+ src_fmt.fmt.pix.width, src_fmt.fmt.pix.height, strerror(errno));
+ }
/* Restore the requested format in the frmival struct */
frmival->pixel_format = dest_fmt.fmt.pix.pixelformat;
@@ -735,3 +1256,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/libv4lsyscall-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h
new file mode 100644
index 000000000..a456ceea3
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2009 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The following file allows for having the complete V4L stack and
+ * hardware drivers in userland.
+ */
+
+#ifndef _LIBV4LSYSCALL_PRIV_H_
+#define _LIBV4LSYSCALL_PRIV_H_
+
+/* Some of these headers are not needed by us, but by linux/videodev2.h,
+ which is broken on some systems and doesn't include them itself :( */
+
+#ifdef linux
+#include <sys/time.h>
+#include <syscall.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */
+#ifdef __NR_mmap2
+#define SYS_mmap2 __NR_mmap2
+#define MMAP2_PAGE_SHIFT 12
+#else
+#define SYS_mmap2 SYS_mmap
+#define MMAP2_PAGE_SHIFT 0
+#endif
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/time.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#define _IOC_NR(cmd) ((cmd) & 0xFF)
+#define _IOC_TYPE(cmd) IOCGROUP(cmd)
+#define _IOC_SIZE(cmd) IOCPARM_LEN(cmd)
+#define MAP_ANONYMOUS MAP_ANON
+#define SYS_mmap2 SYS_mmap
+#define MMAP2_PAGE_SHIFT 0
+typedef off_t __off_t;
+#endif
+
+#undef SYS_OPEN
+#undef SYS_CLOSE
+#undef SYS_IOCTL
+#undef SYS_READ
+#undef SYS_WRITE
+#undef SYS_MMAP
+#undef SYS_MUNMAP
+
+#ifndef CONFIG_SYS_WRAPPER
+
+#define SYS_OPEN(file, oflag, mode) \
+ syscall(SYS_open, (const char *)(file), (int)(oflag), (mode_t)(mode))
+#define SYS_CLOSE(fd) \
+ syscall(SYS_close, (int)(fd))
+#define SYS_IOCTL(fd, cmd, arg) \
+ syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
+#define SYS_READ(fd, buf, len) \
+ syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len));
+#define SYS_WRITE(fd, buf, len) \
+ syscall(SYS_write, (int)(fd), (void *)(buf), (size_t)(len));
+#define SYS_MMAP(addr, len, prot, flags, fd, off) \
+ syscall(SYS_mmap2, (void *)(addr), (size_t)(len), \
+ (int)(prot), (int)(flags), (int)(fd), (__off_t)((off) >> MMAP2_PAGE_SHIFT))
+#define SYS_MUNMAP(addr, len) \
+ syscall(SYS_munmap, (void *)(addr), (size_t)(len))
+
+#else
+
+int v4lx_open_wrapper(const char *, int, int);
+int v4lx_close_wrapper(int);
+int v4lx_ioctl_wrapper(int, unsigned long, void *);
+int v4lx_read_wrapper(int, void *, size_t);
+int v4lx_write_wrapper(int, void *, size_t);
+void *v4lx_mmap_wrapper(void *, size_t, int, int, int, off_t);
+int v4lx_munmap_wrapper(void *, size_t);
+
+#define SYS_OPEN(...) v4lx_open_wrapper(__VA_ARGS__)
+#define SYS_CLOSE(...) v4lx_close_wrapper(__VA_ARGS__)
+#define SYS_IOCTL(...) v4lx_ioctl_wrapper(__VA_ARGS__)
+#define SYS_READ(...) v4lx_read_wrapper(__VA_ARGS__)
+#define SYS_WRITE(...) v4lx_write_wrapper(__VA_ARGS__)
+#define SYS_MMAP(...) v4lx_mmap_wrapper(__VA_ARGS__)
+#define SYS_MUNMAP(...) v4lx_munmap_wrapper(__VA_ARGS__)
+
+#endif
+
+#endif /* _LIBV4LSYSCALL_PRIV_H_ */
diff --git a/v4l2-apps/libv4l/libv4lconvert/mr97310a.c b/v4l2-apps/libv4l/libv4lconvert/mr97310a.c
new file mode 100644
index 000000000..e6ce94b29
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/mr97310a.c
@@ -0,0 +1,172 @@
+/*
+ * MR97310A decoder
+ *
+ * Copyright (C) 2004 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "libv4lconvert-priv.h"
+
+#define CLIP(x) ((x)<0?0:((x)>0xff)?0xff:(x))
+
+/* FIXME not threadsafe */
+static int decoder_initialized = 0;
+
+static struct {
+ unsigned char is_abs;
+ unsigned char len;
+ signed char val;
+} table[256];
+
+static void init_mr97310a_decoder(void)
+{
+ int i;
+ int is_abs, val, len;
+
+ for (i = 0; i < 256; ++i) {
+ is_abs = 0;
+ val = 0;
+ len = 0;
+ if ((i & 0x80) == 0) {
+ /* code 0 */
+ val = 0;
+ len = 1;
+ } else if ((i & 0xe0) == 0xc0) {
+ /* code 110 */
+ val = -3;
+ len = 3;
+ } else if ((i & 0xe0) == 0xa0) {
+ /* code 101 */
+ val = +3;
+ len = 3;
+ } else if ((i & 0xf0) == 0x80) {
+ /* code 1000 */
+ val = +7;
+ len = 4;
+ } else if ((i & 0xf0) == 0x90) {
+ /* code 1001 */
+ val = -7;
+ len = 4;
+ } else if ((i & 0xf0) == 0xf0) {
+ /* code 1111 */
+ val = -15;
+ len = 4;
+ } else if ((i & 0xf8) == 0xe0) {
+ /* code 11100 */
+ val = +15;
+ len = 5;
+ } else if ((i & 0xf8) == 0xe8) {
+ /* code 11101xxxxx */
+ is_abs = 1;
+ val = 0; /* value is calculated later */
+ len = 5;
+ }
+ table[i].is_abs = is_abs;
+ table[i].val = val;
+ table[i].len = len;
+ }
+ decoder_initialized = 1;
+}
+
+static inline unsigned char get_byte(const unsigned char *inp,
+ unsigned int bitpos)
+{
+ const unsigned char *addr;
+ addr = inp + (bitpos >> 3);
+ return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7)));
+}
+
+void v4lconvert_decode_mr97310a(const unsigned char *inp, unsigned char *outp,
+ int width, int height)
+{
+ int row, col;
+ int val;
+ int bitpos;
+ unsigned char code;
+ unsigned char lp, tp, tlp, trp;
+
+ if (!decoder_initialized)
+ init_mr97310a_decoder();
+
+ /* remove the header */
+ inp += 12;
+
+ bitpos = 0;
+
+ /* main decoding loop */
+ for (row = 0; row < height; ++row) {
+ col = 0;
+
+ /* first two pixels in first two rows are stored as raw 8-bit */
+ if (row < 2) {
+ code = get_byte(inp, bitpos);
+ bitpos += 8;
+ *outp++ = code;
+
+ code = get_byte(inp, bitpos);
+ bitpos += 8;
+ *outp++ = code;
+
+ col += 2;
+ }
+
+ while (col < width) {
+ /* get bitcode */
+ code = get_byte(inp, bitpos);
+ /* update bit position */
+ bitpos += table[code].len;
+
+ /* calculate pixel value */
+ if (table[code].is_abs) {
+ /* get 5 more bits and use them as absolute value */
+ code = get_byte(inp, bitpos);
+ val = (code & 0xf8);
+ bitpos += 5;
+
+ } else {
+ /* value is relative to top or left pixel */
+ val = table[code].val;
+ lp = outp[-2];
+ if (row > 1) {
+ tlp = outp[-2*width-2];
+ tp = outp[-2*width];
+ trp = outp[-2*width+2];
+ }
+ if (row < 2) {
+ /* top row: relative to left pixel */
+ val += lp;
+ } else if (col < 2) {
+ /* left column: relative to top pixel */
+ /* initial estimate */
+ val += (2*tp + 2*trp + 1)/4;
+ } else if (col > width - 3) {
+ /* left column: relative to top pixel */
+ val += (2*tp + 2*tlp + 1)/4;
+ /* main area: average of left and top pixel */
+ } else {
+ /* initial estimate for predictor */
+ val += (2*lp + tp + trp + 1)/4;
+ }
+ }
+ /* store pixel */
+ *outp++ = CLIP(val);
+ ++col;
+ }
+ }
+
+ return;
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c b/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c
new file mode 100644
index 000000000..11ed2a601
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c
@@ -0,0 +1,666 @@
+/* We would like to embed this inside libv4l, but we cannot as I've failed
+ to contact Mark W. McClelland to get permission to relicense this,
+ so this lives in an external (GPL licensed) helper */
+
+/* OV511 Decompression Support Module
+ *
+ * Copyright (c) 1999-2003 Mark W. McClelland. All rights reserved.
+ * http://alpha.dyndns.org/ov511/
+ *
+ * Original decompression code Copyright 1998-2000 OmniVision Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include "helper-funcs.h"
+
+/******************************************************************************
+ * Decompression Functions
+ ******************************************************************************/
+
+static void
+DecompressYHI(unsigned char *pIn,
+ unsigned char *pOut,
+ int *iIn, /* in/out */
+ int *iOut, /* in/out */
+ const int w,
+ const int YUVFlag)
+{
+ short ZigZag[64];
+ int temp[64];
+ int Zcnt_Flag = 0;
+ int Num8_Flag = 0;
+ int in_pos = *iIn;
+ int out_pos = *iOut;
+ int tmp, tmp1, tmp2, tmp3;
+ unsigned char header, ZTable[64];
+ short tmpl, tmph, half_byte, idx, count;
+ unsigned long ZigZag_length = 0, ZT_length, i, j;
+ short DeZigZag[64];
+
+ const short a = 11584;
+ const short b = 16068;
+ const short c = 15136;
+ const short d = 13624;
+ const short e = 9104;
+ const short f = 6270;
+ const short g = 3196;
+
+ int out_idx;
+
+ /* Take off every 'Zig' */
+ for (i = 0; i < 64; i++) {
+ ZigZag[i] = 0;
+ }
+
+ /*****************************
+ * Read in the Y header byte *
+ *****************************/
+
+ header = pIn[in_pos];
+ in_pos++;
+
+ ZigZag_length = header & 0x3f;
+ ZigZag_length = ZigZag_length + 1;
+
+ Num8_Flag = header & 0x40;
+ Zcnt_Flag = header & 0x80;
+
+ /*************************
+ * Read in the Y content *
+ *************************/
+
+ if (Zcnt_Flag == 0) { /* Without Zero Table read contents directly */
+ /* Read in ZigZag[0] */
+ ZigZag[0] = pIn[in_pos++];
+ tmpl = pIn[in_pos++];
+ tmph = tmpl<<8;
+ ZigZag[0] = ZigZag[0] | tmph;
+ ZigZag[0] = ZigZag[0]<<4;
+ ZigZag[0] = ZigZag[0]>>4;
+
+ if (Num8_Flag) { /* 8 Bits */
+ for (i = 1; i < ZigZag_length; i++) {
+ ZigZag[i] = pIn[in_pos++];
+ ZigZag[i] = ZigZag[i]<<8;
+ ZigZag[i] = ZigZag[i]>>8;
+ }
+ } else { /* 12 bits and has no Zero Table */
+ idx = 1;
+ half_byte = 0;
+ for (i = 1; i < ZigZag_length; i++) {
+ if (half_byte == 0) {
+ ZigZag[i] = pIn[in_pos++];
+ tmpl = pIn[in_pos++];
+ tmph = tmpl<<8;
+ tmph = tmph&0x0f00;
+ ZigZag[i] = ZigZag[i] | tmph;
+ ZigZag[i] = ZigZag[i]<<4;
+ ZigZag[i] = ZigZag[i]>>4;
+ half_byte = 1;
+ } else {
+ ZigZag[i] = pIn[in_pos++];
+ ZigZag[i] = ZigZag[i]<<8;
+ tmpl = tmpl & 0x00f0;
+ ZigZag[i] = ZigZag[i] | tmpl;
+ ZigZag[i] = ZigZag[i]>>4;
+ half_byte = 0;
+ }
+ }
+ }
+ } else { /* Has Zero Table */
+ /* Calculate Z-Table length */
+ ZT_length = ZigZag_length/8;
+ tmp = ZigZag_length%8;
+
+ if (tmp > 0) {
+ ZT_length = ZT_length + 1;
+ }
+
+ /* Read in Zero Table */
+ for (j = 0; j < ZT_length; j++) {
+ ZTable[j] = pIn[in_pos++];
+ }
+
+ /* Read in ZigZag[0] */
+ ZigZag[0] = pIn[in_pos++];
+ tmpl = pIn[in_pos++];
+ tmph = tmpl<<8;
+ ZigZag[0] = ZigZag[0] | tmph;
+ ZigZag[0] = ZigZag[0]<<4;
+ ZigZag[0] = ZigZag[0]>>4;
+
+ /* Decode ZigZag */
+ idx = 0;
+ ZTable[idx] = ZTable[idx]<<1;
+ count = 7;
+
+ if (Num8_Flag) { /* 8 Bits and has zero table */
+ for (i = 1; i < ZigZag_length; i++) {
+ if ((ZTable[idx]&0x80)) {
+ ZigZag[i] = pIn[in_pos++];
+ ZigZag[i] = ZigZag[i]<<8;
+ ZigZag[i] = ZigZag[i]>>8;
+ }
+
+ ZTable[idx]=ZTable[idx]<<1;
+ count--;
+ if (count == 0) {
+ count = 8;
+ idx++;
+ }
+ }
+ } else { /* 12 bits and has Zero Table */
+ half_byte = 0;
+ for (i = 1; i < ZigZag_length; i++) {
+ if (ZTable[idx]&0x80) {
+ if (half_byte == 0) {
+ ZigZag[i] = pIn[in_pos++];
+ tmpl = pIn[in_pos++];
+ tmph = tmpl <<8;
+ tmph = tmph & 0x0f00;
+ ZigZag[i] = ZigZag[i] | tmph;
+ ZigZag[i] = ZigZag[i]<<4;
+ ZigZag[i] = ZigZag[i]>>4;
+ half_byte = 1;
+ } else {
+ ZigZag[i] = pIn[in_pos++];
+ ZigZag[i] = ZigZag[i]<<8;
+ tmpl = tmpl & 0x00f0;
+ ZigZag[i] = ZigZag[i] | tmpl;
+ ZigZag[i] = ZigZag[i]>>4;
+ half_byte = 0;
+ }
+ }
+
+ ZTable[idx] = ZTable[idx]<<1;
+ count--;
+ if (count == 0) {
+ count = 8;
+ idx++;
+ }
+ }
+ }
+ }
+
+ /*************
+ * De-ZigZag *
+ *************/
+
+ for (j = 0; j < 64; j++) {
+ DeZigZag[j] = 0;
+ }
+
+ if (YUVFlag == 1) {
+ DeZigZag[0] = ZigZag[0];
+ DeZigZag[1] = ZigZag[1]<<1;
+ DeZigZag[2] = ZigZag[5]<<1;
+ DeZigZag[3] = ZigZag[6]<<2;
+
+ DeZigZag[8] = ZigZag[2]<<1;
+ DeZigZag[9] = ZigZag[4]<<1;
+ DeZigZag[10] = ZigZag[7]<<1;
+ DeZigZag[11] = ZigZag[13]<<2;
+
+ DeZigZag[16] = ZigZag[3]<<1;
+ DeZigZag[17] = ZigZag[8]<<1;
+ DeZigZag[18] = ZigZag[12]<<2;
+ DeZigZag[19] = ZigZag[17]<<2;
+
+ DeZigZag[24] = ZigZag[9]<<2;
+ DeZigZag[25] = ZigZag[11]<<2;
+ DeZigZag[26] = ZigZag[18]<<2;
+ DeZigZag[27] = ZigZag[24]<<3;
+ } else {
+ DeZigZag[0] = ZigZag[0];
+ DeZigZag[1] = ZigZag[1]<<2;
+ DeZigZag[2] = ZigZag[5]<<2;
+ DeZigZag[3] = ZigZag[6]<<3;
+
+ DeZigZag[8] = ZigZag[2]<<2;
+ DeZigZag[9] = ZigZag[4]<<2;
+ DeZigZag[10] = ZigZag[7]<<2;
+ DeZigZag[11] = ZigZag[13]<<4;
+
+ DeZigZag[16] = ZigZag[3]<<2;
+ DeZigZag[17] = ZigZag[8]<<2;
+ DeZigZag[18] = ZigZag[12]<<3;
+ DeZigZag[19] = ZigZag[17]<<4;
+
+ DeZigZag[24] = ZigZag[9]<<3;
+ DeZigZag[25] = ZigZag[11]<<4;
+ DeZigZag[26] = ZigZag[18]<<4;
+ DeZigZag[27] = ZigZag[24]<<4;
+ }
+
+ /*****************
+ **** IDCT 1D ****
+ *****************/
+
+#define IDCT_1D(c0, c1, c2, c3, in) \
+ do { \
+ tmp1=((c0)*DeZigZag[in])+((c2)*DeZigZag[(in)+2]); \
+ tmp2=(c1)*DeZigZag[(in)+1]; \
+ tmp3=(c3)*DeZigZag[(in)+3]; \
+ } while (0)
+
+#define COMPOSE_1(out1, out2) \
+ do { \
+ tmp=tmp1+tmp2+tmp3; \
+ temp[out1] = tmp>>15; \
+ tmp=tmp1-tmp2-tmp3; \
+ temp[out2] = tmp>>15; \
+ } while (0)
+
+#define COMPOSE_2(out1, out2) \
+ do { \
+ tmp=tmp1+tmp2-tmp3; \
+ temp[out1] = tmp>>15; \
+ tmp=tmp1-tmp2+tmp3; \
+ temp[out2] = tmp>>15; \
+ } while (0)
+
+ /* j = 0 */
+ IDCT_1D(a, b, c, d, 0); COMPOSE_1( 0, 56);
+ IDCT_1D(a, b, c, d, 8); COMPOSE_1( 1, 57);
+ IDCT_1D(a, b, c, d, 16); COMPOSE_1( 2, 58);
+ IDCT_1D(a, b, c, d, 24); COMPOSE_1( 3, 59);
+
+ /* j = 1 */
+ IDCT_1D(a, d, f, g, 0); COMPOSE_2( 8, 48);
+ IDCT_1D(a, d, f, g, 8); COMPOSE_2( 9, 49);
+ IDCT_1D(a, d, f, g, 16); COMPOSE_2(10, 50);
+ IDCT_1D(a, d, f, g, 24); COMPOSE_2(11, 51);
+
+ /* j = 2 */
+ IDCT_1D(a, e, -f, b, 0); COMPOSE_2(16, 40);
+ IDCT_1D(a, e, -f, b, 8); COMPOSE_2(17, 41);
+ IDCT_1D(a, e, -f, b, 16); COMPOSE_2(18, 42);
+ IDCT_1D(a, e, -f, b, 24); COMPOSE_2(19, 43);
+
+ /* j = 3 */
+ IDCT_1D(a, g, -c, e, 0); COMPOSE_2(24, 32);
+ IDCT_1D(a, g, -c, e, 8); COMPOSE_2(25, 33);
+ IDCT_1D(a, g, -c, e, 16); COMPOSE_2(26, 34);
+ IDCT_1D(a, g, -c, e, 24); COMPOSE_2(27, 35);
+
+#undef IDCT_1D
+#undef COMPOSE_1
+#undef COMPOSE_2
+
+ /*****************
+ **** IDCT 2D ****
+ *****************/
+
+#define IDCT_2D(c0, c1, c2, c3, in) \
+ do { \
+ tmp = temp[in]*(c0) + temp[(in)+1]*(c1) \
+ + temp[(in)+2]*(c2) + temp[(in)+3]*(c3); \
+ } while (0)
+
+#define STORE(i) \
+ do { \
+ tmp = tmp >> 15; \
+ tmp = tmp + 128; \
+ if (tmp > 255) tmp = 255; \
+ if (tmp < 0) tmp = 0; \
+ pOut[i] = (unsigned char) tmp; \
+ } while (0)
+
+#define IDCT_2D_ROW(in) \
+ do { \
+ IDCT_2D(a, b, c, d, in); STORE(0+out_idx); \
+ IDCT_2D(a, d, f, -g, in); STORE(1+out_idx); \
+ IDCT_2D(a, e, -f, -b, in); STORE(2+out_idx); \
+ IDCT_2D(a, g, -c, -e, in); STORE(3+out_idx); \
+ IDCT_2D(a, -g, -c, e, in); STORE(4+out_idx); \
+ IDCT_2D(a, -e, -f, b, in); STORE(5+out_idx); \
+ IDCT_2D(a, -d, f, g, in); STORE(6+out_idx); \
+ IDCT_2D(a, -b, c, -d, in); STORE(7+out_idx); \
+ } while (0)
+
+
+#define IDCT_2D_FAST(c0, c1, c2, c3, in) \
+ do { \
+ tmp1=((c0)*temp[in])+((c2)*temp[(in)+2]); \
+ tmp2=(c1)*temp[(in)+1]; \
+ tmp3=(c3)*temp[(in)+3]; \
+ } while (0)
+
+#define STORE_FAST_1(out1, out2) \
+ do { \
+ tmp=tmp1+tmp2+tmp3; \
+ STORE((out1)+out_idx); \
+ tmp=tmp1-tmp2-tmp3; \
+ STORE((out2)+out_idx); \
+ } while (0)
+
+#define STORE_FAST_2(out1, out2) \
+ do { \
+ tmp=tmp1+tmp2-tmp3; \
+ STORE((out1)+out_idx); \
+ tmp=tmp1-tmp2+tmp3; \
+ STORE((out2)+out_idx); \
+ } while (0)
+
+#define IDCT_2D_FAST_ROW(in) \
+ do { \
+ IDCT_2D_FAST(a, b, c, d, in); STORE_FAST_1(0, 7); \
+ IDCT_2D_FAST(a, d, f, g, in); STORE_FAST_2(1, 6); \
+ IDCT_2D_FAST(a, e, -f, b, in); STORE_FAST_2(2, 5); \
+ IDCT_2D_FAST(a, g, -c, e, in); STORE_FAST_2(3, 4); \
+ } while (0)
+
+ out_idx = out_pos;
+
+ IDCT_2D_ROW(0); out_idx += w;
+ IDCT_2D_ROW(8); out_idx += w;
+ IDCT_2D_ROW(16); out_idx += w;
+ IDCT_2D_ROW(24); out_idx += w;
+ IDCT_2D_ROW(32); out_idx += w;
+ IDCT_2D_ROW(40); out_idx += w;
+ IDCT_2D_FAST_ROW(48); out_idx += w;
+ IDCT_2D_FAST_ROW(56);
+
+ *iIn = in_pos;
+ *iOut = out_pos + 8;
+}
+
+#define DECOMP_Y() DecompressYHI(pIn, pY, &iIn, &iY, w, 1)
+#define DECOMP_U() DecompressYHI(pIn, pU, &iIn, &iU, w/2, 2)
+#define DECOMP_V() DecompressYHI(pIn, pV, &iIn, &iV, w/2, 2)
+
+#if 0
+inline static int
+Decompress400HiNoMMX(unsigned char *pIn,
+ unsigned char *pOut,
+ const int w,
+ const int h,
+ const int inSize)
+{
+ unsigned char *pY = pOut;
+ int x, y, iIn, iY;
+
+ iIn = 0;
+ for (y = 0; y < h; y += 8) {
+ iY = w*y;
+
+ for (x = 0; x < w; x += 8)
+ DECOMP_Y();
+ }
+
+ return 0;
+}
+#endif
+
+inline static int
+Decompress420HiNoMMX(unsigned char *pIn,
+ unsigned char *pOut,
+ const int w,
+ const int h,
+ const int inSize)
+{
+ unsigned char *pY = pOut;
+ unsigned char *pU = pY + w*h;
+ unsigned char *pV = pU + w*h/4;
+ int xY, xUV, iY, iU, iV, iIn, count;
+ const int nBlocks = (w*h) / (32*8);
+
+ iIn = 0;
+ iY = iU = iV = 0;
+ xY = xUV = 0;
+
+ for (count = 0; count < nBlocks; count++) {
+ DECOMP_U();
+ DECOMP_V(); xUV += 16;
+ if (xUV >= w) {
+ iU += (w*7)/2;
+ iV += (w*7)/2;
+ xUV = 0;
+ }
+
+ DECOMP_Y(); xY += 8;
+ DECOMP_Y(); xY += 8;
+ if (xY >= w) {
+ iY += w*7;
+ xY = 0;
+ }
+ DECOMP_Y(); xY += 8;
+ DECOMP_Y(); xY += 8;
+ if (xY >= w) {
+ iY += w*7;
+ xY = 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
+ * image at pOut is specified by w.
+ */
+static inline void
+make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
+{
+ unsigned char *pOut1 = pOut;
+ int x, y;
+
+ for (y = 0; y < 8; y++) {
+ pOut1 = pOut;
+ for (x = 0; x < 8; x++) {
+ *pOut1++ = *pIn++;
+ }
+ pOut += w;
+ }
+}
+
+#if 0
+/*
+ * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 ... 127 248 249 ... 255
+ *
+ */
+static void
+yuv400raw_to_yuv400p(struct ov511_frame *frame,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ int x, y;
+ unsigned char *pIn, *pOut, *pOutLine;
+
+ /* Copy Y */
+ pIn = pIn0;
+ pOutLine = pOut0;
+ for (y = 0; y < frame->rawheight - 1; y += 8) {
+ pOut = pOutLine;
+ for (x = 0; x < frame->rawwidth - 1; x += 8) {
+ make_8x8(pIn, pOut, frame->rawwidth);
+ pIn += 64;
+ pOut += 8;
+ }
+ pOutLine += 8 * frame->rawwidth;
+ }
+}
+#endif
+
+/*
+ * For YUV 4:2:0 images, the data show up in 384 byte segments.
+ * The first 64 bytes of each segment are U, the next 64 are V. The U and
+ * V are arranged as follows:
+ *
+ * 0 1 ... 7
+ * 8 9 ... 15
+ * ...
+ * 56 57 ... 63
+ *
+ * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+ *
+ * The next 256 bytes are full resolution Y data and represent 4 squares
+ * of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255
+ *
+ * Note that the U and V data in one segment represent a 16 x 16 pixel
+ * area, but the Y data represent a 32 x 8 pixel area. If the width is not an
+ * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
+ * next horizontal stripe.
+ *
+ * If dumppix module param is set, _parse_data just dumps the incoming segments,
+ * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
+ * this puts the data on the standard output and can be analyzed with the
+ * parseppm.c utility I wrote. That's a much faster way for figuring out how
+ * these data are scrambled.
+ */
+
+/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
+ *
+ * FIXME: Currently only handles width and height that are multiples of 16
+ */
+static void
+yuv420raw_to_yuv420p(unsigned char *pIn0, unsigned char *pOut0,
+ int width, int height)
+{
+ int k, x, y;
+ unsigned char *pIn, *pOut, *pOutLine;
+ const unsigned int a = width * height;
+ const unsigned int w = width / 2;
+
+ /* Copy U and V */
+ pIn = pIn0;
+ pOutLine = pOut0 + a;
+ for (y = 0; y < height - 1; y += 16) {
+ pOut = pOutLine;
+ for (x = 0; x < width - 1; x += 16) {
+ make_8x8(pIn, pOut, w);
+ make_8x8(pIn + 64, pOut + a/4, w);
+ pIn += 384;
+ pOut += 8;
+ }
+ pOutLine += 8 * w;
+ }
+
+ /* Copy Y */
+ pIn = pIn0 + 128;
+ pOutLine = pOut0;
+ k = 0;
+ for (y = 0; y < height - 1; y += 8) {
+ pOut = pOutLine;
+ for (x = 0; x < width - 1; x += 8) {
+ make_8x8(pIn, pOut, width);
+ pIn += 64;
+ pOut += 8;
+ if ((++k) > 3) {
+ k = 0;
+ pIn += 128;
+ }
+ }
+ pOutLine += 8 * width;
+ }
+}
+
+
+/* Remove all 0 blocks from input */
+static void remove0blocks(unsigned char *pIn, int *inSize)
+{
+ long long *in = (long long *)pIn;
+ long long *out = (long long *)pIn;
+ int i, j;
+
+ for (i = 0; i < *inSize; i += 32, in += 4) {
+ int all_zero = 1;
+ for (j = 0; j < 4; j++)
+ if (in[j]) {
+ all_zero = 0;
+ break;
+ }
+
+ /* Skip 32 byte blocks of all 0 */
+ if (all_zero)
+ continue;
+
+ for (j = 0; j < 4; j++)
+ *out++ = in[j];
+ }
+
+ *inSize -= (in - out) * 8;
+}
+
+static int v4lconvert_ov511_to_yuv420(unsigned char *src, unsigned char *dest,
+ int w, int h, int yvu, int src_size)
+{
+ int rc = 0;
+
+ src_size -= 11; /* Remove footer */
+
+ remove0blocks(src, &src_size);
+
+ /* Compressed ? */
+ if (src[8] & 0x40) {
+ rc = Decompress420HiNoMMX(src + 9, dest, w, h, src_size);
+ } else {
+ yuv420raw_to_yuv420p(src + 9, dest, w, h);
+ }
+
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ int width, height, yvu, src_size, dest_size;
+ unsigned char src_buf[500000];
+ unsigned char dest_buf[500000];
+
+ while (1) {
+ if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (src_size > sizeof(src_buf)) {
+ fprintf(stderr, "%s: error: src_buf too small, need: %d\n",
+ argv[0], src_size);
+ return 2;
+ }
+
+ if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+
+ dest_size = width * height * 3 / 2;
+ if (dest_size > sizeof(dest_buf)) {
+ fprintf(stderr, "%s: error: dest_buf too small, need: %d\n",
+ argv[0], dest_size);
+ dest_size = -1;
+ } else if (v4lconvert_ov511_to_yuv420(src_buf, dest_buf, width, height,
+ yvu, src_size))
+ dest_size = -1;
+
+ if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int),
+ argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (dest_size == -1)
+ continue;
+
+ if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c b/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c
new file mode 100644
index 000000000..51b8d8c60
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c
@@ -0,0 +1,1477 @@
+/* We would like to embed this inside libv4l, but we cannot as I've failed
+ to contact Mark W. McClelland to get permission to relicense this,
+ so this lives in an external (GPL licensed) helper */
+
+/* OV518 Decompression Support Module (No-MMX version)
+ *
+ * Copyright (c) 2002-2003 Mark W. McClelland. All rights reserved.
+ * http://alpha.dyndns.org/ov511/
+ *
+ * Fast integer iDCT by Yuri van Oers <yvanoers AT xs4all.nl>
+ * Original OV511 decompression code Copyright 1998-2000 OmniVision Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include "helper-funcs.h"
+
+/******************************************************************************
+ * Compile-time Options
+ ******************************************************************************/
+
+/* Defining APPROXIMATE_MUL_BY_SHIFT increases performance by approximation
+ * the multiplications by shifts. I think there's no change in the
+ * calculated picture, but I'm not sure, so the choice is still in here. */
+#undef APPROXIMATE_MUL_BY_SHIFT
+
+/******************************************************************************
+ * Local Data Types
+ ******************************************************************************/
+
+/* Make sure this remains naturally aligned and 2^n bytes in size */
+struct tree_node {
+ short left; /* Pointer to left child node */
+ short right; /* Pointer to right child node */
+ signed char depth; /* Depth (starting at 1) if leaf, else -1 */
+ signed char coeffbits; /* Size of coefficient data, or zero if none */
+ signed char skip; /* Number of zero coefficients. Unused w/ DC */
+ char padding; /* Pad out to 8 bytes */
+};
+
+struct comp_info {
+ int bytes; /* Number of processed input bytes */
+ int bits; /* Number of unprocessed input bits */
+ int rawLen; /* Total number of bytes in input buffer */
+ unsigned char *qt; /* Current quantization table */
+};
+
+/******************************************************************************
+ * Constant Data Definitions
+ ******************************************************************************/
+
+/* Zig-Zag Table */
+static const unsigned char ZigZag518[] = {
+ 0x00, 0x02, 0x03, 0x09,
+ 0x01, 0x04, 0x08, 0x0a,
+ 0x05, 0x07, 0x0b, 0x11,
+ 0x06, 0x0c, 0x10, 0x12,
+ 0x0d, 0x0f, 0x13, 0x19,
+ 0x0e, 0x14, 0x18, 0x1a,
+ 0x15, 0x17, 0x1b, 0x1e,
+ 0x16, 0x1c, 0x1d, 0x1f
+};
+
+/* Huffman trees */
+
+static const struct tree_node treeYAC[] = {
+ { 1, 4, -1, 0, -1}, { 2, 3, -1, 0, -1},
+ { -1, -1, 2, 1, 0}, { -1, -1, 2, 2, 0},
+ { 5, 9, -1, 0, -1}, { 6, 7, -1, 0, -1},
+ { -1, -1, 3, 3, 0}, {323, 8, -1, 0, -1},
+ { -1, -1, 4, 4, 0}, { 10, 13, -1, 0, -1},
+ { 38, 11, -1, 0, -1}, { 12, 39, -1, 0, -1},
+ { -1, -1, 5, 5, 0}, { 59, 14, -1, 0, -1},
+ { 15, 18, -1, 0, -1}, { 16, 113, -1, 0, -1},
+ { 17, 40, -1, 0, -1}, { -1, -1, 7, 6, 0},
+ { 19, 22, -1, 0, -1}, { 20, 41, -1, 0, -1},
+ { 21, 61, -1, 0, -1}, { -1, -1, 8, 7, 0},
+ { 23, 27, -1, 0, -1}, {169, 24, -1, 0, -1},
+ {208, 25, -1, 0, -1}, { 26, 62, -1, 0, -1},
+ { -1, -1, 10, 8, 0}, { 44, 28, -1, 0, -1},
+ { 63, 29, -1, 0, -1}, { 30, 191, -1, 0, -1},
+ { 31, 119, -1, 0, -1}, { 32, 82, -1, 0, -1},
+ { 33, 55, -1, 0, -1}, { 34, 48, -1, 0, -1},
+ {171, 35, -1, 0, -1}, { 36, 37, -1, 0, -1},
+ { -1, -1, 16, 9, 0}, { -1, -1, 16, 10, 0},
+ { -1, -1, 4, 1, 1}, { -1, -1, 5, 2, 1},
+ { -1, -1, 7, 3, 1}, {151, 42, -1, 0, -1},
+ { 43, 79, -1, 0, -1}, { -1, -1, 9, 4, 1},
+ { 96, 45, -1, 0, -1}, {246, 46, -1, 0, -1},
+ { 47, 115, -1, 0, -1}, { -1, -1, 11, 5, 1},
+ { 49, 52, -1, 0, -1}, { 50, 51, -1, 0, -1},
+ { -1, -1, 16, 6, 1}, { -1, -1, 16, 7, 1},
+ { 53, 54, -1, 0, -1}, { -1, -1, 16, 8, 1},
+ { -1, -1, 16, 9, 1}, { 56, 71, -1, 0, -1},
+ { 57, 68, -1, 0, -1}, { 58, 67, -1, 0, -1},
+ { -1, -1, 16, 10, 1}, { 60, 77, -1, 0, -1},
+ { -1, -1, 5, 1, 2}, { -1, -1, 8, 2, 2},
+ { -1, -1, 10, 3, 2}, {265, 64, -1, 0, -1},
+ { 65, 134, -1, 0, -1}, { 66, 80, -1, 0, -1},
+ { -1, -1, 12, 4, 2}, { -1, -1, 16, 5, 2},
+ { 69, 70, -1, 0, -1}, { -1, -1, 16, 6, 2},
+ { -1, -1, 16, 7, 2}, { 72, 75, -1, 0, -1},
+ { 73, 74, -1, 0, -1}, { -1, -1, 16, 8, 2},
+ { -1, -1, 16, 9, 2}, { 76, 81, -1, 0, -1},
+ { -1, -1, 16, 10, 2}, { 78, 95, -1, 0, -1},
+ { -1, -1, 6, 1, 3}, { -1, -1, 9, 2, 3},
+ { -1, -1, 12, 3, 3}, { -1, -1, 16, 4, 3},
+ { 83, 101, -1, 0, -1}, { 84, 91, -1, 0, -1},
+ { 85, 88, -1, 0, -1}, { 86, 87, -1, 0, -1},
+ { -1, -1, 16, 5, 3}, { -1, -1, 16, 6, 3},
+ { 89, 90, -1, 0, -1}, { -1, -1, 16, 7, 3},
+ { -1, -1, 16, 8, 3}, { 92, 98, -1, 0, -1},
+ { 93, 94, -1, 0, -1}, { -1, -1, 16, 9, 3},
+ { -1, -1, 16, 10, 3}, { -1, -1, 6, 1, 4},
+ { 97, 225, -1, 0, -1}, { -1, -1, 10, 2, 4},
+ { 99, 100, -1, 0, -1}, { -1, -1, 16, 3, 4},
+ { -1, -1, 16, 4, 4}, {102, 109, -1, 0, -1},
+ {103, 106, -1, 0, -1}, {104, 105, -1, 0, -1},
+ { -1, -1, 16, 5, 4}, { -1, -1, 16, 6, 4},
+ {107, 108, -1, 0, -1}, { -1, -1, 16, 7, 4},
+ { -1, -1, 16, 8, 4}, {110, 116, -1, 0, -1},
+ {111, 112, -1, 0, -1}, { -1, -1, 16, 9, 4},
+ { -1, -1, 16, 10, 4}, {114, 133, -1, 0, -1},
+ { -1, -1, 7, 1, 5}, { -1, -1, 11, 2, 5},
+ {117, 118, -1, 0, -1}, { -1, -1, 16, 3, 5},
+ { -1, -1, 16, 4, 5}, {120, 156, -1, 0, -1},
+ {121, 139, -1, 0, -1}, {122, 129, -1, 0, -1},
+ {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1},
+ { -1, -1, 16, 5, 5}, { -1, -1, 16, 6, 5},
+ {127, 128, -1, 0, -1}, { -1, -1, 16, 7, 5},
+ { -1, -1, 16, 8, 5}, {130, 136, -1, 0, -1},
+ {131, 132, -1, 0, -1}, { -1, -1, 16, 9, 5},
+ { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6},
+ {135, 152, -1, 0, -1}, { -1, -1, 12, 2, 6},
+ {137, 138, -1, 0, -1}, { -1, -1, 16, 3, 6},
+ { -1, -1, 16, 4, 6}, {140, 147, -1, 0, -1},
+ {141, 144, -1, 0, -1}, {142, 143, -1, 0, -1},
+ { -1, -1, 16, 5, 6}, { -1, -1, 16, 6, 6},
+ {145, 146, -1, 0, -1}, { -1, -1, 16, 7, 6},
+ { -1, -1, 16, 8, 6}, {148, 153, -1, 0, -1},
+ {149, 150, -1, 0, -1}, { -1, -1, 16, 9, 6},
+ { -1, -1, 16, 10, 6}, { -1, -1, 8, 1, 7},
+ { -1, -1, 12, 2, 7}, {154, 155, -1, 0, -1},
+ { -1, -1, 16, 3, 7}, { -1, -1, 16, 4, 7},
+ {157, 175, -1, 0, -1}, {158, 165, -1, 0, -1},
+ {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1},
+ { -1, -1, 16, 5, 7}, { -1, -1, 16, 6, 7},
+ {163, 164, -1, 0, -1}, { -1, -1, 16, 7, 7},
+ { -1, -1, 16, 8, 7}, {166, 172, -1, 0, -1},
+ {167, 168, -1, 0, -1}, { -1, -1, 16, 9, 7},
+ { -1, -1, 16, 10, 7}, {170, 187, -1, 0, -1},
+ { -1, -1, 9, 1, 8}, { -1, -1, 15, 2, 8},
+ {173, 174, -1, 0, -1}, { -1, -1, 16, 3, 8},
+ { -1, -1, 16, 4, 8}, {176, 183, -1, 0, -1},
+ {177, 180, -1, 0, -1}, {178, 179, -1, 0, -1},
+ { -1, -1, 16, 5, 8}, { -1, -1, 16, 6, 8},
+ {181, 182, -1, 0, -1}, { -1, -1, 16, 7, 8},
+ { -1, -1, 16, 8, 8}, {184, 188, -1, 0, -1},
+ {185, 186, -1, 0, -1}, { -1, -1, 16, 9, 8},
+ { -1, -1, 16, 10, 8}, { -1, -1, 9, 1, 9},
+ {189, 190, -1, 0, -1}, { -1, -1, 16, 2, 9},
+ { -1, -1, 16, 3, 9}, {192, 258, -1, 0, -1},
+ {193, 226, -1, 0, -1}, {194, 210, -1, 0, -1},
+ {195, 202, -1, 0, -1}, {196, 199, -1, 0, -1},
+ {197, 198, -1, 0, -1}, { -1, -1, 16, 4, 9},
+ { -1, -1, 16, 5, 9}, {200, 201, -1, 0, -1},
+ { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9},
+ {203, 206, -1, 0, -1}, {204, 205, -1, 0, -1},
+ { -1, -1, 16, 8, 9}, { -1, -1, 16, 9, 9},
+ {207, 209, -1, 0, -1}, { -1, -1, 16, 10, 9},
+ { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10},
+ {211, 218, -1, 0, -1}, {212, 215, -1, 0, -1},
+ {213, 214, -1, 0, -1}, { -1, -1, 16, 3, 10},
+ { -1, -1, 16, 4, 10}, {216, 217, -1, 0, -1},
+ { -1, -1, 16, 5, 10}, { -1, -1, 16, 6, 10},
+ {219, 222, -1, 0, -1}, {220, 221, -1, 0, -1},
+ { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10},
+ {223, 224, -1, 0, -1}, { -1, -1, 16, 9, 10},
+ { -1, -1, 16, 10, 10}, { -1, -1, 10, 1, 11},
+ {227, 242, -1, 0, -1}, {228, 235, -1, 0, -1},
+ {229, 232, -1, 0, -1}, {230, 231, -1, 0, -1},
+ { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11},
+ {233, 234, -1, 0, -1}, { -1, -1, 16, 4, 11},
+ { -1, -1, 16, 5, 11}, {236, 239, -1, 0, -1},
+ {237, 238, -1, 0, -1}, { -1, -1, 16, 6, 11},
+ { -1, -1, 16, 7, 11}, {240, 241, -1, 0, -1},
+ { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11},
+ {243, 251, -1, 0, -1}, {244, 248, -1, 0, -1},
+ {245, 247, -1, 0, -1}, { -1, -1, 16, 10, 11},
+ { -1, -1, 10, 1, 12}, { -1, -1, 16, 2, 12},
+ {249, 250, -1, 0, -1}, { -1, -1, 16, 3, 12},
+ { -1, -1, 16, 4, 12}, {252, 255, -1, 0, -1},
+ {253, 254, -1, 0, -1}, { -1, -1, 16, 5, 12},
+ { -1, -1, 16, 6, 12}, {256, 257, -1, 0, -1},
+ { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12},
+ {259, 292, -1, 0, -1}, {260, 277, -1, 0, -1},
+ {261, 270, -1, 0, -1}, {262, 267, -1, 0, -1},
+ {263, 264, -1, 0, -1}, { -1, -1, 16, 9, 12},
+ { -1, -1, 16, 10, 12}, {266, 322, -1, 0, -1},
+ { -1, -1, 11, 1, 13}, {268, 269, -1, 0, -1},
+ { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13},
+ {271, 274, -1, 0, -1}, {272, 273, -1, 0, -1},
+ { -1, -1, 16, 4, 13}, { -1, -1, 16, 5, 13},
+ {275, 276, -1, 0, -1}, { -1, -1, 16, 6, 13},
+ { -1, -1, 16, 7, 13}, {278, 285, -1, 0, -1},
+ {279, 282, -1, 0, -1}, {280, 281, -1, 0, -1},
+ { -1, -1, 16, 8, 13}, { -1, -1, 16, 9, 13},
+ {283, 284, -1, 0, -1}, { -1, -1, 16, 10, 13},
+ { -1, -1, 16, 1, 14}, {286, 289, -1, 0, -1},
+ {287, 288, -1, 0, -1}, { -1, -1, 16, 2, 14},
+ { -1, -1, 16, 3, 14}, {290, 291, -1, 0, -1},
+ { -1, -1, 16, 4, 14}, { -1, -1, 16, 5, 14},
+ {293, 308, -1, 0, -1}, {294, 301, -1, 0, -1},
+ {295, 298, -1, 0, -1}, {296, 297, -1, 0, -1},
+ { -1, -1, 16, 6, 14}, { -1, -1, 16, 7, 14},
+ {299, 300, -1, 0, -1}, { -1, -1, 16, 8, 14},
+ { -1, -1, 16, 9, 14}, {302, 305, -1, 0, -1},
+ {303, 304, -1, 0, -1}, { -1, -1, 16, 10, 14},
+ { -1, -1, 16, 1, 15}, {306, 307, -1, 0, -1},
+ { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15},
+ {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1},
+ {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15},
+ { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1},
+ { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15},
+ {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1},
+ { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15},
+ {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15},
+ { -1, -1, 11, 0, 16}, { -1, -1, 4, 0, -1},
+};
+
+static const struct tree_node treeUVAC[] = {
+ { 1, 3, -1, 0, -1}, {323, 2, -1, 0, -1},
+ { -1, -1, 2, 1, 0}, { 4, 8, -1, 0, -1},
+ { 5, 6, -1, 0, -1}, { -1, -1, 3, 2, 0},
+ { 7, 37, -1, 0, -1}, { -1, -1, 4, 3, 0},
+ { 9, 13, -1, 0, -1}, { 10, 60, -1, 0, -1},
+ { 11, 12, -1, 0, -1}, { -1, -1, 5, 4, 0},
+ { -1, -1, 5, 5, 0}, { 14, 17, -1, 0, -1},
+ { 15, 97, -1, 0, -1}, { 16, 38, -1, 0, -1},
+ { -1, -1, 6, 6, 0}, { 18, 21, -1, 0, -1},
+ { 19, 39, -1, 0, -1}, { 20, 135, -1, 0, -1},
+ { -1, -1, 7, 7, 0}, { 22, 26, -1, 0, -1},
+ { 82, 23, -1, 0, -1}, { 24, 99, -1, 0, -1},
+ { 25, 42, -1, 0, -1}, { -1, -1, 9, 8, 0},
+ { 27, 31, -1, 0, -1}, {211, 28, -1, 0, -1},
+ {248, 29, -1, 0, -1}, { 30, 63, -1, 0, -1},
+ { -1, -1, 10, 9, 0}, { 43, 32, -1, 0, -1},
+ { 33, 48, -1, 0, -1}, {153, 34, -1, 0, -1},
+ { 35, 64, -1, 0, -1}, { 36, 47, -1, 0, -1},
+ { -1, -1, 12, 10, 0}, { -1, -1, 4, 1, 1},
+ { -1, -1, 6, 2, 1}, {152, 40, -1, 0, -1},
+ { 41, 62, -1, 0, -1}, { -1, -1, 8, 3, 1},
+ { -1, -1, 9, 4, 1}, { 84, 44, -1, 0, -1},
+ {322, 45, -1, 0, -1}, { 46, 136, -1, 0, -1},
+ { -1, -1, 11, 5, 1}, { -1, -1, 12, 6, 1},
+ { 49, 189, -1, 0, -1}, { 50, 119, -1, 0, -1},
+ { 51, 76, -1, 0, -1}, { 66, 52, -1, 0, -1},
+ { 53, 69, -1, 0, -1}, { 54, 57, -1, 0, -1},
+ { 55, 56, -1, 0, -1}, { -1, -1, 16, 7, 1},
+ { -1, -1, 16, 8, 1}, { 58, 59, -1, 0, -1},
+ { -1, -1, 16, 9, 1}, { -1, -1, 16, 10, 1},
+ { 61, 81, -1, 0, -1}, { -1, -1, 5, 1, 2},
+ { -1, -1, 8, 2, 2}, { -1, -1, 10, 3, 2},
+ { 65, 86, -1, 0, -1}, { -1, -1, 12, 4, 2},
+ {286, 67, -1, 0, -1}, { 68, 304, -1, 0, -1},
+ { -1, -1, 15, 5, 2}, { 70, 73, -1, 0, -1},
+ { 71, 72, -1, 0, -1}, { -1, -1, 16, 6, 2},
+ { -1, -1, 16, 7, 2}, { 74, 75, -1, 0, -1},
+ { -1, -1, 16, 8, 2}, { -1, -1, 16, 9, 2},
+ { 77, 102, -1, 0, -1}, { 78, 91, -1, 0, -1},
+ { 79, 88, -1, 0, -1}, { 80, 87, -1, 0, -1},
+ { -1, -1, 16, 10, 2}, { -1, -1, 5, 1, 3},
+ { 83, 171, -1, 0, -1}, { -1, -1, 8, 2, 3},
+ { 85, 117, -1, 0, -1}, { -1, -1, 10, 3, 3},
+ { -1, -1, 12, 4, 3}, { -1, -1, 16, 5, 3},
+ { 89, 90, -1, 0, -1}, { -1, -1, 16, 6, 3},
+ { -1, -1, 16, 7, 3}, { 92, 95, -1, 0, -1},
+ { 93, 94, -1, 0, -1}, { -1, -1, 16, 8, 3},
+ { -1, -1, 16, 9, 3}, { 96, 101, -1, 0, -1},
+ { -1, -1, 16, 10, 3}, { 98, 116, -1, 0, -1},
+ { -1, -1, 6, 1, 4}, {100, 188, -1, 0, -1},
+ { -1, -1, 9, 2, 4}, { -1, -1, 16, 3, 4},
+ {103, 110, -1, 0, -1}, {104, 107, -1, 0, -1},
+ {105, 106, -1, 0, -1}, { -1, -1, 16, 4, 4},
+ { -1, -1, 16, 5, 4}, {108, 109, -1, 0, -1},
+ { -1, -1, 16, 6, 4}, { -1, -1, 16, 7, 4},
+ {111, 114, -1, 0, -1}, {112, 113, -1, 0, -1},
+ { -1, -1, 16, 8, 4}, { -1, -1, 16, 9, 4},
+ {115, 118, -1, 0, -1}, { -1, -1, 16, 10, 4},
+ { -1, -1, 6, 1, 5}, { -1, -1, 10, 2, 5},
+ { -1, -1, 16, 3, 5}, {120, 156, -1, 0, -1},
+ {121, 138, -1, 0, -1}, {122, 129, -1, 0, -1},
+ {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1},
+ { -1, -1, 16, 4, 5}, { -1, -1, 16, 5, 5},
+ {127, 128, -1, 0, -1}, { -1, -1, 16, 6, 5},
+ { -1, -1, 16, 7, 5}, {130, 133, -1, 0, -1},
+ {131, 132, -1, 0, -1}, { -1, -1, 16, 8, 5},
+ { -1, -1, 16, 9, 5}, {134, 137, -1, 0, -1},
+ { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6},
+ { -1, -1, 11, 2, 6}, { -1, -1, 16, 3, 6},
+ {139, 146, -1, 0, -1}, {140, 143, -1, 0, -1},
+ {141, 142, -1, 0, -1}, { -1, -1, 16, 4, 6},
+ { -1, -1, 16, 5, 6}, {144, 145, -1, 0, -1},
+ { -1, -1, 16, 6, 6}, { -1, -1, 16, 7, 6},
+ {147, 150, -1, 0, -1}, {148, 149, -1, 0, -1},
+ { -1, -1, 16, 8, 6}, { -1, -1, 16, 9, 6},
+ {151, 155, -1, 0, -1}, { -1, -1, 16, 10, 6},
+ { -1, -1, 7, 1, 7}, {154, 267, -1, 0, -1},
+ { -1, -1, 11, 2, 7}, { -1, -1, 16, 3, 7},
+ {157, 173, -1, 0, -1}, {158, 165, -1, 0, -1},
+ {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1},
+ { -1, -1, 16, 4, 7}, { -1, -1, 16, 5, 7},
+ {163, 164, -1, 0, -1}, { -1, -1, 16, 6, 7},
+ { -1, -1, 16, 7, 7}, {166, 169, -1, 0, -1},
+ {167, 168, -1, 0, -1}, { -1, -1, 16, 8, 7},
+ { -1, -1, 16, 9, 7}, {170, 172, -1, 0, -1},
+ { -1, -1, 16, 10, 7}, { -1, -1, 8, 1, 8},
+ { -1, -1, 16, 2, 8}, {174, 181, -1, 0, -1},
+ {175, 178, -1, 0, -1}, {176, 177, -1, 0, -1},
+ { -1, -1, 16, 3, 8}, { -1, -1, 16, 4, 8},
+ {179, 180, -1, 0, -1}, { -1, -1, 16, 5, 8},
+ { -1, -1, 16, 6, 8}, {182, 185, -1, 0, -1},
+ {183, 184, -1, 0, -1}, { -1, -1, 16, 7, 8},
+ { -1, -1, 16, 8, 8}, {186, 187, -1, 0, -1},
+ { -1, -1, 16, 9, 8}, { -1, -1, 16, 10, 8},
+ { -1, -1, 9, 1, 9}, {190, 257, -1, 0, -1},
+ {191, 224, -1, 0, -1}, {192, 207, -1, 0, -1},
+ {193, 200, -1, 0, -1}, {194, 197, -1, 0, -1},
+ {195, 196, -1, 0, -1}, { -1, -1, 16, 2, 9},
+ { -1, -1, 16, 3, 9}, {198, 199, -1, 0, -1},
+ { -1, -1, 16, 4, 9}, { -1, -1, 16, 5, 9},
+ {201, 204, -1, 0, -1}, {202, 203, -1, 0, -1},
+ { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9},
+ {205, 206, -1, 0, -1}, { -1, -1, 16, 8, 9},
+ { -1, -1, 16, 9, 9}, {208, 217, -1, 0, -1},
+ {209, 214, -1, 0, -1}, {210, 213, -1, 0, -1},
+ { -1, -1, 16, 10, 9}, {212, 230, -1, 0, -1},
+ { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10},
+ {215, 216, -1, 0, -1}, { -1, -1, 16, 3, 10},
+ { -1, -1, 16, 4, 10}, {218, 221, -1, 0, -1},
+ {219, 220, -1, 0, -1}, { -1, -1, 16, 5, 10},
+ { -1, -1, 16, 6, 10}, {222, 223, -1, 0, -1},
+ { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10},
+ {225, 241, -1, 0, -1}, {226, 234, -1, 0, -1},
+ {227, 231, -1, 0, -1}, {228, 229, -1, 0, -1},
+ { -1, -1, 16, 9, 10}, { -1, -1, 16, 10, 10},
+ { -1, -1, 9, 1, 11}, {232, 233, -1, 0, -1},
+ { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11},
+ {235, 238, -1, 0, -1}, {236, 237, -1, 0, -1},
+ { -1, -1, 16, 4, 11}, { -1, -1, 16, 5, 11},
+ {239, 240, -1, 0, -1}, { -1, -1, 16, 6, 11},
+ { -1, -1, 16, 7, 11}, {242, 250, -1, 0, -1},
+ {243, 246, -1, 0, -1}, {244, 245, -1, 0, -1},
+ { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11},
+ {247, 249, -1, 0, -1}, { -1, -1, 16, 10, 11},
+ { -1, -1, 9, 1, 12}, { -1, -1, 16, 2, 12},
+ {251, 254, -1, 0, -1}, {252, 253, -1, 0, -1},
+ { -1, -1, 16, 3, 12}, { -1, -1, 16, 4, 12},
+ {255, 256, -1, 0, -1}, { -1, -1, 16, 5, 12},
+ { -1, -1, 16, 6, 12}, {258, 291, -1, 0, -1},
+ {259, 275, -1, 0, -1}, {260, 268, -1, 0, -1},
+ {261, 264, -1, 0, -1}, {262, 263, -1, 0, -1},
+ { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12},
+ {265, 266, -1, 0, -1}, { -1, -1, 16, 9, 12},
+ { -1, -1, 16, 10, 12}, { -1, -1, 11, 1, 13},
+ {269, 272, -1, 0, -1}, {270, 271, -1, 0, -1},
+ { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13},
+ {273, 274, -1, 0, -1}, { -1, -1, 16, 4, 13},
+ { -1, -1, 16, 5, 13}, {276, 283, -1, 0, -1},
+ {277, 280, -1, 0, -1}, {278, 279, -1, 0, -1},
+ { -1, -1, 16, 6, 13}, { -1, -1, 16, 7, 13},
+ {281, 282, -1, 0, -1}, { -1, -1, 16, 8, 13},
+ { -1, -1, 16, 9, 13}, {284, 288, -1, 0, -1},
+ {285, 287, -1, 0, -1}, { -1, -1, 16, 10, 13},
+ { -1, -1, 14, 1, 14}, { -1, -1, 16, 2, 14},
+ {289, 290, -1, 0, -1}, { -1, -1, 16, 3, 14},
+ { -1, -1, 16, 4, 14}, {292, 308, -1, 0, -1},
+ {293, 300, -1, 0, -1}, {294, 297, -1, 0, -1},
+ {295, 296, -1, 0, -1}, { -1, -1, 16, 5, 14},
+ { -1, -1, 16, 6, 14}, {298, 299, -1, 0, -1},
+ { -1, -1, 16, 7, 14}, { -1, -1, 16, 8, 14},
+ {301, 305, -1, 0, -1}, {302, 303, -1, 0, -1},
+ { -1, -1, 16, 9, 14}, { -1, -1, 16, 10, 14},
+ { -1, -1, 15, 1, 15}, {306, 307, -1, 0, -1},
+ { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15},
+ {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1},
+ {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15},
+ { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1},
+ { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15},
+ {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1},
+ { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15},
+ {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15},
+ { -1, -1, 10, 0, 16}, { -1, -1, 2, 0, -1},
+};
+
+static const struct tree_node treeYDC[] = {
+ { 1, 6, -1, 0}, { 2, 3, -1, 0},
+ { -1, -1, 2, 0}, { 4, 5, -1, 0},
+ { -1, -1, 3, 1}, { -1, -1, 3, 2},
+ { 7, 10, -1, 0}, { 8, 9, -1, 0},
+ { -1, -1, 3, 3}, { -1, -1, 3, 4},
+ { 11, 12, -1, 0}, { -1, -1, 3, 5},
+ { 13, 14, -1, 0}, { -1, -1, 4, 6},
+ { 15, 16, -1, 0}, { -1, -1, 5, 7},
+ { 17, 18, -1, 0}, { -1, -1, 6, 8},
+ { 19, 20, -1, 0}, { -1, -1, 7, 9},
+ { 21, 22, -1, 0}, { -1, -1, 8, 10},
+ { 23, -1, -1, 0}, { -1, -1, 9, 11},
+};
+
+static const struct tree_node treeUVDC[] = {
+ { 1, 4, -1, 0}, { 2, 3, -1, 0},
+ { -1, -1, 2, 0}, { -1, -1, 2, 1},
+ { 5, 6, -1, 0}, { -1, -1, 2, 2},
+ { 7, 8, -1, 0}, { -1, -1, 3, 3},
+ { 9, 10, -1, 0}, { -1, -1, 4, 4},
+ { 11, 12, -1, 0}, { -1, -1, 5, 5},
+ { 13, 14, -1, 0}, { -1, -1, 6, 6},
+ { 15, 16, -1, 0}, { -1, -1, 7, 7},
+ { 17, 18, -1, 0}, { -1, -1, 8, 8},
+ { 19, 20, -1, 0}, { -1, -1, 9, 9},
+ { 21, 22, -1, 0}, { -1, -1, 10, 10},
+ { 23, -1, -1, 0}, { -1, -1, 11, 11},
+};
+
+/******************************************************************************
+ * Huffman Decoder
+ ******************************************************************************/
+
+/* Note: There is no penalty for passing the tree as an argument, since dummy
+ * args are passed anyway (to maintain 16-byte stack alignment), and since the
+ * address is loaded into a register either way. */
+
+/* If no node is found, coeffbits and skip will not be modified */
+/* Return: Depth of node found, or -1 if invalid input code */
+static int
+getNodeAC(unsigned int in, signed char *coeffbits, signed char *skip,
+ const struct tree_node *tree)
+{
+ int node = 0;
+ int i = 0;
+ int depth;
+
+ do {
+ if ((in & 0x80000000) == 0)
+ node = tree[node].left;
+ else
+ node = tree[node].right;
+
+ if (node == -1)
+ break;
+
+ depth = tree[node].depth;
+
+ /* Is it a leaf? If not, branch downward */
+ if (depth != -1) {
+ *coeffbits = tree[node].coeffbits;
+ *skip = tree[node].skip;
+ return depth;
+ }
+
+ in <<= 1;
+ ++i;
+ } while (i <= 15);
+
+ return -1;
+}
+
+/* If no node is found, coeffbits will not be modified */
+/* Return: Depth of node found, or -1 if invalid input code */
+static int
+getNodeDC(unsigned int in, signed char *coeffbits, const struct tree_node *tree)
+{
+ int node = 0;
+ int i = 0;
+ int depth;
+
+ do {
+ if ((in & 0x80000000) == 0)
+ node = tree[node].left;
+ else
+ node = tree[node].right;
+
+ if (node == -1)
+ break;
+
+ depth = tree[node].depth;
+
+ /* Is it a leaf? If not, branch downward */
+ if (depth != -1) {
+ *coeffbits = tree[node].coeffbits;
+ return depth;
+ }
+
+ in <<= 1;
+ ++i;
+ } while (i <= 15);
+
+ return -1;
+}
+
+static inline unsigned int
+getBytes(int *rawData, struct comp_info *cinfo)
+{
+ int bufLen = cinfo->rawLen;
+ int bits = cinfo->bits;
+ int bytes = cinfo->bytes;
+ unsigned char *in = bytes + (unsigned char *) rawData;
+ unsigned char b1, b2, b3, b4, b5;
+ unsigned int packedIn;
+
+ /* Pull 5 bytes out of raw data */
+ if (bytes < bufLen - 4) {
+ b1 = in[0];
+ b2 = in[1];
+ b3 = in[2];
+ b4 = in[3];
+ b5 = in[4];
+ } else {
+ if (bytes < bufLen - 3) {
+ b1 = in[0];
+ b2 = in[1];
+ b3 = in[2];
+ b4 = in[3];
+ } else {
+ if (bytes < bufLen - 2) {
+ b1 = in[0];
+ b2 = in[1];
+ b3 = in[2];
+ } else {
+ if (bytes < bufLen - 1) {
+ b1 = in[0];
+ b2 = in[1];
+ } else {
+ if (bytes <= bufLen) {
+ b1 = in[0];
+ } else {
+ b1 = 0;
+ }
+ b2 = 0;
+ }
+ b3 = 0;
+ }
+ b4 = 0;
+ }
+ b5 = 0;
+ }
+
+ /* Pack the bytes */
+ packedIn = b1 << 24;
+ packedIn += b2 << 16;
+ packedIn += b3 << 8;
+ packedIn += b4;
+
+ if (bits != 0) {
+ packedIn = packedIn << bits;
+ packedIn += b5 >> (8 - bits);
+ }
+
+ return packedIn;
+}
+
+static int
+getACCoefficient(int *rawData, int *coeff, struct comp_info *cinfo,
+ const struct tree_node *tree)
+{
+ int input, bits, bytes, tmp_c;
+ signed char coeffbits = 0;
+ signed char skip = 0;
+
+ input = getBytes(rawData, cinfo);
+ bits = getNodeAC(input, &coeffbits, &skip, tree);
+
+ if (coeffbits) {
+ input = input << (bits - 1);
+ input &= 0x7fffffff;
+ if (! (input & 0x40000000))
+ input |= 0x80000000;
+
+ tmp_c = input >> (31 - coeffbits);
+ if (tmp_c < 0)
+ tmp_c++;
+ *coeff = tmp_c;
+
+ bits += coeffbits;
+ }
+
+ bytes = (bits + cinfo->bits) >> 3;
+ cinfo->bytes += bytes;
+ cinfo->bits += bits - (bytes << 3);
+
+ return skip;
+}
+
+static void
+getDCCoefficient(int *rawData, int *coeff, struct comp_info *cinfo,
+ const struct tree_node *tree)
+{
+ int input, bits, bytes, tmp_c;
+ signed char coeffbits = 0;
+
+ input = getBytes(rawData, cinfo);
+ bits = getNodeDC(input, &coeffbits, tree);
+
+ if (bits == -1) {
+ bits = 1; /* Try to re-sync at the next bit */
+ *coeff = 0; /* Indicates no change from last DC */
+ } else {
+
+ input = input << (bits - 1);
+ input &= 0x7fffffff;
+ if (! (input & 0x40000000))
+ input |= 0x80000000;
+
+ tmp_c = input >> (31 - coeffbits);
+ if (tmp_c < 0)
+ tmp_c++;
+ *coeff = tmp_c;
+
+ bits += coeffbits;
+ }
+
+ bytes = (bits + cinfo->bits) >> 3;
+ cinfo->bytes += bytes;
+ cinfo->bits += bits - (bytes << 3);
+}
+
+/* For AC coefficients, here is what the "skip" value means:
+ * -1: Either the 8x4 block has ended, or the decoding failed.
+ * 0: Use the returned coeff. Don't skip anything.
+ * 1-15: The next <skip> coeffs are zero. The returned coeff is used.
+ * 16: The next 16 coeffs are zero. The returned coeff is ignored.
+ *
+ * You must ensure that the C[] array not be overrun, or stack corruption will
+ * result.
+ */
+static void
+huffmanDecoderY(int *C, int *pIn, struct comp_info *cinfo)
+{
+ int coeff = 0;
+ int i = 1;
+ int k, skip;
+
+ getDCCoefficient(pIn, C, cinfo, treeYDC);
+
+ i = 1;
+ do {
+ skip = getACCoefficient(pIn, &coeff, cinfo, treeYAC);
+
+ if (skip == -1) {
+ break;
+ } else if (skip == 0) {
+ C[i++] = coeff;
+ } else if (skip == 16) {
+ k = 16;
+ if (i > 16)
+ k = 32 - i;
+
+ while (k--)
+ C[i++] = 0;
+ } else {
+ k = skip;
+ if (skip > 31 - i)
+ k = 31 - i;
+
+ while (k--)
+ C[i++] = 0;
+
+ C[i++] = coeff;
+ }
+ } while (i <= 31);
+
+ if (skip == -1)
+ while (i <= 31) C[i++] = 0;
+ else
+ getACCoefficient(pIn, &coeff, cinfo, treeYAC);
+}
+
+/* Same as huffmanDecoderY, except for the tables used */
+static void
+huffmanDecoderUV(int *C, int *pIn, struct comp_info *cinfo)
+{
+ int coeff = 0;
+ int i = 1;
+ int k, skip;
+
+ getDCCoefficient(pIn, C, cinfo, treeUVDC);
+
+ i = 1;
+ do {
+ skip = getACCoefficient(pIn, &coeff, cinfo, treeUVAC);
+
+ if (skip == -1) {
+ break;
+ } else if (skip == 0) {
+ C[i++] = coeff;
+ } else if (skip == 16) {
+ k = 16;
+ if (i > 16)
+ k = 32 - i;
+
+ while (k--)
+ C[i++] = 0;
+ } else {
+ k = skip;
+ if (skip > 31 - i)
+ k = 31 - i;
+
+ while (k--)
+ C[i++] = 0;
+
+ C[i++] = coeff;
+ }
+ } while (i <= 31);
+
+ if (skip == -1)
+ while (i <= 31) C[i++] = 0;
+ else
+ getACCoefficient(pIn, &coeff, cinfo, treeUVAC);
+}
+
+/******************************************************************************
+ * iDCT Functions
+ ******************************************************************************/
+
+#ifndef APPROXIMATE_MUL_BY_SHIFT
+
+#define IDCT_MESSAGE "iDCT with multiply"
+
+#define TIMES_16382(u) ((u)? 16382 * (u):0)
+#define TIMES_23168(u) ((u)? 23168 * (u):0)
+#define TIMES_30270(u) ((u)? 30270 * (u):0)
+#define TIMES_41986(u) ((u)? 41986 * (u):0)
+#define TIMES_35594(u) ((u)? 35594 * (u):0)
+#define TIMES_23783(u) ((u)? 23783 * (u):0)
+#define TIMES_8351(u) ((u)? 8351 * (u):0)
+#define TIMES_17391(u) ((u)? 17391 * (u):0)
+#define TIMES_14743(u) ((u)? 14743 * (u):0)
+#define TIMES_9851(u) ((u)? 9851 * (u):0)
+#define TIMES_3459(u) ((u)? 3459 * (u):0)
+#define TIMES_32134(u) ((u)? 32134 * (u):0)
+#define TIMES_27242(u) ((u)? 27242 * (u):0)
+#define TIMES_18202(u) ((u)? 18202 * (u):0)
+#define TIMES_6392(u) ((u)? 6392 * (u):0)
+#define TIMES_39550(u) ((u)? 39550 * (u):0)
+#define TIMES_6785(u) ((u)? 6785 * (u):0)
+#define TIMES_12538(u) ((u)? 12538 * (u):0)
+
+#else
+
+#define IDCT_MESSAGE "iDCT with shift"
+
+#define TIMES_16382(u) ( (u)? x=(u) , (x<<14) - (x<<1) :0 )
+#define TIMES_23168(u) ( (u)? x=(u) , (x<<14) + (x<<12) + (x<<11) + (x<<9) :0 )
+#define TIMES_30270(u) ( (u)? x=(u) , (x<<15) - (x<<11) :0 )
+#define TIMES_41986(u) ( (u)? x=(u) , (x<<15) + (x<<13) + (x<<10) :0 )
+#define TIMES_35594(u) ( (u)? x=(u) , (x<<15) + (x<<11) + (x<<9) + (x<<8) :0 )
+#define TIMES_23783(u) ( (u)? x=(u) , (x<<14) + (x<<13) - (x<<9) - (x<<8) :0 )
+#define TIMES_8351(u) ( (u)? x=(u) , (x<<13) :0 )
+#define TIMES_17391(u) ( (u)? x=(u) , (x<<14) + (x<<10) :0 )
+#define TIMES_14743(u) ( (u)? x=(u) , (x<<14) - (x<<10) - (x<<9) :0 )
+#define TIMES_9851(u) ( (u)? x=(u) , (x<<13) + (x<<10) + (x<<9) :0 )
+#define TIMES_3459(u) ( (u)? x=(u) , (x<<12) - (x<<9) :0 )
+#define TIMES_32134(u) ( (u)? x=(u) , (x<<15) - (x<<9) :0 )
+#define TIMES_27242(u) ( (u)? x=(u) , (x<<14) + (x<<13) + (x<<11) + (x<<9) :0 )
+#define TIMES_18202(u) ( (u)? x=(u) , (x<<14) + (x<<11) - (x<<8) :0 )
+#define TIMES_6392(u) ( (u)? x=(u) , (x<<13) - (x<<11) + (x<<8) :0 )
+#define TIMES_39550(u) ( (u)? x=(u) , (x<<15) + (x<<12) + (x<<11) + (x<<9) :0 )
+#define TIMES_6785(u) ( (u)? x=(u) , (x<<12) + (x<<11) + (x<<9) :0 )
+#define TIMES_12538(u) ( (u)? x=(u) , (x<<13) + (x<<12) + (x<<8) :0 )
+
+/*
+ * The variables C0, C4, C16 and C20 can also be removed from the algorithm
+ * if APPROXIMATE_MUL_BY_SHIFTS is defined. They store correction values
+ * and can be considered insignificant.
+ */
+
+#endif
+
+static void
+DCT_8x4(int *coeff, unsigned char *out)
+/* pre: coeff == coefficients
+ post: coeff != coefficients
+ ** DO NOT ASSUME coeff TO BE THE SAME BEFORE AND AFTER CALLING THIS FUNCTION!
+*/
+{
+ register int base,val1,val2,val3;
+ int tmp1,tmp2;
+ int C0,C4,C16,C20;
+ int C2_18,C6_22,C1_17,C3_19,C5_21,C7_23;
+ register int t;
+#ifdef APPROXIMATE_MUL_BY_SHIFT
+ register int x;
+#endif
+
+ C0=coeff[0];
+ C4=coeff[4];
+ C16=coeff[16];
+ C20=coeff[20];
+
+ coeff[0]=TIMES_23168(coeff[0]);
+ coeff[4]=TIMES_23168(coeff[4]);
+ coeff[16]=TIMES_23168(coeff[16]);
+ coeff[20]=TIMES_23168(coeff[20]);
+
+ C2_18 = coeff[2]+coeff[18];
+ C6_22 = coeff[6]+coeff[22];
+ C1_17 = coeff[1]+coeff[17];
+ C3_19 = coeff[3]+coeff[19];
+ C5_21 = coeff[5]+coeff[21];
+ C7_23 = coeff[7]+coeff[23];
+
+// 0,7,25,32
+
+ base = 0x1000000;
+ base += coeff[0]+coeff[4]+coeff[16]+coeff[20];
+ base += TIMES_30270(C2_18);
+ base += TIMES_12538(C6_22);
+
+ val1 = TIMES_41986(coeff[9]);
+ val1 += TIMES_35594(coeff[11]);
+ val1 += TIMES_23783(coeff[13]);
+ val1 += TIMES_8351(coeff[15]);
+ val1 += TIMES_17391(coeff[25]);
+ val1 += TIMES_14743(coeff[27]);
+ val1 += TIMES_9851(coeff[29]);
+ val1 += TIMES_3459(coeff[31]);
+
+ val2 = TIMES_32134(C1_17);
+ val2 += TIMES_27242(C3_19);
+ val2 += TIMES_18202(C5_21);
+ val2 += TIMES_6392(C7_23);
+
+ val3 = TIMES_39550(coeff[10]);
+ val3 += TIMES_16382(coeff[14]+coeff[26]);
+ val3 += TIMES_6785(coeff[30]);
+ val3 += TIMES_30270(coeff[8]+coeff[12]);
+ val3 += TIMES_12538(coeff[24]+coeff[28]);
+
+ t=(base + val1 + val2 + val3) >> 17;
+ out[0]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3 - C4 - C20) >> 17;
+ out[7]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3 - C16- C20) >> 17;
+ out[24]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3 - C4 - C16 - C20) >> 17;
+ out[31]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+
+//1,6,25,30
+
+ base = 0x1000000;
+ base += coeff[0]-coeff[4]+coeff[16]-coeff[20];
+ base += TIMES_12538(C2_18);
+ base -= TIMES_30270(C6_22);
+
+ val1 = TIMES_35594(coeff[9]);
+ val1 -= TIMES_8351(coeff[11]);
+ val1 -= TIMES_41986(coeff[13]);
+ val1 -= TIMES_23783(coeff[15]);
+ val1 -= TIMES_14743(coeff[25]);
+ val1 -= TIMES_3459(coeff[27]);
+ val1 -= TIMES_17391(coeff[29]);
+ val1 -= TIMES_9851(coeff[31]);
+
+ val2 = TIMES_27242(C1_17);
+ val2 -= TIMES_6392(C3_19);
+ val2 -= TIMES_32134(C5_21);
+ val2 -= TIMES_18202(C7_23);
+
+ val3 = TIMES_16382(coeff[10]-coeff[30]);
+ val3 -= TIMES_39550(coeff[14]);
+ val3 += TIMES_6785(coeff[26]);
+ val3 += TIMES_12538(coeff[24]-coeff[28]);
+ val3 += TIMES_30270(coeff[8]-coeff[12]);
+
+ t=(base + val1 + val2 + val3 + C4 + C20) >> 17;
+ out[1]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3) >> 17;
+ out[6]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3 + C4 - C16 + C20) >> 17;
+ out[25]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3 + C20) >> 17;
+ out[30]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+
+//2,5,26,29
+
+ base = 0x1000000;
+ base += coeff[0] - coeff[4] + coeff[16] - coeff[20];
+ base -= TIMES_12538(C2_18);
+ base += TIMES_30270(C6_22);
+
+ val1 = TIMES_23783(coeff[9]);
+ val1 -= TIMES_41986(coeff[11]);
+ val1 += TIMES_8351(coeff[13]);
+ val1 += TIMES_35594(coeff[15]);
+ val1 += TIMES_9851(coeff[25]);
+ val1 -= TIMES_17391(coeff[27]);
+ val1 += TIMES_3459(coeff[29]);
+ val1 += TIMES_14743(coeff[31]);
+
+ val2 = TIMES_18202(C1_17);
+ val2 -= TIMES_32134(C3_19);
+ val2 += TIMES_6392(C5_21);
+ val2 += TIMES_27242(C7_23);
+
+ val3 = -TIMES_16382(coeff[10] - coeff[30]);
+ val3 += TIMES_39550(coeff[14]);
+ val3 -= TIMES_6785(coeff[26]);
+ val3 += TIMES_12538(coeff[24] - coeff[28]);
+ val3 += TIMES_30270(coeff[8] - coeff[12]);
+
+ t=(base + val1 + val2 + val3) >> 17;
+ out[2]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3) >> 17;
+ out[5]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3 - C16) >> 17;
+ out[26]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3 + C4 - C16 + C20) >> 17;
+ out[29]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+
+//3,4,27,28
+
+ base = 0x1000000;
+ base += coeff[0] + coeff[4] + coeff[16] + coeff[20];
+ base -= TIMES_30270(C2_18);
+ base -= TIMES_12538(C6_22);
+
+ val1 = TIMES_8351(coeff[9]);
+ val1 -= TIMES_23783(coeff[11]);
+ val1 += TIMES_35594(coeff[13]);
+ val1 += TIMES_3459(coeff[25]);
+ val1 -= TIMES_9851(coeff[27]);
+ val1 += TIMES_14743(coeff[29]);
+
+ val2 = TIMES_6392(C1_17);
+ val2 -= TIMES_18202(C3_19);
+ val2 += TIMES_27242(C5_21);
+
+ val3 = -TIMES_39550(coeff[10]);
+ val3 += TIMES_16382(coeff[14] + coeff[26]);
+ val3 -= TIMES_6785(coeff[30]);
+ val3 += TIMES_30270(coeff[8] + coeff[12]);
+ val3 += TIMES_12538(coeff[24] + coeff[28]);
+
+ tmp1 = TIMES_32134(C7_23);
+ tmp2 = TIMES_41986(coeff[15]) + TIMES_17391(coeff[31]);
+
+ t=(base + val1 + val2 + val3 - tmp1 - tmp2 - C4 - C20) >> 17;
+ out[3]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3) >> 17;
+ out[4]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3 - tmp1 + tmp2) >> 17;
+ out[27]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3 - C16 - C20) >> 17;
+ out[28]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+
+// Second half
+ C2_18 = coeff[2] - coeff[18];
+ C6_22 = coeff[6] - coeff[22];
+ C1_17 = coeff[1] - coeff[17];
+ C3_19 = coeff[3] - coeff[19];
+ C5_21 = coeff[5] - coeff[21];
+ C7_23 = coeff[7] - coeff[23];
+
+// 8,15,16,23
+
+ base = 0x1000000;
+ base += coeff[0] + coeff[4] - coeff[16] - coeff[20];
+ base +=TIMES_30270(C2_18);
+ base +=TIMES_12538(C6_22);
+
+ val1 = TIMES_17391(coeff[9]);
+ val1 += TIMES_14743(coeff[11]);
+ val1 += TIMES_9851(coeff[13]);
+ val1 += TIMES_3459(coeff[15]);
+ val1 -= TIMES_41986(coeff[25]);
+ val1 -= TIMES_35594(coeff[27]);
+ val1 -= TIMES_23783(coeff[29]);
+ val1 -= TIMES_8351(coeff[31]);
+
+ val2 = TIMES_32134(C1_17);
+ val2 += TIMES_27242(C3_19);
+ val2 += TIMES_18202(C5_21);
+ val2 += TIMES_6392(C7_23);
+
+ val3 = TIMES_16382(coeff[10] - coeff[30]);
+ val3 += TIMES_6785(coeff[14]);
+ val3 -= TIMES_39550(coeff[26]);
+ val3 -=TIMES_30270(coeff[24] + coeff[28]);
+ val3 +=TIMES_12538(coeff[8] + coeff[12]);
+
+ t=(base + val1 + val2 + val3) >> 17;
+ out[8]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3 - C4 + C16 + C20) >> 17;
+ out[15]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3) >> 17;
+ out[16]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3 - C4 + C20) >> 17;
+ out[23]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+
+//9,14,17,22
+
+ base = 0x1000000;
+ base += coeff[0] - coeff[4] - coeff[16] + coeff[20];
+ base += TIMES_12538(C2_18);
+ base -= TIMES_30270(C6_22);
+
+ val1 = TIMES_14743(coeff[9]);
+ val1 -= TIMES_3459(coeff[11]);
+ val1 -= TIMES_17391(coeff[13]);
+ val1 -= TIMES_9851(coeff[15]);
+ val1 -= TIMES_35594(coeff[25]);
+ val1 += TIMES_8351(coeff[27]);
+ val1 += TIMES_41986(coeff[29]);
+ val1 += TIMES_23783(coeff[31]);
+
+ val2 = TIMES_27242(C1_17);
+ val2 -= TIMES_6392(C3_19);
+ val2 -= TIMES_32134(C5_21);
+ val2 -= TIMES_18202(C7_23);
+
+ val3 = TIMES_6785(coeff[10]);
+ val3 -= TIMES_16382(coeff[14] + coeff[26]);
+ val3 += TIMES_39550(coeff[30]);
+ val3 += TIMES_12538(coeff[8] - coeff[12]);
+ val3 -= TIMES_30270(coeff[24] - coeff[28]);
+
+ t=(base + val1 + val2 + val3 + C4 + C16 - C20) >> 17;
+ out[9]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3 + C16) >> 17;
+ out[14]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3 + C4) >> 17;
+ out[17]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3) >> 17;
+ out[22]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+
+//10,13,18,21
+
+ base = 0x1000000;
+ base += coeff[0] - coeff[4] - coeff[16] + coeff[20];
+ base -= TIMES_12538(C2_18);
+ base += TIMES_30270(C6_22);
+
+ val1 = TIMES_9851(coeff[9]);
+ val1 -= TIMES_17391(coeff[11]);
+ val1 += TIMES_3459(coeff[13]);
+ val1 += TIMES_14743(coeff[15]);
+ val1 -= TIMES_23783(coeff[25]);
+ val1 += TIMES_41986(coeff[27]);
+ val1 -= TIMES_8351(coeff[29]);
+ val1 -= TIMES_35594(coeff[31]);
+
+ val2 = TIMES_18202(C1_17);
+ val2 -= TIMES_32134(C3_19);
+ val2 += TIMES_6392(C5_21);
+ val2 += TIMES_27242(C7_23);
+
+ val3 = -TIMES_6785(coeff[10]);
+ val3 += TIMES_16382(coeff[14]+coeff[26]);
+ val3 -= TIMES_39550(coeff[30]);
+ val3 += TIMES_12538(coeff[8]-coeff[12]);
+ val3 -= TIMES_30270(coeff[24]-coeff[28]);
+
+ t=(base + val1 + val2 + val3) >> 17;
+ out[10]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3 + C4 + C16 - C20) >> 17;
+ out[13]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3) >> 17;
+ out[18]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3 + C4) >> 17;
+ out[21]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+
+// 11,12,19,20
+
+ base = 0x1000000;
+ base += coeff[0]+coeff[4]-coeff[16]-coeff[20];
+ base -= TIMES_30270(C2_18);
+ base -= TIMES_12538(C6_22);
+
+ val1 = TIMES_3459(coeff[9]);
+ val1 -= TIMES_9851(coeff[11]);
+ val1 += TIMES_14743(coeff[13]);
+ val1 -= TIMES_8351(coeff[25]);
+ val1 += TIMES_23783(coeff[27]);
+ val1 -= TIMES_35594(coeff[29]);
+
+ val2 = TIMES_6392(C1_17);
+ val2 -= TIMES_18202(C3_19);
+ val2 += TIMES_27242(C5_21);
+
+ val3 = -TIMES_16382(coeff[10] - coeff[30]);
+ val3 -= TIMES_6785(coeff[14]);
+ val3 += TIMES_39550(coeff[26]);
+ val3 -= TIMES_30270(coeff[24]+coeff[28]);
+ val3 += TIMES_12538(coeff[8]+coeff[12]);
+
+ tmp1 = TIMES_32134(C7_23);
+ tmp2 = -TIMES_17391(coeff[15]) + TIMES_41986(coeff[31]);
+
+ t=(base + val1 + val2 + val3 - tmp1 + tmp2 + C16 + C20) >> 17;
+ out[11]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 - val2 + val3 + C16 + C20) >> 17;
+ out[12]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base - val1 + val2 - val3 - tmp1 - tmp2 - C4 + C20) >> 17;
+ out[19]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+ t=(base + val1 - val2 - val3) >> 17;
+ out[20]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t;
+}
+
+#undef TIMES_16382
+#undef TIMES_23168
+#undef TIMES_30270
+#undef TIMES_41986
+#undef TIMES_35594
+#undef TIMES_23783
+#undef TIMES_8351
+#undef TIMES_17391
+#undef TIMES_14743
+#undef TIMES_9851
+#undef TIMES_3459
+#undef TIMES_32134
+#undef TIMES_27242
+#undef TIMES_18202
+#undef TIMES_6392
+#undef TIMES_39550
+#undef TIMES_6785
+#undef TIMES_12538
+
+/******************************************************************************
+ * Main Decoder Functions
+ ******************************************************************************/
+
+/* This function handles the decompression of a single 8x4 block. It is
+ * independent of the palette (YUV422, YUV420, YUV400, GBR422...). cinfo->bytes
+ * determines the positin in the input buffer.
+ */
+static int
+decompress8x4(unsigned char *pOut,
+ unsigned char *pIn,
+ int *lastDC,
+ int uvFlag,
+ struct comp_info *cinfo)
+{
+ int i, x, y, dc;
+ int coeffs[32];
+ int deZigZag[32];
+ int *dest;
+ int *src;
+ unsigned char *qt = cinfo->qt;
+
+ if (! uvFlag) {
+ huffmanDecoderY(coeffs, (int*) pIn, cinfo);
+
+ /* iDPCM and dequantize first coefficient */
+ dc = (*lastDC) + coeffs[0];
+ coeffs[0] = dc * (qt[0] + 1);
+ *lastDC = dc;
+
+ /* ...and the second coefficient */
+ coeffs[1] = ((qt[1] + 1) * coeffs[1]) >> 1;
+
+ /* Dequantize, starting at 3rd element */
+ for (i = 2; i < 32; i++)
+ coeffs[i] = (qt[i] + 1) * coeffs[i];
+ } else {
+ huffmanDecoderUV(coeffs, (int*) pIn, cinfo);
+
+ /* iDPCM */
+ dc = (*lastDC) + coeffs[0];
+ coeffs[0] = dc;
+ *lastDC = dc;
+
+ /* Dequantize */
+ for (i = 0; i < 32; i++)
+ coeffs[i] = (qt[32 + i] + 1) * coeffs[i];
+ }
+
+ /* Dezigzag */
+ for (i = 0; i < 32; i++)
+ deZigZag[i] = coeffs[ZigZag518[i]];
+
+ /* Transpose the dezigzagged coefficient matrix */
+ src = deZigZag;
+ dest = coeffs;
+ for (y = 0; y <= 3; ++y) {
+ for (x = 0; x <= 7; ++x) {
+ dest[x] = src[x * 4];
+ }
+ src += 1;
+ dest += 8;
+ }
+
+ /* Do the inverse DCT transform */
+ DCT_8x4(coeffs, pOut);
+
+ return 0; /* Always returns 0 */
+}
+
+static inline void
+copyBlock(unsigned char *src, unsigned char *dest, int destInc)
+{
+ int i;
+ unsigned int *pSrc, *pDest;
+
+ for (i = 0; i <= 3; i++) {
+ pSrc = (unsigned int *) src;
+ pDest = (unsigned int *) dest;
+ pDest[0] = pSrc[0];
+ pDest[1] = pSrc[1];
+ src += 8;
+ dest += destInc;
+ }
+}
+
+#if 0
+static inline int
+decompress400NoMMXOV518(unsigned char *pIn,
+ unsigned char *pOut,
+ unsigned char *pTmp,
+ const int w,
+ const int h,
+ const int numpix,
+ struct comp_info *cinfo)
+{
+ int iOutY, x, y;
+ int lastYDC = 0;
+
+ /* Start Y loop */
+ y = 0;
+ do {
+ iOutY = w * y;
+ x = 0;
+ do {
+ decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo);
+ copyBlock(pTmp, pOut + iOutY, w);
+ iOutY += 8;
+ x += 8;
+ } while (x < w);
+ y += 4;
+ } while (y < h);
+
+ /* Did we decode too much? */
+ if (cinfo->bytes > cinfo->rawLen + 897)
+ return 1;
+
+ /* Did we decode enough? */
+ if (cinfo->bytes >= cinfo->rawLen - 897)
+ return 0;
+ else
+ return 1;
+}
+#endif
+
+static inline int
+decompress420NoMMXOV518(unsigned char *pIn,
+ unsigned char *pOut,
+ unsigned char *pTmp,
+ const int w,
+ const int h,
+ const int numpix,
+ struct comp_info *cinfo,
+ int yvu)
+{
+ unsigned char *pOutU, *pOutV;
+ int iOutY, iOutU, iOutV, x, y;
+ int lastYDC = 0;
+ int lastUDC = 0;
+ int lastVDC = 0;
+
+ if (yvu) {
+ pOutV = pOut + numpix;
+ pOutU = pOutV + numpix / 4;
+ } else {
+ pOutU = pOut + numpix;
+ pOutV = pOutU + numpix / 4;
+ }
+
+ /* Start Y loop */
+ y = 0;
+ do {
+ iOutY = w * y;
+ iOutV = iOutU = iOutY / 4;
+
+ x = 0;
+ do {
+ decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo);
+ copyBlock(pTmp, pOut + iOutY, w);
+ iOutY += 8;
+ x += 8;
+ } while (x < w);
+
+
+
+ iOutY = w * (y + 4);
+ x = 0;
+ do {
+ decompress8x4(pTmp, pIn, &lastUDC, 1, cinfo);
+ copyBlock(pTmp, pOutU + iOutU, w/2);
+ iOutU += 8;
+
+ decompress8x4(pTmp, pIn, &lastVDC, 1, cinfo);
+ copyBlock(pTmp, pOutV + iOutV, w/2);
+ iOutV += 8;
+
+ decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo);
+ copyBlock(pTmp, pOut + iOutY, w);
+ iOutY += 8;
+
+ decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo);
+ copyBlock(pTmp, pOut + iOutY, w);
+ iOutY += 8;
+
+ x += 16;
+ } while (x < w);
+
+ y += 8;
+ } while (y < h);
+
+ /* Did we decode too much? */
+ if (cinfo->bytes > cinfo->rawLen + 897)
+ return 1;
+
+ /* Did we decode enough? */
+ if (cinfo->bytes >= cinfo->rawLen - (897 + 64))
+ return 0;
+ else
+ return 1;
+}
+
+/* Get quantization tables from input
+ * Returns: <0 if error, or >=0 otherwise */
+static int
+get_qt_dynamic(unsigned char *pIn, struct comp_info *cinfo)
+{
+ int rawLen = cinfo->rawLen;
+
+ /* Make sure input is actually big enough to hold trailer */
+ if (rawLen < 72) {
+ return -1;
+ }
+
+ cinfo->qt = pIn + rawLen - 64;
+
+ return 0;
+}
+
+/* Remove all 0 blocks from input */
+static void remove0blocks(unsigned char *pIn, int *inSize)
+{
+ long long *in = (long long *)pIn;
+ long long *out = (long long *)pIn;
+ int i;
+
+ for (i = 0; i < *inSize; i += 8, in++)
+ /* Skip 8 byte blocks of all 0 */
+ if (*in)
+ *out++ = *in;
+
+ *inSize -= (in - out) * 8;
+}
+
+#if 0 /* not used */
+/* Input format is raw isoc. data (with intact SOF header, packet numbers
+ * stripped, and all-zero blocks removed).
+ * Output format is planar YUV400
+ * Returns uncompressed data length if success, or zero if error
+ */
+static int
+Decompress400(unsigned char *pIn,
+ unsigned char *pOut,
+ int w,
+ int h,
+ int inSize)
+{
+ struct comp_info cinfo;
+ int numpix = w * h;
+ unsigned char pTmp[32];
+
+ remove0blocks(pIn, &inSize);
+
+ cinfo.bytes = 0;
+ cinfo.bits = 0;
+ cinfo.rawLen = inSize;
+
+ if (get_qt_dynamic(pIn, &cinfo) < 0)
+ return 0;
+
+ /* Decompress, skipping the 8-byte SOF header */
+ if (decompress400NoMMXOV518(pIn + 8, pOut, pTmp, w, h, numpix, &cinfo))
+// return 0;
+ ; /* Don't return error yet */
+
+ return (numpix);
+}
+#endif
+
+/* Input format is raw isoc. data (with intact SOF header, packet numbers
+ * stripped, and all-zero blocks removed).
+ * Output format is planar YUV420
+ * Returns uncompressed data length if success, or zero if error
+ */
+static int v4lconvert_ov518_to_yuv420(unsigned char *src, unsigned char *dst,
+ int w, int h, int yvu, int inSize)
+{
+ struct comp_info cinfo;
+ int numpix = w * h;
+ unsigned char pTmp[32];
+
+ remove0blocks(src, &inSize);
+
+ cinfo.bytes = 0;
+ cinfo.bits = 0;
+ cinfo.rawLen = inSize;
+
+ if (get_qt_dynamic(src, &cinfo) < 0)
+ return -1;
+
+ /* Decompress, skipping the 8-byte SOF header */
+ if (decompress420NoMMXOV518(src + 8, dst, pTmp, w, h, numpix, &cinfo, yvu))
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int width, height, yvu, src_size, dest_size;
+ unsigned char src_buf[200000];
+ unsigned char dest_buf[500000];
+
+ while (1) {
+ if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (src_size > sizeof(src_buf)) {
+ fprintf(stderr, "%s: error: src_buf too small, need: %d\n",
+ argv[0], src_size);
+ return 2;
+ }
+
+ if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+
+ dest_size = width * height * 3 / 2;
+ if (dest_size > sizeof(dest_buf)) {
+ fprintf(stderr, "%s: error: dest_buf too small, need: %d\n",
+ argv[0], dest_size);
+ dest_size = -1;
+ } else if (v4lconvert_ov518_to_yuv420(src_buf, dest_buf, width, height,
+ yvu, src_size))
+ dest_size = -1;
+
+ if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int),
+ argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+
+ if (dest_size == -1)
+ continue;
+
+ if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0]))
+ return 1; /* Erm, no way to recover without loosing sync with libv4l */
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c b/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c
new file mode 100644
index 000000000..358264c68
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c
@@ -0,0 +1,140 @@
+/*
+# (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 <unistd.h>
+
+#include "libv4lprocessing.h"
+#include "libv4lprocessing-priv.h"
+#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
+#include "../libv4lsyscall-priv.h"
+
+static int autogain_active(struct v4lprocessing_data *data) {
+ return v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN);
+}
+
+/* auto gain and exposure algorithm based on the knee algorithm described here:
+ http://ytse.tricolour.net/docs/LowLightOptimization.html */
+static int autogain_calculate_lookup_tables(
+ struct v4lprocessing_data *data,
+ unsigned char *buf, const struct v4l2_format *fmt)
+{
+ int x, y, target, steps, avg_lum = 0;
+ int gain, exposure, orig_gain, orig_exposure;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl gainctrl, expoctrl;
+ const int deadzone = 8;
+
+ ctrl.id = V4L2_CID_EXPOSURE;
+ expoctrl.id = V4L2_CID_EXPOSURE;
+ if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &expoctrl) ||
+ SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl))
+ return 0;
+ exposure = orig_exposure = ctrl.value;
+
+ ctrl.id = V4L2_CID_GAIN;
+ gainctrl.id = V4L2_CID_GAIN;
+ if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &gainctrl) ||
+ SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl))
+ return 0;
+ gain = orig_gain = ctrl.value;
+
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SRGGB8:
+ buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 +
+ fmt->fmt.pix.width / 4;
+
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
+ avg_lum += *buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width / 2;
+ }
+ avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width / 4;
+ break;
+
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 +
+ fmt->fmt.pix.width * 3 / 4;
+
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
+ avg_lum += *buf++;
+ avg_lum += *buf++;
+ avg_lum += *buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3 / 2;
+ }
+ avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width * 3 / 4;
+ break;
+ }
+
+ /* If we are off a multiple of deadzone, do multiple steps to reach the
+ desired lumination fast (with the risc of a slight overshoot) */
+ target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET);
+ steps = abs(target - avg_lum) / deadzone;
+
+ for (x = 0; x < steps; x++) {
+ if (avg_lum > target) {
+ if (exposure > expoctrl.default_value)
+ exposure--;
+ else if (gain > gainctrl.default_value)
+ gain--;
+ else if (exposure > expoctrl.minimum)
+ exposure--;
+ else if (gain > gainctrl.minimum)
+ gain--;
+ else
+ break;
+ } else {
+ if (gain < gainctrl.default_value)
+ gain++;
+ else if (exposure < expoctrl.default_value)
+ exposure++;
+ else if (gain < gainctrl.maximum)
+ gain++;
+ else if (exposure < expoctrl.maximum)
+ exposure++;
+ else
+ break;
+ }
+ }
+
+ if (gain != orig_gain) {
+ ctrl.id = V4L2_CID_GAIN;
+ ctrl.value = gain;
+ SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl);
+ }
+ if (exposure != orig_exposure) {
+ ctrl.id = V4L2_CID_EXPOSURE;
+ ctrl.value = exposure;
+ SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl);
+ }
+
+ return 0;
+}
+
+struct v4lprocessing_filter autogain_filter = {
+ autogain_active, autogain_calculate_lookup_tables };
diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c b/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c
new file mode 100644
index 000000000..fcb5bb0cf
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c
@@ -0,0 +1,57 @@
+/*
+# (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 <math.h>
+#include "libv4lprocessing.h"
+#include "libv4lprocessing-priv.h"
+
+#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color)))
+
+static int gamma_active(struct v4lprocessing_data *data) {
+ int gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA);
+
+ return gamma && gamma != 1000;
+}
+
+static int gamma_calculate_lookup_tables(
+ struct v4lprocessing_data *data,
+ unsigned char *buf, const struct v4l2_format *fmt)
+{
+ int i, x, gamma;
+
+ gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA);
+
+ if (gamma != data->last_gamma) {
+ for (i = 0; i < 256; i++) {
+ x = powf(i / 255.0, 1000.0 / gamma) * 255;
+ data->gamma_table[i] = CLIP(x);
+ }
+ data->last_gamma = gamma;
+ }
+
+ for (i = 0; i < 256; i++) {
+ data->comp1[i] = data->gamma_table[data->comp1[i]];
+ data->green[i] = data->gamma_table[data->green[i]];
+ data->comp2[i] = data->gamma_table[data->comp2[i]];
+ }
+
+ return 1;
+}
+
+struct v4lprocessing_filter gamma_filter = {
+ gamma_active, gamma_calculate_lookup_tables };
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..b73c73b53
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h
@@ -0,0 +1,60 @@
+/*
+# (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"
+#include "../libv4lsyscall-priv.h"
+
+#define V4L2PROCESSING_UPDATE_RATE 10
+
+struct v4lprocessing_data {
+ struct v4lcontrol_data *control;
+ int fd;
+ int do_process;
+ /* True if any of the lookup tables does not contain
+ linear 0-255 */
+ int lookup_table_active;
+ /* Counts the number of processed frames until a
+ V4L2PROCESSING_UPDATE_RATE overflow happens */
+ int lookup_table_update_counter;
+ /* RGB/BGR lookup tables */
+ unsigned char comp1[256];
+ unsigned char green[256];
+ unsigned char comp2[256];
+ /* Filter private data for filters which need it */
+ int last_gamma;
+ unsigned char gamma_table[256];
+};
+
+struct v4lprocessing_filter {
+ /* Returns 1 if the filter is active */
+ int (*active)(struct v4lprocessing_data *data);
+ /* Returns 1 if any of the lookup tables was changed */
+ int (*calculate_lookup_tables)(struct v4lprocessing_data *data,
+ unsigned char *buf, const struct v4l2_format *fmt);
+};
+
+extern struct v4lprocessing_filter whitebalance_filter;
+extern struct v4lprocessing_filter autogain_filter;
+extern struct v4lprocessing_filter gamma_filter;
+
+#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..cbbcca73c
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c
@@ -0,0 +1,181 @@
+/*
+# (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 <unistd.h>
+#include "libv4lprocessing.h"
+#include "libv4lprocessing-priv.h"
+#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
+
+static struct v4lprocessing_filter *filters[] = {
+ &whitebalance_filter,
+ &autogain_filter,
+ &gamma_filter,
+};
+
+struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data* control)
+{
+ struct v4lprocessing_data *data =
+ calloc(1, sizeof(struct v4lprocessing_data));
+
+ if (!data)
+ return NULL;
+
+ data->fd = fd;
+ data->control = control;
+
+ return data;
+}
+
+void v4lprocessing_destroy(struct v4lprocessing_data *data)
+{
+ free(data);
+}
+
+int v4lprocessing_pre_processing(struct v4lprocessing_data *data)
+{
+ int i;
+
+ data->do_process = 0;
+ for (i = 0; i < ARRAY_SIZE(filters); i++) {
+ if (filters[i]->active(data))
+ data->do_process = 1;
+ }
+
+ return data->do_process;
+}
+
+static void v4lprocessing_update_lookup_tables(struct v4lprocessing_data *data,
+ unsigned char *buf, const struct v4l2_format *fmt)
+{
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ data->comp1[i] = i;
+ data->green[i] = i;
+ data->comp2[i] = i;
+ }
+
+ data->lookup_table_active = 0;
+ for (i = 0; i < ARRAY_SIZE(filters); i++) {
+ if (filters[i]->active(data)) {
+ if (filters[i]->calculate_lookup_tables(data, buf, fmt))
+ data->lookup_table_active = 1;
+ }
+ }
+}
+
+static void v4lprocessing_do_processing(struct v4lprocessing_data *data,
+ unsigned char *buf, const struct v4l2_format *fmt)
+{
+ int x, y;
+
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
+ *buf = data->green[*buf];
+ buf++;
+ *buf = data->comp1[*buf];
+ buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
+ *buf = data->comp2[*buf];
+ buf++;
+ *buf = data->green[*buf];
+ buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
+ }
+ break;
+
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */
+ for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
+ *buf = data->comp1[*buf];
+ buf++;
+ *buf = data->green[*buf];
+ buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
+ for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
+ *buf = data->green[*buf];
+ buf++;
+ *buf = data->comp2[*buf];
+ buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
+ }
+ break;
+
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ for (y = 0; y < fmt->fmt.pix.height; y++) {
+ for (x = 0; x < fmt->fmt.pix.width; x++) {
+ *buf = data->comp1[*buf];
+ buf++;
+ *buf = data->green[*buf];
+ buf++;
+ *buf = data->comp2[*buf];
+ buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - 3 * fmt->fmt.pix.width;
+ }
+ break;
+ }
+}
+
+void v4lprocessing_processing(struct v4lprocessing_data *data,
+ unsigned char *buf, const struct v4l2_format *fmt)
+{
+ if (!data->do_process)
+ return;
+
+ /* Do we support the current pixformat? */
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ break;
+ default:
+ return; /* Non supported pix format */
+ }
+
+ if (v4lcontrol_controls_changed(data->control) ||
+ data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) {
+ v4lprocessing_update_lookup_tables(data, buf, fmt);
+ data->lookup_table_update_counter = 0;
+ } else
+ data->lookup_table_update_counter++;
+
+ if (data->lookup_table_active)
+ v4lprocessing_do_processing(data, buf, fmt);
+
+ 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..69d8865b2
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h
@@ -0,0 +1,43 @@
+/*
+# (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
+
+#include "../libv4lsyscall-priv.h"
+#include <linux/videodev2.h>
+
+struct v4lprocessing_data;
+struct v4lcontrol_data;
+
+struct v4lprocessing_data *v4lprocessing_create(int fd, 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/whitebalance.c b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c
new file mode 100644
index 000000000..64d20b3ff
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c
@@ -0,0 +1,142 @@
+/*
+# (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 <unistd.h>
+#include "libv4lprocessing.h"
+#include "libv4lprocessing-priv.h"
+#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
+
+#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color)))
+
+static int whitebalance_active(struct v4lprocessing_data *data) {
+ return v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE);
+}
+
+static int whitebalance_calculate_lookup_tables_bayer(
+ struct v4lprocessing_data *data, unsigned char *buf,
+ const struct v4l2_format *fmt, int starts_with_green)
+{
+ int x, y, a1 = 0, a2 = 0, b1 = 0, b2 = 0;
+ int green_avg, comp1_avg, comp2_avg, avg_avg;
+
+ for (y = 0; y < fmt->fmt.pix.height; y += 2) {
+ for (x = 0; x < fmt->fmt.pix.width; x += 2) {
+ a1 += *buf++;
+ a2 += *buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
+ for (x = 0; x < fmt->fmt.pix.width; x += 2) {
+ b1 += *buf++;
+ b2 += *buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
+ }
+
+ if (starts_with_green) {
+ green_avg = (a1 + b2) / 512;
+ comp1_avg = a2 / 256;
+ comp2_avg = b1 / 256;
+ } else {
+ green_avg = (a2 + b1) / 512;
+ comp1_avg = a1 / 256;
+ comp2_avg = b2 / 256;
+ }
+
+ x = fmt->fmt.pix.width * fmt->fmt.pix.height / 64;
+ if (abs(green_avg - comp1_avg) < x &&
+ abs(green_avg - comp2_avg) < x &&
+ abs(comp1_avg - comp2_avg) < x)
+ return 0;
+
+ avg_avg = (green_avg + comp1_avg + comp2_avg) / 3;
+
+ for (x = 0; x < 256; x++) {
+ data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg);
+ data->green[x] = CLIP(data->green[x] * avg_avg / green_avg);
+ data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg);
+ }
+
+ return 1;
+}
+
+static int whitebalance_calculate_lookup_tables_rgb(
+ struct v4lprocessing_data *data, unsigned char *buf,
+ const struct v4l2_format *fmt)
+{
+ int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0, avg_avg;
+
+ for (y = 0; y < fmt->fmt.pix.height; y++) {
+ for (x = 0; x < fmt->fmt.pix.width; x++) {
+ comp1_avg += *buf++;
+ green_avg += *buf++;
+ comp2_avg += *buf++;
+ }
+ buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3;
+ }
+
+ x = fmt->fmt.pix.width * fmt->fmt.pix.height * 4;
+ if (abs(green_avg - comp1_avg) < x &&
+ abs(green_avg - comp2_avg) < x &&
+ abs(comp1_avg - comp2_avg) < x)
+ return 0;
+
+ /* scale to avoid integer overflows */
+ green_avg /= 256;
+ comp1_avg /= 256;
+ comp2_avg /= 256;
+ avg_avg = (green_avg + comp1_avg + comp2_avg) / 3;
+
+ for (x = 0; x < 256; x++) {
+ data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg);
+ data->green[x] = CLIP(data->green[x] * avg_avg / green_avg);
+ data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg);
+ }
+
+ return 1;
+}
+
+
+static int whitebalance_calculate_lookup_tables(
+ struct v4lprocessing_data *data,
+ unsigned char *buf, const struct v4l2_format *fmt)
+{
+ switch (fmt->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */
+ return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 1);
+
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */
+ return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 0);
+
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ return whitebalance_calculate_lookup_tables_rgb(data, buf, fmt);
+ }
+
+ return 0; /* Should never happen */
+}
+
+struct v4lprocessing_filter whitebalance_filter = {
+ whitebalance_active, whitebalance_calculate_lookup_tables };
diff --git a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c
index 0f26b227a..31cbc3c68 100644
--- a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c
+++ b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c
@@ -20,13 +20,65 @@
*/
+#include <string.h>
#include "libv4lconvert-priv.h"
-#define RGB2YUV(r,g,b,y,u,v) \
- (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15); \
+#define RGB2Y(r,g,b,y) \
+ (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15)
+
+#define RGB2UV(r,g,b,u,v) \
(u) = (( -4878*(r) - 9578*(g) + 14456*(b) + 4210688) >> 15); \
(v) = (( 14456*(r) - 12105*(g) - 2351*(b) + 4210688) >> 15)
+void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt, int bgr, int yvu)
+{
+ int x, y;
+ unsigned char *udest, *vdest;
+
+ /* Y */
+ for (y = 0; y < src_fmt->fmt.pix.height; y++) {
+ for (x = 0; x < src_fmt->fmt.pix.width; x++) {
+ if (bgr) {
+ RGB2Y(src[2], src[1], src[0], *dest++);
+ } else {
+ RGB2Y(src[0], src[1], src[2], *dest++);
+ }
+ src += 3;
+ }
+ src += src_fmt->fmt.pix.bytesperline - 3 * src_fmt->fmt.pix.width;
+ }
+ src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline;
+
+ /* U + V */
+ if (yvu) {
+ vdest = dest;
+ udest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4;
+ } else {
+ udest = dest;
+ vdest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4;
+ }
+
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ for (x = 0; x < src_fmt->fmt.pix.width / 2; x++) {
+ int avg_src[3];
+ avg_src[0] = (src[0] + src[3] + src[src_fmt->fmt.pix.bytesperline] +
+ src[src_fmt->fmt.pix.bytesperline + 3]) / 4;
+ avg_src[1] = (src[1] + src[4] + src[src_fmt->fmt.pix.bytesperline + 1] +
+ src[src_fmt->fmt.pix.bytesperline + 4]) / 4;
+ avg_src[2] = (src[2] + src[5] + src[src_fmt->fmt.pix.bytesperline + 2] +
+ src[src_fmt->fmt.pix.bytesperline + 5]) / 4;
+ if (bgr) {
+ RGB2UV(avg_src[2], avg_src[1], avg_src[0], *udest++, *vdest++);
+ } else {
+ RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++);
+ }
+ src += 6;
+ }
+ src += 2 * src_fmt->fmt.pix.bytesperline - 3 * src_fmt->fmt.pix.width;
+ }
+}
+
#define YUV2R(y, u, v) ({ \
int r = (y) + ((((v)-128)*1436) >> 10); r > 255 ? 255 : r < 0 ? 0 : r; })
#define YUV2G(y, u, v) ({ \
@@ -37,13 +89,20 @@
#define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))
void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
const unsigned char *ysrc = src;
- const unsigned char *usrc = src + width * height;
- const unsigned char *vsrc = usrc + (width * height) / 4;
+ const unsigned char *usrc, *vsrc;
+
+ if (yvu) {
+ vsrc = src + width * height;
+ usrc = vsrc + (width * height) / 4;
+ } else {
+ usrc = src + width * height;
+ vsrc = usrc + (width * height) / 4;
+ }
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
@@ -76,7 +135,7 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
vsrc++;
}
/* Rewind u and v for next line */
- if (i&1) {
+ if (!(i&1)) {
usrc -= width / 2;
vsrc -= width / 2;
}
@@ -84,13 +143,20 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
}
void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
const unsigned char *ysrc = src;
- const unsigned char *usrc = src + width * height;
- const unsigned char *vsrc = usrc + (width * height) / 4;
+ const unsigned char *usrc, *vsrc;
+
+ if (yvu) {
+ vsrc = src + width * height;
+ usrc = vsrc + (width * height) / 4;
+ } else {
+ usrc = src + width * height;
+ vsrc = usrc + (width * height) / 4;
+ }
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
@@ -123,7 +189,7 @@ void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest,
vsrc++;
}
/* Rewind u and v for next line */
- if (i&1) {
+ if (!(i&1)) {
usrc -= width / 2;
vsrc -= width / 2;
}
@@ -183,11 +249,11 @@ void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dest,
}
void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int yvu)
{
int i, j;
const unsigned char *src1;
- unsigned char *vdest;
+ unsigned char *udest, *vdest;
/* copy the Y values */
src1 = src;
@@ -202,10 +268,16 @@ void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest,
/* copy the U and V values */
src++; /* point to V */
src1 = src + width * 2; /* next line */
- vdest = dest + width * height / 4;
+ if (yvu) {
+ vdest = dest;
+ udest = dest + width * height / 4;
+ } else {
+ udest = dest;
+ vdest = dest + width * height / 4;
+ }
for (i = 0; i < height; i += 2) {
for (j = 0; j < width; j += 2) {
- *dest++ = ((int) src[0] + src1[0]) / 2; /* U */
+ *udest++ = ((int) src[0] + src1[0]) / 2; /* U */
*vdest++ = ((int) src[2] + src1[2]) / 2; /* V */
src += 4;
src1 += 4;
@@ -267,31 +339,88 @@ void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dest,
}
}
-void v4lconvert_yvyu_to_yuv420(const unsigned char *src, unsigned char *dest,
+void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dest,
int width, int height)
{
+ int j;
+
+ while (--height >= 0) {
+ for (j = 0; j < width; j += 2) {
+ int u = src[0];
+ int v = src[2];
+ int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
+ int rg = (((u - 128) << 1) + (u - 128) +
+ ((v - 128) << 2) + ((v - 128) << 1)) >> 3;
+ int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
+
+ *dest++ = CLIP(src[1] + u1);
+ *dest++ = CLIP(src[1] - rg);
+ *dest++ = CLIP(src[1] + v1);
+
+ *dest++ = CLIP(src[3] + u1);
+ *dest++ = CLIP(src[3] - rg);
+ *dest++ = CLIP(src[3] + v1);
+ src += 4;
+ }
+ }
+}
+
+void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dest,
+ int width, int height)
+{
+ int j;
+
+ while (--height >= 0) {
+ for (j = 0; j < width; j += 2) {
+ int u = src[0];
+ int v = src[2];
+ int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
+ int rg = (((u - 128) << 1) + (u - 128) +
+ ((v - 128) << 2) + ((v - 128) << 1)) >> 3;
+ int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
+
+ *dest++ = CLIP(src[1] + v1);
+ *dest++ = CLIP(src[1] - rg);
+ *dest++ = CLIP(src[1] + u1);
+
+ *dest++ = CLIP(src[3] + v1);
+ *dest++ = CLIP(src[3] - rg);
+ *dest++ = CLIP(src[3] + u1);
+ src += 4;
+ }
+ }
+}
+
+void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dest,
+ int width, int height, int yvu)
+{
int i, j;
const unsigned char *src1;
- unsigned char *vdest;
+ unsigned char *udest, *vdest;
/* copy the Y values */
src1 = src;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
- *dest++ = src1[0];
- *dest++ = src1[2];
+ *dest++ = src1[1];
+ *dest++ = src1[3];
src1 += 4;
}
}
/* copy the U and V values */
- src++; /* point to V */
src1 = src + width * 2; /* next line */
- vdest = dest + width * height / 4;
+ if (yvu) {
+ vdest = dest;
+ udest = dest + width * height / 4;
+ } else {
+ udest = dest;
+ vdest = dest + width * height / 4;
+ }
for (i = 0; i < height; i += 2) {
for (j = 0; j < width; j += 2) {
- *dest++ = ((int) src[2] + src1[2]) / 2; /* U */
- *vdest++ = ((int) src[0] + src1[0]) / 2; /* V */
+ *udest++ = ((int) src[0] + src1[0]) / 2; /* U */
+ *vdest++ = ((int) src[2] + src1[2]) / 2; /* V */
src += 4;
src1 += 4;
}
@@ -314,3 +443,32 @@ void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst,
*dst++ = tmp0;
}
}
+
+void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dest,
+ const struct v4l2_format *src_fmt)
+{
+ int y;
+
+ /* Copy Y */
+ for (y = 0; y < src_fmt->fmt.pix.height; y++) {
+ memcpy(dest, src, src_fmt->fmt.pix.width);
+ dest += src_fmt->fmt.pix.width;
+ src += src_fmt->fmt.pix.bytesperline;
+ }
+
+ /* Copy component 2 */
+ src += src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 4;
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ memcpy(dest, src, src_fmt->fmt.pix.width / 2);
+ dest += src_fmt->fmt.pix.width / 2;
+ src += src_fmt->fmt.pix.bytesperline / 2;
+ }
+
+ /* Copy component 1 */
+ src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 2;
+ for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) {
+ memcpy(dest, src, src_fmt->fmt.pix.width / 2);
+ dest += src_fmt->fmt.pix.width / 2;
+ src += src_fmt->fmt.pix.bytesperline / 2;
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c b/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c
new file mode 100644
index 000000000..19951300f
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/sn9c20x.c
@@ -0,0 +1,137 @@
+/*
+ * Sonix SN9C20X decoder
+ * Vasily Khoruzhick, (C) 2008-2009
+ * Algorithm based on Java code written by Jens on microdia google group
+ *
+ * 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
+ *
+ * Note this code was originally licensed under the GNU GPL instead of the
+ * GNU LGPL, its license has been changed by its author.
+ */
+
+#include "libv4lconvert-priv.h"
+
+#define DO_SANITY_CHECKS 0
+
+static const int UVTranslate[32] = {0, 1, 2, 3,
+ 8, 9, 10, 11,
+ 16, 17, 18, 19,
+ 24, 25, 26, 27,
+ 4, 5, 6, 7,
+ 12, 13, 14, 15,
+ 20, 21, 22, 23,
+ 28, 29, 30, 31};
+
+static const int Y_coords_624x[128][2] = {
+{ 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 4, 0}, { 5, 0}, { 6, 0}, { 7, 0},
+{ 0, 1}, { 1, 1}, { 2, 1}, { 3, 1}, { 4, 1}, { 5, 1}, { 6, 1}, { 7, 1},
+{ 0, 2}, { 1, 2}, { 2, 2}, { 3, 2}, { 4, 2}, { 5, 2}, { 6, 2}, { 7, 2},
+{ 0, 3}, { 1, 3}, { 2, 3}, { 3, 3}, { 4, 3}, { 5, 3}, { 6, 3}, { 7, 3},
+
+{ 0, 4}, { 1, 4}, { 2, 4}, { 3, 4}, { 4, 4}, { 5, 4}, { 6, 4}, { 7, 4},
+{ 0, 5}, { 1, 5}, { 2, 5}, { 3, 5}, { 4, 5}, { 5, 5}, { 6, 5}, { 7, 5},
+{ 0, 6}, { 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6}, { 7, 6},
+{ 0, 7}, { 1, 7}, { 2, 7}, { 3, 7}, { 4, 7}, { 5, 7}, { 6, 7}, { 7, 7},
+
+{ 8, 0}, { 9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0},
+{ 8, 1}, { 9, 1}, {10, 1}, {11, 1}, {12, 1}, {13, 1}, {14, 1}, {15, 1},
+{ 8, 2}, { 9, 2}, {10, 2}, {11, 2}, {12, 2}, {13, 2}, {14, 2}, {15, 2},
+{ 8, 3}, { 9, 3}, {10, 3}, {11, 3}, {12, 3}, {13, 3}, {14, 3}, {15, 3},
+
+{ 8, 4}, { 9, 4}, {10, 4}, {11, 4}, {12, 4}, {13, 4}, {14, 4}, {15, 4},
+{ 8, 5}, { 9, 5}, {10, 5}, {11, 5}, {12, 5}, {13, 5}, {14, 5}, {15, 5},
+{ 8, 6}, { 9, 6}, {10, 6}, {11, 6}, {12, 6}, {13, 6}, {14, 6}, {15, 6},
+{ 8, 7}, { 9, 7}, {10, 7}, {11, 7}, {12, 7}, {13, 7}, {14, 7}, {15, 7}
+};
+
+static void do_write_u(const unsigned char *buf, unsigned char *ptr,
+ int i, int j)
+{
+ *ptr = buf[i + 128 + j];
+}
+
+static void do_write_v(const unsigned char *buf, unsigned char *ptr,
+ int i, int j)
+{
+ *ptr = buf[i + 160 + j];
+}
+
+void v4lconvert_sn9c20x_to_yuv420(const unsigned char *raw, unsigned char *i420,
+ int width, int height, int yvu)
+{
+ int i = 0, x = 0, y = 0, j, relX, relY, x_div2, y_div2;
+ const unsigned char *buf = raw;
+ unsigned char *ptr;
+ int frame_size = width * height;
+ int frame_size_div2 = frame_size >> 1;
+ int frame_size_div4 = frame_size >> 2;
+ int width_div2 = width >> 1;
+ int height_div2 = height >> 1;
+ void (*do_write_uv1)(const unsigned char *buf, unsigned char *ptr, int i,
+ int j) = NULL;
+ void (*do_write_uv2)(const unsigned char *buf, unsigned char *ptr, int i,
+ int j) = NULL;
+
+ if (yvu) {
+ do_write_uv1 = do_write_v;
+ do_write_uv2 = do_write_u;
+ }
+ else {
+ do_write_uv1 = do_write_u;
+ do_write_uv2 = do_write_v;
+ }
+
+ while (i < (frame_size + frame_size_div2)) {
+ for (j = 0; j < 128; j++) {
+ relX = x + Y_coords_624x[j][0];
+ relY = y + Y_coords_624x[j][1];
+
+#if (DO_SANITY_CHECKS==1)
+ if ((relX < width) && (relY < height)) {
+#endif
+ ptr = i420 + relY * width + relX;
+ *ptr = buf[i + j];
+#if (DO_SANITY_CHECKS==1)
+ }
+#endif
+
+ }
+ x_div2 = x >> 1;
+ y_div2 = y >> 1;
+ for (j = 0; j < 32; j++) {
+ relX = (x_div2) + (j & 0x07);
+ relY = (y_div2) + (j >> 3);
+
+#if (DO_SANITY_CHECKS==1)
+ if ((relX < width_div2) && (relY < height_div2)) {
+#endif
+ ptr = i420 + frame_size +
+ relY * width_div2 + relX;
+ do_write_uv1(buf, ptr, i, j);
+ ptr += frame_size_div4;
+ do_write_uv2(buf, ptr, i, j);
+#if (DO_SANITY_CHECKS==1)
+ }
+#endif
+ }
+
+ i += 192;
+ x += 16;
+ if (x >= width) {
+ x = 0;
+ y += 8;
+ }
+ }
+}
diff --git a/v4l2-apps/libv4l/libv4lconvert/spca501.c b/v4l2-apps/libv4l/libv4lconvert/spca501.c
index 9157629e3..f491512e3 100644
--- a/v4l2-apps/libv4l/libv4lconvert/spca501.c
+++ b/v4l2-apps/libv4l/libv4lconvert/spca501.c
@@ -20,7 +20,7 @@
/* YUYV per line */
void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
unsigned long *lsrc = (unsigned long *)src;
@@ -34,7 +34,10 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line U */
- ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
@@ -48,7 +51,10 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line V */
- ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
@@ -58,7 +64,7 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
/* YYUV per line */
void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
unsigned long *lsrc = (unsigned long *)src;
@@ -72,14 +78,20 @@ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line U */
- ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
}
/* -128 - 127 --> 0 - 255 and copy 1 line V */
- ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
@@ -89,7 +101,7 @@ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
/* YUVY per line */
void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst,
- int width, int height)
+ int width, int height, int yvu)
{
int i,j;
unsigned long *lsrc = (unsigned long *)src;
@@ -103,14 +115,20 @@ void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst,
}
/* -128 - 127 --> 0 - 255 and copy 1 line U */
- ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
}
/* -128 - 127 --> 0 - 255 and copy 1 line V */
- ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
+ if (yvu)
+ ldst = (unsigned long *)(dst + width * height + i * width / 4);
+ else
+ ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4);
for (j = 0; j < width/2; j += sizeof(long)) {
*ldst = *lsrc++;
*ldst++ ^= 0x8080808080808080ULL;
diff --git a/v4l2-apps/libv4l/libv4lconvert/sq905c.c b/v4l2-apps/libv4l/libv4lconvert/sq905c.c
new file mode 100644
index 000000000..a73b4da93
--- /dev/null
+++ b/v4l2-apps/libv4l/libv4lconvert/sq905c.c
@@ -0,0 +1,217 @@
+/*
+ * sq905c.c
+ *
+ * Here is the decompression function for the SQ905C cameras. The functions
+ * used are adapted from the libgphoto2 functions for the same cameras,
+ * which was
+ * Copyright (c) 2005 and 2007 Theodore Kilgore <kilgota@auburn.edu>
+ * This version for libv4lconvert is
+ * Copyright (c) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * 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 <stdlib.h>
+
+#include "libv4lconvert-priv.h"
+
+
+#define CLIP(x) ((x) < 0 ? 0 : ((x) > 0xff) ? 0xff : (x))
+
+
+static int
+sq905c_first_decompress(unsigned char *output, const unsigned char *input,
+ unsigned int outputsize)
+{
+ unsigned char parity = 0;
+ unsigned char nibble_to_keep[2];
+ unsigned char temp1 = 0, temp2 = 0;
+ unsigned char input_byte;
+ unsigned char lookup = 0;
+ unsigned int i = 0;
+ unsigned int bytes_used = 0;
+ unsigned int bytes_done = 0;
+ unsigned int bit_counter = 8;
+ unsigned int cycles = 0;
+ int table[9] = { -1, 0, 2, 6, 0x0e, 0x0e, 0x0e, 0x0e, 0xfb};
+ unsigned char lookup_table[16]
+ = {0, 2, 6, 0x0e, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb};
+ unsigned char translator[16] = {8, 7, 9, 6, 10, 11, 12, 13,
+ 14, 15, 5, 4, 3, 2, 1, 0};
+
+ nibble_to_keep[0] = 0;
+ nibble_to_keep[1] = 0;
+
+ while (bytes_done < outputsize) {
+ while (parity < 2) {
+ while (lookup > table[cycles]) {
+ if (bit_counter == 8) {
+ input_byte = input[bytes_used];
+ bytes_used++;
+ temp1 = input_byte;
+ bit_counter = 0;
+ }
+ input_byte = temp1;
+ temp2 = (temp2 << 1) & 0xFF;
+ input_byte = input_byte >> 7;
+ temp2 = temp2 | input_byte;
+ temp1 = (temp1 << 1) & 0xFF;
+ bit_counter++;
+ cycles++;
+ if (cycles > 9)
+ return -1;
+ lookup = temp2 & 0xff;
+ }
+ temp2 = 0;
+ for (i = 0; i < 17; i++) {
+ if (lookup == lookup_table[i]) {
+ nibble_to_keep[parity] = translator[i];
+ break;
+ }
+ if (i == 16)
+ return -1;
+ }
+ cycles = 0;
+ parity++;
+ }
+ output[bytes_done] = (nibble_to_keep[0]<<4)|nibble_to_keep[1];
+ bytes_done++;
+ parity = 0;
+ }
+ return 0;
+}
+
+static int
+sq905c_second_decompress(unsigned char *uncomp, unsigned char *in,
+ int width, int height)
+{
+ int diff = 0;
+ int tempval = 0;
+ int i, m, parity;
+ unsigned char delta_left = 0;
+ unsigned char delta_right = 0;
+ int input_counter = 0;
+ int delta_table[] = {-144, -110, -77, -53, -35, -21, -11, -3,
+ 2, 10, 20, 34, 52, 76, 110, 144};
+ unsigned char *templine_red;
+ unsigned char *templine_green;
+ unsigned char *templine_blue;
+ templine_red = malloc(width);
+ if (!templine_red) {
+ free(templine_red);
+ return -1;
+ }
+ for (i = 0; i < width; i++)
+ templine_red[i] = 0x80;
+ templine_green = malloc(width);
+ if (!templine_green) {
+ free(templine_green);
+ return -1;
+ }
+ for (i = 0; i < width; i++)
+ templine_green[i] = 0x80;
+ templine_blue = malloc(width);
+ if (!templine_blue) {
+ free(templine_blue);
+ return -1;
+ }
+ for (i = 0; i < width; i++)
+ templine_blue[i] = 0x80;
+ for (m = 0; m < height/2; m++) {
+ /* First we do an even-numbered line */
+ for (i = 0; i < width/2; i++) {
+ parity = i&1;
+ delta_right = in[input_counter] & 0x0f;
+ delta_left = (in[input_counter]>>4)&0xff;
+ input_counter++;
+ /* left pixel (red) */
+ diff = delta_table[delta_left];
+ if (!i)
+ tempval = templine_red[0] + diff;
+ else
+ tempval = (templine_red[i]
+ + uncomp[2*m*width+2*i-2])/2 + diff;
+ tempval = CLIP(tempval);
+ uncomp[2*m*width+2*i] = tempval;
+ templine_red[i] = tempval;
+ /* right pixel (green) */
+ diff = delta_table[delta_right];
+ if (!i)
+ tempval = templine_green[1] + diff;
+ else if (2*i == width - 2)
+ tempval = (templine_green[i]
+ + uncomp[2*m*width+2*i-1])/2
+ + diff;
+ else
+ tempval = (templine_green[i+1]
+ + uncomp[2*m*width+2*i-1])/2
+ + diff;
+ tempval = CLIP(tempval);
+ uncomp[2*m*width+2*i+1] = tempval;
+ templine_green[i] = tempval;
+ }
+ /* then an odd-numbered line */
+ for (i = 0; i < width/2; i++) {
+ delta_right = in[input_counter] & 0x0f;
+ delta_left = (in[input_counter]>>4) & 0xff;
+ input_counter++;
+ /* left pixel (green) */
+ diff = delta_table[delta_left];
+ if (!i)
+ tempval = templine_green[0] + diff;
+ else
+ tempval = (templine_green[i]
+ + uncomp[(2*m+1)*width+2*i-2])/2
+ + diff;
+ tempval = CLIP(tempval);
+ uncomp[(2*m+1)*width+2*i] = tempval;
+ templine_green[i] = tempval;
+ /* right pixel (blue) */
+ diff = delta_table[delta_right];
+ if (!i)
+ tempval = templine_blue[0] + diff;
+ else
+ tempval = (templine_blue[i]
+ + uncomp[(2*m+1)*width+2*i-1])/2
+ + diff;
+ tempval = CLIP(tempval);
+ uncomp[(2*m+1)*width+2*i+1] = tempval;
+ templine_blue[i] = tempval;
+ }
+ }
+ free(templine_green);
+ free(templine_red);
+ free(templine_blue);
+ return 0;
+}
+
+void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst,
+ int width, int height)
+{
+ int size;
+ unsigned char *temp_data;
+ const unsigned char *raw;
+ /* here we get rid of the 0x50 bytes of header in src. */
+ raw = src + 0x50;
+ size = width*height/2;
+ temp_data = malloc(size);
+ if (!temp_data)
+ goto out;
+ sq905c_first_decompress(temp_data, raw, size);
+ sq905c_second_decompress(dst, temp_data, width, height);
+out:
+ free(temp_data);
+}