[meego-commits] 5410: Changes to Trunk:Testing/libv4l

Bin Gao bin.gao at intel.com
Thu Jul 8 03:00:20 UTC 2010


Hi,
I have made the following changes to libv4l in project Trunk:Testing. Please review and accept ASAP.

Thank You,
Bin Gao

[This message was auto-generated]

---

Request #5410:

  submit:   home:bgao1:branches:Trunk:Test/libv4l(r2) -> Trunk:Testing/libv4l


Message:
    Update to 0.6.4

State:   new          2010-07-07T14:55:55 bgao1
Comment: None



changes files:
--------------
--- libv4l.changes
+++ libv4l.changes
@@ -0,0 +1,3 @@
+* Thu Jul 08 2010 Bin Gao <bin.gao at intel.com> 0.6.4
+- Update to 0.6.4
+

old:
----
  libv4l-0.5.9.tar.gz

new:
----
  libv4l-0.6.4.tar.gz

spec files:
-----------
--- libv4l.spec
+++ libv4l.spec
@@ -1,11 +1,11 @@
 Name:           libv4l
-Version:        0.5.9
+Version:        0.6.4
 Release:        1
 Summary:        Collection of video4linux support libraries 
 Group:          System/Libraries
 License:        LGPLv2+
 URL:            http://hansdegoede.livejournal.com/3636.html
-Source0:        http://people.atrpms.net/~hdegoede/%{name}-%{version}.tar.gz
+Source0:		http://people.fedoraproject.org/~jwrdegoede/%{name}-%{version}.tar.gz
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildRequires:  kernel-headers
 

other changes:
--------------

++++++ libv4l-0.5.9.tar.gz -> libv4l-0.6.4.tar.gz
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,115 @@
+libv4l-0.6.4
+------------
+* Add more laptop models to the upside down devices table
+* Add error checking to mr97310a decompression
+* Increase mr97310a minimum clockdiv upon 3 consecutive decoding errors
+* Add support for decompressing CPIA1 compressed YUV
+* Speed up autogain algorithm
+
+libv4l-0.6.3
+------------
+* Add more laptop models to the upside down devices table
+* Improved mr97310a decompression
+* Add support for decompressing yuv420 planar JPEG (one component per SOS,
+  3 SOS per frame), this is needed for w9968cf based cams
+* Add support for STV0680 raw bayer data
+
+libv4l-0.6.2
+------------
+* Add more laptop models to the upside down devices table
+* Put usb id in controls shm segment name for USB devices, to better
+  distuingish between devices plugged into the same port
+* Enable software whitebalance and autogain for mr97310a cameras
+* Improvements / tweaks to software autogain algorithm
+
+libv4l-0.6.1
+------------
+* Add more laptop models to the upside down devices table
+* Makefile changes to make life easier for the Debian package (Gregor Jasny)
+* Bugfix: fixup 320x240 output for pac7302 cameras
+* README improvements / clarifications (Bifferos)
+* Bugfix: fix reqbuf Device or Resource busy error when using v4l2_read()
+* Some applications want to use jpg format if possible, so do not hide
+  it from the apps (do not assume it always needs conversion)
+* Change controls shm segment name to include the username, as it is only
+  writable by the user (this means libv4l controls are per user) (Gregor Jasny)
+* Add support for decompressing sn9c2028 compressed bayer (Theodore Kilgore)
+* Report V4L2_FMT_FLAG_EMULATED in v4l2_fmtdesc flags for emulated formats
+
+libv4l-0.6.0
+------------
+* Recognize disabled controls and replace with fake equivalents where
+  available
+* Add support for decompressing ov511 and ov518 "JPEG", by piping data through
+  an external helper as I've failed to contact Mark W. McClelland to get
+  permission to relicense the code. If you know a working email address for
+  Mark W. McClelland, please let me know.
+* Add tons of laptop models to the upside down devices table
+* Support for rgb565 source format by Mauro Carvalho Chehab
+* Many bug fixes (see the mercurial tree for details)
+* Improved pac207 decompression code to also support higher compression
+  modes of the pac207, which enables us to use higher framerates.
+  Many many thanks to Bertrik Sikken for figuring the decompression out!
+
+libv4l-0.5.99
+-------------
+* Link libv4lconvert with -lm for powf by Gregor Jasny
+* Fix black screen on devices with hardware gamma control
+* Fix crash with devices on which we do not emulate fake controls
+* Add a patch by Hans Petter Selasky <hselasky at freebsd.org>, which should
+  lead to allowing use of libv4l (and the Linux webcam drivers ported
+  to userspace usb drivers) on FreeBSD, this is a work in progress
+
+libv4l-0.5.98
+-------------
+* Add software gamma correction
+* Add software auto gain / exposure
+* Add support for separate vflipping and hflipping
+* Add fake controls controlling the software h- and v-flipping
+* Add ability to determine upside down cams based on DMI info
+* Add the capability to provide 320x240 to apps if the cam can only
+  do 320x232 (some zc3xx cams) by adding black borders
+* Rewrite video processing code to make it easier to add more video filters
+  (and with little extra processing cost). As part of this the normalize
+  filter has been removed as it wasn't functioning satisfactory anyways
+* Support V4L2_CTRL_FLAG_NEXT_CTRL for fake controls by Adam Baker
+* Some makefile improvements by Gregor Jasny
+* Various small bugfixes and tweaks
+* The V4L2_ENABLE_ENUM_FMT_EMULATION v4l2_fd_open flag is obsolete, libv4l2
+  now *always* reports emulated formats through the ENUM_FMT ioctl
+
+libv4l-0.5.97
+-------------
+* As the version number shows this is a beta release of the 0.6.x series,
+  the big change here is the addition of video processing to libv4l
+  currently this only does whitebalance and normalizing (which turns out
+  to be useless for most cams) but the basic framework for doing video
+  processing, and being able to control it through fake v4l2 controls using
+  for example v4l2ucp is there.
+  The initial version of this code was written by 3 of my computer science
+  students: Elmar Kleijn, Sjoerd Piepenbrink and Radjnies Bhansingh
+* Currently whitebalancing gets enabled based on USB-ID's and it only gets
+  enabled for Pixart webcam's. You can force it being enabled with other
+  webcams by setting the environment variable LIBV4LCONTROL_CONTROLS, this
+  sets a bitmask enabling certain v4l2 controls which control the video
+  processing set it to 15 to enable both whitebalancing and normalize. You
+  can then change the settings using a v4l2 control panel like v4l2ucp
+* Only report / allow supported destination formats in enum_fmt / try_fmt /
+  g_fmt / s_fmt when processing, rotating or flipping.
+* Some applications / libs (*cough* gstreamer *cough*) will not work
+  correctly with planar YUV formats when the width is not a multiple of 8,
+  so crop widths which are not a multiple of 8 to the nearest multiple of 8
+  when converting to planar YUV
+* Add dependency generation to libv4l by: Gilles Gigan <gilles.gigan at gmail.com>
+* Add support to use orientation from VIDIOC_ENUMINPUT by:
+  Adam Baker <linux at baker-net.org.uk>
+* sn9c20x cams have occasional bad jpeg frames, drop these to avoid the
+  flickering effect they cause, by: Brian Johnson <brijohn at gmail.com>
+* adjust libv4l's upside down cam detection to also work with devices
+  which have the usb interface as parent instead of the usb device
+* fix libv4l upside down detection for the new v4l minor numbering scheme
+* fix reading outside of the source memory when doing yuv420->rgb conversion
+
 libv4l-0.5.9
 ------------
 * Add support for MR97310A decompression by Kyle Guinn <elyk03 at gmail.com>
--- Makefile
+++ Makefile
@@ -1,5 +1,5 @@
 LIB_RELEASE=0
-V4L2_LIB_VERSION=$(LIB_RELEASE).5.9
+V4L2_LIB_VERSION=$(LIB_RELEASE).6.4
 
 all install:
 	$(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@
@@ -7,7 +7,7 @@
 	$(MAKE) -C libv4l1 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@
 
 clean:
-	rm -f *~ include/*~
+	rm -f *~ include/*~ DEADJOE include/DEADJOE
 	$(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@
 	$(MAKE) -C libv4l2 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@
 	$(MAKE) -C libv4l1 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@
--- README
+++ README
@@ -15,14 +15,26 @@
 libv4lconvert
 -------------
 
-libv4lconvert offers functions to convert from any (known) pixelformat
-to V4l2_PIX_FMT_BGR24 or V4l2_PIX_FMT_YUV420.
+libv4lconvert started as a library to convert from any (known) pixelformat to
+V4l2_PIX_FMT_BGR24, RGB24, YUV420 or YVU420.
 
-Currently the following source formats are supported:
-jpeg, mjpeg, bayer (all 4 variants: bggr, rggb, gbrg, grbg),
-spca501 (chip specific yuv 420 with interlaced components),
-spca561 (chip specific compressed gbrg bayer)
-For more details on the v4lconvert_ functions see libv4lconvert.h .
+The list of know source formats is large and continually growing, so instead
+of keeping an (almost always outdated) list here in the README, I refer you
+to the source, see the list of defines at the top of
+libv4lconvert/libv4lconvert.c for the full list.
+For more details on the v4lconvert_ functions see libv4lconvert.h.
+
+Later on libv4lconvert was expanded to also be able to do various video
+processing functions to improve webcam video quality on a software basis. So
+the name no longer 100% covers the functionality. The video processing is
+split in to 2 parts, libv4lconvert/control and libv4lconvert/processing.
+
+The control part is used to offer video controls which can be used to control
+the video processing functions made available by libv4lconvert/processing.
+These controls are stored application wide (until reboot) by using a
+persistent shared memory object.
+
+libv4lconvert/processing offers the actual video processing functionality.
 
 
 libv4l1
@@ -68,6 +80,12 @@
 $ camorama
 
 
+Prerequisites
+-------------
+
+libv4l requires shmem file system support in the kernel (CONFIG_SHMEM).
+
+
 Installation Instructions
 -------------------------
 
@@ -76,9 +94,15 @@
 make
 make install PREFIX=/usr/local
 
-Note: make install also supports the DESTDIR=... paramter for installation
+Note: make install also supports the DESTDIR=... parameter for installation
 into chroots.
 
+If you require static libraries to also be built, these can be compiled
+along with the dynamic equivalents by defining LINKTYPE to 'static', e.g.:
+
+make LINKTYPE=static
+make install LINKTYPE=static
+
 
 FAQ
 ---
@@ -105,10 +129,13 @@
 allows this code to be used from as many different applications as possible.
 Hence libv4l was born.
 
+
 Q: Under which license may I use and distribute libv4l?
-A: All libv4l components are licensed under the GNU Library General Publishing
+A: The libv4l libraries are licensed under the GNU Library General Publishing
 License version 2 or (at your option) any later version. See the included
-COPYING.LIB file.
+COPYING.LIB file. The decompression helpers are licensed under the GNU
+Library Publishing License version 2 (as they are derived from kernel code)
+
 
 Q: Okay so I get the use of having a libv4lconvert, but why libv4l1 ?
 A: Many v4l2 drivers do not offer full v4l1 compatibility. They often do not
@@ -126,9 +153,8 @@
 
 
 Q: Why should I use libv4l2 in my app instead of direct device access
-combined with libv4lconvert?
-
-libv4l2 is mainly meant for quickly and easily adding support for more
+   combined with libv4lconvert?
+A: libv4l2 is mainly meant for quickly and easily adding support for more
 pixelformats to existing v4l2 applications. So if you feel better directly
 accessing the device in combination with libv4lconvert thats fine too.
 
@@ -139,3 +165,11 @@
 data directly to the buffer the application provided to v4l2_read(). Thus
 another reason to use liv4l2 is to get the no memcpy advantage of the mmap
 capture method combined with the simplicity of making a simple read() call.
+
+
+Q: Where to send bugreports / questions?
+A: Please send libv4l questions / bugreports to the:
+   Linux Media Mailing List <linux-media at vger.kernel.org>
+   Subscription is not necessary to send mail to this list. If you're not
+   subscribed please put yourself in the CC of your original mail so you
+   will receive replies.
--- TODO
+++ TODO
@@ -8,3 +8,8 @@
  necessary to implement CGMBUF in the kernel for each driver.
 
 -take the possibility of pitch != width into account everywhere
+
+-make updating of parameters happen based on time elapsed rather then
+ frames
+
+-get standardized CID for AUTOGAIN_TARGET upstream and switch to that
--- include/libv4l1.h
+++ include/libv4l1.h
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -24,6 +24,7 @@
 #endif /* __cplusplus */
 
 #include <stdio.h>
+#include <stdint.h>
 #include <unistd.h>
 
 #if __GNUC__ >= 4
@@ -63,7 +64,7 @@
 LIBV4L_PUBLIC int v4l1_ioctl (int fd, unsigned long int request, ...);
 LIBV4L_PUBLIC ssize_t v4l1_read (int fd, void* buffer, size_t n);
 LIBV4L_PUBLIC void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd,
-  __off64_t offset);
+  int64_t offset);
 LIBV4L_PUBLIC int v4l1_munmap(void *_start, size_t length);
 
 #ifdef __cplusplus
--- include/libv4l2.h
+++ include/libv4l2.h
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -21,6 +21,7 @@
 
 #include <stdio.h>
 #include <unistd.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -41,21 +42,22 @@
    format which is not supported by the cam, but is supported by libv4lconvert,
    then the try_fmt / set_fmt will succeed as if the cam supports the format
    and on dqbuf / read the data will be converted for you and returned in
-   the request format.
+   the request format. enum_fmt will also report support for the formats to
+   which conversion is possible.
 
    Another difference is that you can make v4l2_read() calls even on devices
    which do not support the regular read() method.
 
-   Note that libv4l2 normally does not interfere with enum_fmt, so enum_fmt
-   will still return the actual formats the hardware supports, and not any
-   formats which may be emulated on top of that. If you pass the
-   V4L2_ENABLE_ENUM_FMT_EMULATION flag to v4l2_fd_open (as the v4l2convert.so
-   wrapper does) then enum_fmt will also report support for the formats to
-   which conversion is possible.
-
    Note the device name passed to v4l2_open must be of a video4linux2 device,
    if it is anything else (including a video4linux1 device), v4l2_open will
    fail.
+
+   Note that the argument to v4l2_ioctl after the request must be a valid
+   memory address of structure of the appropriate type for the request (for
+   v4l2 requests which expect a structure address). Passing in NULL or an
+   invalid memory address will not lead to failure with errno being EFAULT,
+   as it would with a real ioctl, but will cause libv4l2 to break, and you
+   get to keep both pieces.
 */
 
 LIBV4L_PUBLIC int v4l2_open (const char *file, int oflag, ...);
@@ -64,7 +66,7 @@
 LIBV4L_PUBLIC int v4l2_ioctl (int fd, unsigned long int request, ...);
 LIBV4L_PUBLIC ssize_t v4l2_read (int fd, void* buffer, size_t n);
 LIBV4L_PUBLIC void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd,
-  __off64_t offset);
+  int64_t offset);
 LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length);
 
 
@@ -89,11 +91,14 @@
 
 /* Flags for v4l2_fd_open's v4l2_flags argument */
 
-/* Disable all format conversion done by libv4l2 (reduces libv4l2 functionality
-   to offering v4l2_read() even on devices which don't implement read()) */
+/* Disable all format conversion done by libv4l2, this includes the software
+   whitebalance, gamma correction, flipping, etc. libv4lconvert does. Use this
+   if you want raw frame data, but still want the additional error checks and
+   the read() emulation libv4l2 offers. */
 #define V4L2_DISABLE_CONVERSION 0x01
-/* Report not only real but also emulated formats with the ENUM_FMT ioctl */
-#define V4L2_ENABLE_ENUM_FMT_EMULATION 02
+/* This flag is *OBSOLETE*, since version 0.5.98 libv4l *always* reports
+   emulated formats to ENUM_FMT, except when conversion is disabled. */
+#define V4L2_ENABLE_ENUM_FMT_EMULATION 0x02
 
 /* v4l2_fd_open: open an already opened fd for further use through
    v4l2lib and possibly modify libv4l2's default behavior through the
--- include/libv4lconvert.h
+++ include/libv4lconvert.h
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -21,10 +21,21 @@
 
 /* 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 <linux/types.h>
 #include <linux/ioctl.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#endif
+
 /* end broken header workaround includes */
+
 #include <linux/videodev2.h>
 
 #ifdef __cplusplus
@@ -42,18 +53,30 @@
 LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd);
 LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data);
 
+/* When doing flipping / rotating / video-processing, only supported
+   destination formats can be used (as flipping / rotating / video-processing
+   is not supported on other formats). This function can be used to query
+   if that is the case. */
+LIBV4L_PUBLIC int v4lconvert_supported_dst_fmt_only(
+  struct v4lconvert_data *data);
+
 /* With regards to dest_fmt just like VIDIOC_TRY_FMT, except that the try
    format will succeed and return the requested V4L2_PIX_FMT_foo in dest_fmt if
    the cam has a format from which v4lconvert can convert to dest_fmt.
    The real format to which the cam should be set is returned through src_fmt
-   when not NULL. */
+   when not NULL.
+   Note that just like the real VIDIOC_TRY_FMT this function will change the
+   dest_fmt when not supported. This includes changing it to a supported
+   destination format when trying a native format of the camera and
+   v4lconvert_supported_dst_fmt_only() returns true. */
 LIBV4L_PUBLIC int v4lconvert_try_format(struct v4lconvert_data *data,
   struct v4l2_format *dest_fmt, /* in / out */
   struct v4l2_format *src_fmt /* out */
 );
 
-/* Just like VIDIOC_ENUM_FMT, except that the emulated formats are added at
-   the end of the list */
+/* Like VIDIOC_ENUM_FMT, but the emulated formats are added at the end of the
+   list, except if flipping / processing is active for the device, then only
+   supported destination formats are listed */
 LIBV4L_PUBLIC int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt);
 
 /* Is conversion necessary or can the app use the data directly? */
@@ -81,6 +104,17 @@
 LIBV4L_PUBLIC int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
   struct v4l2_frmivalenum *frmival);
 
+/* Pass calls to query, get and set video controls to the libv4lcontrol class */
+LIBV4L_PUBLIC int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data,
+  void *arg);
+LIBV4L_PUBLIC int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data,
+  void *arg);
+LIBV4L_PUBLIC int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data,
+  void *arg);
+
+/* Is the passed in pixelformat supported as destination format ? */
+LIBV4L_PUBLIC int v4lconvert_supported_dst_format(unsigned int pixelformat);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
--- libv4l1/Makefile
+++ libv4l1/Makefile
@@ -13,9 +13,10 @@
 
 ifeq ($(LINKTYPE),static)
 V4L1_LIB      = libv4l1.a
+V4L1_DEPS     = $(V4L1_OBJS)
 else
 V4L1_LIB      = libv4l1.so
-V4L1_OBJS    += ../libv4l2/libv4l2.so
+V4L1_DEPS    += $(V4L1_OBJS) ../libv4l2/libv4l2.so
 TARGETS      += $(V4L1COMPAT)
 override CPPFLAGS += -fPIC
 endif
@@ -32,10 +33,15 @@
 LIBDIR = $(PREFIX)/lib
 endif
 
+ifeq ($(LIBSUBDIR),)
+LIBSUBDIR = libv4l
+endif
+
 all: $(TARGETS)
 
+-include $(V4L1_OBJS:.o=.d)
 
-$(V4L1_LIB): $(V4L1_OBJS)
+$(V4L1_LIB): $(V4L1_DEPS)
 
 $(V4L1COMPAT): $(V4L1COMPAT_O) $(V4L1_LIB)
 
@@ -58,21 +64,21 @@
 	mkdir -p $(DESTDIR)$(LIBDIR)
 	install -m 644 $(V4L1_LIB) $(DESTDIR)$(LIBDIR)
 else
-	mkdir -p $(DESTDIR)$(LIBDIR)/libv4l
+	mkdir -p $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR)
 	install -m 755 $(V4L1_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR)
 	cd $(DESTDIR)$(LIBDIR) && \
 	  ln -f -s $(V4L1_LIB).$(LIB_RELEASE) $(V4L1_LIB)
 	install -m 755 $(V4L1COMPAT).$(LIB_RELEASE) \
-	  $(DESTDIR)$(LIBDIR)/libv4l/$(V4L1COMPAT)
+	  $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR)/$(V4L1COMPAT)
 endif
 	mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig
 	install -m 644 libv4l1.pc $(DESTDIR)$(LIBDIR)/pkgconfig
 
 clean::
-	rm -f *.a *.so* *.o *.d libv4l1.pc log *~ *.orig *.rej
+	rm -f *.a *.so* *.o *.d libv4l1.pc log *~ *.orig *.rej DEADJOE
 
 %.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) $^ $(LIBS_$*)
--- libv4l1/libv4l1-priv.h
+++ libv4l1/libv4l1-priv.h
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -22,14 +22,7 @@
 #include <stdio.h>
 #include <pthread.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
+#include "../libv4lconvert/libv4lsyscall-priv.h"
 
 #define V4L1_MAX_DEVICES 16
 #define V4L1_NO_FRAMES 4
--- libv4l1/libv4l1.c
+++ libv4l1/libv4l1.c
@@ -1,7 +1,7 @@
 /*
 # libv4l1 userspace v4l1 api emulation for v4l2 devices
 
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -44,18 +44,12 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <syscall.h>
 #include <fcntl.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/mman.h>
-/* These headers are not needed by us, but by linux/videodev2.h,
-   which is broken on some systems and doesn't include them itself :( */
-#include <sys/time.h>
-#include <asm/types.h>
-#include <linux/ioctl.h>
-/* end broken header workaround includes */
+#include "../libv4lconvert/libv4lsyscall-priv.h"
 #include <linux/videodev.h>
 #include <linux/videodev2.h>
 #include <libv4l2.h>
@@ -155,8 +149,10 @@
 
   /* Do we need to change the resolution / format ? */
   if (width == devices[index].width && height == devices[index].height &&
-      v4l2_pixfmt == devices[index].v4l2_pixfmt)
+      v4l2_pixfmt == devices[index].v4l2_pixfmt) {
+    devices[index].v4l1_pal = v4l1_pal;
     return 0;
+  }
 
   /* Get current settings, apply our changes and try the new setting */
   if ((result = v4l2_ioctl(devices[index].fd, VIDIOC_G_FMT, &fmt2))) {
@@ -223,14 +219,14 @@
   for (i = 0; ; i++) {
     fmtdesc2.index = i;
 
-    if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_ENUM_FMT, &fmtdesc2))
+    if (v4l2_ioctl(devices[index].fd, VIDIOC_ENUM_FMT, &fmtdesc2))
       break;
 
     fmt2->fmt.pix.pixelformat = fmtdesc2.pixelformat;
     fmt2->fmt.pix.width = 48;
     fmt2->fmt.pix.height = 32;
 
-    if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) {
+    if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) {
       if (fmt2->fmt.pix.width < devices[index].min_width)
 	devices[index].min_width = fmt2->fmt.pix.width;
       if (fmt2->fmt.pix.height < devices[index].min_height)
@@ -241,7 +237,7 @@
     fmt2->fmt.pix.width = 100000;
     fmt2->fmt.pix.height = 100000;
 
-    if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) {
+    if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) {
       if (fmt2->fmt.pix.width > devices[index].max_width)
 	devices[index].max_width = fmt2->fmt.pix.width;
       if (fmt2->fmt.pix.height > devices[index].max_height)
@@ -278,11 +274,12 @@
     va_start (ap, oflag);
     mode = va_arg (ap, mode_t);
 
-    fd = syscall(SYS_open, file, oflag, mode);
+    fd = SYS_OPEN(file, oflag, mode);
 
     va_end(ap);
   } else
-    fd = syscall(SYS_open, file, oflag);
+    fd = SYS_OPEN(file, oflag, 0);
+
   /* end of original open code */
 
   if (fd == -1 || !v4l_device)
@@ -290,7 +287,7 @@
 
   /* check that this is an v4l2 device, no need to emulate v4l1 on
      a v4l1 device */
-  if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap2))
+  if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap2))
     return fd;
 
   /* IMPROVEME */
@@ -308,21 +305,20 @@
   if (!v4l2_log_file)
     v4l2_log_file = v4l1_log_file;
 
-  /* Get initial width, height and pixelformat */
-  fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-  if (syscall(SYS_ioctl, fd, VIDIOC_G_FMT, &fmt2)) {
+  /* Register with libv4l2, as we use that todo format conversion and read()
+     emulation for us */
+  if (v4l2_fd_open(fd, 0) == -1) {
     int saved_err = errno;
-    V4L1_LOG_ERR("getting pixformat: %s\n", strerror(errno));
-    syscall(SYS_close, fd);
+    SYS_CLOSE(fd);
     errno = saved_err;
     return -1;
   }
 
-  /* Register with libv4l2, as we use that todo format conversion and read()
-     emulation for us */
-  if (v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION) == -1) {
+  /* Get initial width, height and pixelformat */
+  fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  if (v4l2_ioctl(fd, VIDIOC_G_FMT, &fmt2)) {
     int saved_err = errno;
-    syscall(SYS_close, fd);
+    SYS_CLOSE(fd);
     errno = saved_err;
     return -1;
   }
@@ -399,7 +395,7 @@
   int index, result;
 
   if ((index = v4l1_get_index(fd)) == -1)
-    return syscall(SYS_close, fd);
+    return SYS_CLOSE(fd);
 
   /* Abuse stream_lock to stop 2 closes from racing and trying to free the
      resources twice */
@@ -417,7 +413,7 @@
       V4L1_LOG("v4l1 capture buffer still mapped: %d times on close()\n",
 	devices[index].v4l1_frame_buf_map_count);
     else
-      syscall(SYS_munmap, devices[index].v4l1_frame_pointer,
+      SYS_MUNMAP(devices[index].v4l1_frame_pointer,
 	      V4L1_NO_FRAMES * V4L1_FRAME_BUF_SIZE);
     devices[index].v4l1_frame_pointer = MAP_FAILED;
   }
@@ -446,7 +442,6 @@
   return v4l2_dup(fd);
 }
 
-
 int v4l1_ioctl (int fd, unsigned long int request, ...)
 {
   void *arg;
@@ -458,7 +453,7 @@
   va_end (ap);
 
   if ((index = v4l1_get_index(fd)) == -1)
-    return syscall(SYS_ioctl, fd, request, arg);
+    return SYS_IOCTL(fd, request, arg);
 
   /* Appearantly the kernel and / or glibc ignore the 32 most significant bits
      when long = 64 bits, and some applications pass an int holding the req to
@@ -485,7 +480,7 @@
       {
 	struct video_capability *cap = arg;
 
-	result = syscall(SYS_ioctl, fd, request, arg);
+	result = SYS_IOCTL(fd, request, arg);
 
 	/* override kernel v4l1 compat min / max size with our own more
 	   accurate values */
@@ -547,24 +542,27 @@
       break;
 
     case VIDIOCSWIN:
+    case VIDIOCGWIN:
       {
 	struct video_window *win = arg;
 
 	devices[index].flags |= V4L1_PIX_SIZE_TOUCHED;
 
-	result = v4l1_set_format(index, win->width, win->height, -1, 1);
+	if (request == VIDIOCSWIN)
+	  result = v4l1_set_format(index, win->width, win->height, -1, 1);
+	else
+	  result = 0;
+
 	if (result == 0) {
+	  win->x = 0;
+	  win->y = 0;
 	  win->width  = devices[index].width;
 	  win->height = devices[index].height;
+	  win->flags = 0;
 	}
       }
       break;
 
-    case VIDIOCGWIN:
-      devices[index].flags |= V4L1_PIX_SIZE_TOUCHED;
-      result = syscall(SYS_ioctl, fd, request, arg);
-      break;
-
     case VIDIOCGCHAN:
       {
 	struct v4l2_input input2;
@@ -572,7 +570,7 @@
 
 	if ((devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) &&
 	    (devices[index].flags & V4L1_SUPPORTS_ENUMSTD)) {
-	  result = syscall(SYS_ioctl, fd, request, arg);
+	  result = SYS_IOCTL(fd, request, arg);
 	  break;
 	}
 
@@ -614,7 +612,7 @@
 	struct video_channel *chan = arg;
 	if ((devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) &&
 	    (devices[index].flags & V4L1_SUPPORTS_ENUMSTD)) {
-	  result = syscall(SYS_ioctl, fd, request, arg);
+	  result = SYS_IOCTL(fd, request, arg);
 	  break;
 	}
 	/* In case of no ENUMSTD support, ignore the norm member of the
@@ -650,7 +648,7 @@
 	}
 
 	if (devices[index].v4l1_frame_pointer == MAP_FAILED) {
-	  devices[index].v4l1_frame_pointer = (void *)syscall(SYS_mmap2, NULL,
+	  devices[index].v4l1_frame_pointer = (void *)SYS_MMAP(NULL,
 				      (size_t)mbuf->size,
 				      PROT_READ|PROT_WRITE,
 				      MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
@@ -747,7 +745,7 @@
   ssize_t result;
 
   if ((index = v4l1_get_index(fd)) == -1)
-    return syscall(SYS_read, fd, buffer, n);
+    return SYS_READ(fd, buffer, n);
 
   pthread_mutex_lock(&devices[index].stream_lock);
   result = v4l2_read(fd, buffer, n);
@@ -758,7 +756,7 @@
 
 
 void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd,
-  __off64_t offset)
+  int64_t offset)
 {
   int index;
   void *result;
--- libv4l1/log.c
+++ libv4l1/log.c
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -18,12 +18,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <linux/ioctl.h>
-/* These headers are not needed by us, but by linux/videodev2.h,
-   which is broken on some systems and doesn't include them itself :( */
-#include <sys/time.h>
-#include <asm/types.h>
-/* end broken header workaround includes */
+#include "../libv4lconvert/libv4lsyscall-priv.h"
 #include <linux/videodev.h>
 #include "libv4l1-priv.h"
 
--- libv4l1/v4l1compat.c
+++ libv4l1/v4l1compat.c
@@ -2,7 +2,7 @@
 # open/close/ioctl/mmap/munmap library call wrapper doing v4l1 api emulation
 # for v4l2 devices
 
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -25,6 +25,7 @@
 #include <stdarg.h>
 #include <fcntl.h>
 #include <libv4l1.h>
+#include "../libv4lconvert/libv4lsyscall-priv.h" /* for __off_t */
 
 #include <sys/ioctl.h>
 #include <sys/mman.h>
@@ -61,6 +62,7 @@
   return fd;
 }
 
+#ifdef linux
 LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...)
 {
   int fd;
@@ -81,6 +83,7 @@
 
   return fd;
 }
+#endif
 
 LIBV4L_PUBLIC int close(int fd) {
   return v4l1_close(fd);
@@ -114,11 +117,13 @@
   return v4l1_mmap(start, length, prot, flags, fd, offset);
 }
 
+#ifdef linux
 LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd,
   __off64_t offset)
 {
   return v4l1_mmap(start, length, prot, flags, fd, offset);
 }
+#endif
 
 LIBV4L_PUBLIC int munmap(void *start, size_t length)
 {
--- libv4l2/Makefile
+++ libv4l2/Makefile
@@ -13,9 +13,10 @@
 
 ifeq ($(LINKTYPE),static)
 V4L2_LIB      = libv4l2.a
+V4L2_DEPS     = $(V4L2_OBJS)
 else
 V4L2_LIB      = libv4l2.so
-V4L2_OBJS    += ../libv4lconvert/libv4lconvert.so
+V4L2_DEPS    += $(V4L2_OBJS) ../libv4lconvert/libv4lconvert.so
 TARGETS      += $(V4L2CONVERT)
 override CPPFLAGS += -fPIC
 endif
@@ -32,9 +33,15 @@
 LIBDIR = $(PREFIX)/lib
 endif
 
+ifeq ($(LIBSUBDIR),)
+LIBSUBDIR = libv4l
+endif
+
 all: $(TARGETS)
 
-$(V4L2_LIB): $(V4L2_OBJS)
+-include $(V4L2_OBJS:.o=.d)
+
+$(V4L2_LIB): $(V4L2_DEPS)
 
 $(V4L2CONVERT): $(V4L2CONVERT_O) $(V4L2_LIB)
 
@@ -57,21 +64,21 @@
 	mkdir -p $(DESTDIR)$(LIBDIR)
 	install -m 644 $(V4L2_LIB) $(DESTDIR)$(LIBDIR)
 else
-	mkdir -p $(DESTDIR)$(LIBDIR)/libv4l
+	mkdir -p $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR)
 	install -m 755 $(V4L2_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR)
 	cd $(DESTDIR)$(LIBDIR) && \
 	  ln -f -s $(V4L2_LIB).$(LIB_RELEASE) $(V4L2_LIB)
 	install -m 755 $(V4L2CONVERT).$(LIB_RELEASE) \
-	  $(DESTDIR)$(LIBDIR)/libv4l/$(V4L2CONVERT)
+	  $(DESTDIR)$(LIBDIR)/$(LIBSUBDIR)/$(V4L2CONVERT)
 endif
 	mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig
 	install -m 644 libv4l2.pc $(DESTDIR)$(LIBDIR)/pkgconfig
 
 clean::
-	rm -f *.a *.so* *.o *.d libv4l2.pc log *~ *.orig *.rej
+	rm -f *.a *.so* *.o *.d libv4l2.pc log *~ *.orig *.rej DEADJOE
 
 %.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) $^ $(LIBS_$*)
@@ -79,4 +86,3 @@
 
 %.a:
 	$(AR) cqs $@ $^
-
--- libv4l2/libv4l2-priv.h
+++ libv4l2/libv4l2-priv.h
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -23,14 +23,7 @@
 #include <pthread.h>
 #include <libv4lconvert.h> /* includes videodev2.h for us */
 
-/* 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
+#include "../libv4lconvert/libv4lsyscall-priv.h"
 
 #define V4L2_MAX_DEVICES 16
 /* Warning when making this larger the frame_queued and frame_mapped members of
@@ -87,6 +80,9 @@
   int frame_queued; /* 1 status bit per frame */
   /* mapping tracking of our fake (converting mmap) frame buffers */
   unsigned char frame_map_count[V4L2_MAX_NO_FRAMES];
+  /* buffer when doing conversion and using read() for read() */
+  int readbuf_size;
+  unsigned char *readbuf;
 };
 
 /* From log.c */
--- libv4l2/libv4l2.c
+++ libv4l2/libv4l2.c
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -59,7 +59,6 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <syscall.h>
 #include <fcntl.h>
 #include <string.h>
 #include <unistd.h>
@@ -77,6 +76,8 @@
 #define V4L2_STREAM_CONTROLLED_BY_READ	0x0400
 #define V4L2_SUPPORTS_READ		0x0800
 #define V4L2_IS_UVC			0x1000
+#define V4L2_STREAM_TOUCHED		0x2000
+#define V4L2_USE_READ_FOR_READ		0x4000
 
 #define V4L2_MMAP_OFFSET_MAGIC      0xABCDEF00u
 
@@ -99,9 +100,9 @@
 					  devices[index].nreadbuffers;
   req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   req.memory = V4L2_MEMORY_MMAP;
-  if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){
+  if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){
     int saved_err = errno;
-    V4L2_LOG_ERR("requesting %u buffers: %s\n", req.count, strerror(errno));
+    V4L2_LOG("warning reqbuf (%u) failed: %s\n", req.count, strerror(errno));
     errno = saved_err;
     return result;
   }
@@ -126,7 +127,7 @@
   req.count = 0;
   req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   req.memory = V4L2_MEMORY_MMAP;
-  if(syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req) < 0)
+  if(SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req) < 0)
     return;
 
   devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES);
@@ -147,7 +148,7 @@
     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     buf.memory = V4L2_MEMORY_MMAP;
     buf.index = i;
-    result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf);
+    result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf);
     if (result) {
       int saved_err = errno;
       V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno));
@@ -155,9 +156,9 @@
       break;
     }
 
-    devices[index].frame_pointers[i] = (void *)syscall(SYS_mmap2, NULL,
+    devices[index].frame_pointers[i] = (void *)SYS_MMAP(NULL,
       (size_t)buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, devices[index].fd,
-      (__off_t)(buf.m.offset >> MMAP2_PAGE_SHIFT));
+      buf.m.offset);
     if (devices[index].frame_pointers[i] == MAP_FAILED) {
       int saved_err = errno;
       V4L2_LOG_ERR("mmapping buffer %u: %s\n", i, strerror(errno));
@@ -181,7 +182,7 @@
   /* unmap the buffers */
   for (i = 0; i < devices[index].no_frames; i++) {
     if (devices[index].frame_pointers[i] != MAP_FAILED) {
-      syscall(SYS_munmap, devices[index].frame_pointers[i],
+      SYS_MUNMAP(devices[index].frame_pointers[i],
 	      devices[index].frame_sizes[i]);
       devices[index].frame_pointers[i] = MAP_FAILED;
       V4L2_LOG("unmapped buffer %u\n", i);
@@ -195,7 +196,7 @@
   enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
   if (!(devices[index].flags & V4L2_STREAMON)) {
-    if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMON,
+    if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMON,
 			  &type))) {
       int saved_err = errno;
       V4L2_LOG_ERR("turning on stream: %s\n", strerror(errno));
@@ -214,7 +215,7 @@
   enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
   if (devices[index].flags & V4L2_STREAMON) {
-    if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMOFF,
+    if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMOFF,
 			  &type))) {
       int saved_err = errno;
       V4L2_LOG_ERR("turning off stream: %s\n", strerror(errno));
@@ -242,7 +243,7 @@
   buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   buf.memory = V4L2_MEMORY_MMAP;
   buf.index  = buffer_index;
-  if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, &buf))) {
+  if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, &buf))) {
     int saved_err = errno;
     V4L2_LOG_ERR("queuing buf %d: %s\n", buffer_index, strerror(errno));
     errno = saved_err;
@@ -264,7 +265,7 @@
     return result;
 
   do {
-    if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf))) {
+    if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf))) {
       if (errno != EAGAIN) {
 	int saved_err = errno;
 	V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno));
@@ -305,6 +306,62 @@
   return result;
 }
 
+static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size)
+{
+  const int max_tries = 10;
+  int result, buf_size, tries = max_tries;
+
+  buf_size = devices[index].dest_fmt.fmt.pix.sizeimage;
+
+  if (devices[index].readbuf_size < buf_size) {
+    unsigned char *new_buf;
+
+    new_buf = realloc(devices[index].readbuf, buf_size);
+    if (!new_buf)
+      return -1;
+
+    devices[index].readbuf = new_buf;
+    devices[index].readbuf_size = buf_size;
+  }
+
+  do {
+    result = SYS_READ(devices[index].fd, devices[index].readbuf, buf_size);
+    if (result <= 0) {
+      if (result && errno != EAGAIN) {
+	int saved_err = errno;
+	V4L2_LOG_ERR("reading: %s\n", strerror(errno));
+	errno = saved_err;
+      }
+      return result;
+    }
+
+    result = v4lconvert_convert(devices[index].convert,
+	   &devices[index].src_fmt, &devices[index].dest_fmt,
+	   devices[index].readbuf, result, dest, dest_size);
+
+    if (result < 0) {
+      int saved_err = errno;
+
+      if(errno == EAGAIN)
+	V4L2_LOG("warning error while converting frame data: %s\n",
+	  v4lconvert_get_error_message(devices[index].convert));
+      else
+	V4L2_LOG_ERR("converting / decoding frame data: %s\n",
+	  v4lconvert_get_error_message(devices[index].convert));
+
+      errno = saved_err;
+    }
+    tries--;
+  } while (result < 0 && errno == EAGAIN && tries);
+
+  if (result < 0 && errno == EAGAIN) {
+    V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s\n",
+      max_tries, v4lconvert_get_error_message(devices[index].convert));
+  }
+
+  return result;
+}
+
 static int v4l2_queue_read_buffers(int index)
 {
   unsigned int i;
@@ -332,6 +389,11 @@
 {
   int result;
 
+  if ((devices[index].flags & V4L2_STREAMON) || devices[index].frame_queued) {
+    errno = EBUSY;
+    return -1;
+  }
+
   if ((result = v4l2_request_read_buffers(index)))
     return result;
 
@@ -364,12 +426,20 @@
   return 0;
 }
 
+static int v4l2_needs_conversion(int index)
+{
+  if (!(devices[index].flags & V4L2_DISABLE_CONVERSION))
+    return v4lconvert_needs_conversion(devices[index].convert,
+			    &devices[index].src_fmt, &devices[index].dest_fmt);
+
+  return 0;
+}
+
 static int v4l2_buffers_mapped(int index)
 {
   unsigned int i;
 
-  if (devices[index].src_fmt.fmt.pix.pixelformat ==
-      devices[index].dest_fmt.fmt.pix.pixelformat) {
+  if (!v4l2_needs_conversion(index)) {
     /* Normal (no conversion) mode */
     struct v4l2_buffer buf;
 
@@ -377,7 +447,7 @@
       buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
       buf.memory = V4L2_MEMORY_MMAP;
       buf.index = i;
-      if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf)) {
+      if (SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf)) {
 	int saved_err = errno;
 	V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno));
 	errno = saved_err;
@@ -413,12 +483,12 @@
     va_start (ap, oflag);
     mode = va_arg (ap, mode_t);
 
-    fd = syscall(SYS_open, file, oflag, mode);
+    fd = SYS_OPEN(file, oflag, mode);
 
     va_end(ap);
   }
   else
-    fd = syscall(SYS_open, file, oflag);
+    fd = SYS_OPEN(file, oflag, 0);
   /* end of original open code */
 
   if (fd == -1)
@@ -426,7 +496,7 @@
 
   if (v4l2_fd_open(fd, 0) == -1) {
     int saved_err = errno;
-    syscall(SYS_close, fd);
+    SYS_CLOSE(fd);
     errno = saved_err;
     return -1;
   }
@@ -448,22 +518,21 @@
     v4l2_log_file = fopen(lfname, "w");
 
   /* check that this is an v4l2 device */
-  if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap)) {
+  if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) {
     int saved_err = errno;
     V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
     errno = saved_err;
     return -1;
   }
 
-  /* we only add functionality for video capture devices, and we do not
-     handle devices which don't do mmap */
+  /* we only add functionality for video capture devices */
   if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ||
-      !(cap.capabilities & V4L2_CAP_STREAMING))
+      !(cap.capabilities & (V4L2_CAP_STREAMING|V4L2_CAP_READWRITE)))
     return fd;
 
   /* Get current cam format */
   fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-  if (syscall(SYS_ioctl, fd, VIDIOC_G_FMT, &fmt)) {
+  if (SYS_IOCTL(fd, VIDIOC_G_FMT, &fmt)) {
     int saved_err = errno;
     V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
     errno = saved_err;
@@ -493,12 +562,24 @@
   devices[index].flags = v4l2_flags;
   if (cap.capabilities & V4L2_CAP_READWRITE)
     devices[index].flags |= V4L2_SUPPORTS_READ;
+  if (!(cap.capabilities & V4L2_CAP_STREAMING))
+    devices[index].flags |= V4L2_USE_READ_FOR_READ;
   if (!strcmp((char *)cap.driver, "uvcvideo"))
     devices[index].flags |= V4L2_IS_UVC;
   devices[index].open_count = 1;
   devices[index].src_fmt = fmt;
   devices[index].dest_fmt = fmt;
 
+  /* When a user does a try_fmt with the current dest_fmt and the dest_fmt
+     is a supported one we will align the resolution (see try_fmt for why).
+     Do the same here now, so that a try_fmt on the result of a get_fmt done
+     immediately after open leaves the fmt unchanged. */
+  if (v4lconvert_supported_dst_format(
+				devices[index].dest_fmt.fmt.pix.pixelformat)) {
+    devices[index].dest_fmt.fmt.pix.width &= ~7;
+    devices[index].dest_fmt.fmt.pix.height &= ~1;
+  }
+
   pthread_mutex_init(&devices[index].stream_lock, NULL);
 
   devices[index].no_frames = 0;
@@ -510,6 +591,8 @@
     devices[index].frame_map_count[i] = 0;
   }
   devices[index].frame_queued = 0;
+  devices[index].readbuf = NULL;
+  devices[index].readbuf_size = 0;
 
   if (index >= devices_used)
     devices_used = index + 1;
@@ -544,7 +627,7 @@
   int index, result;
 
   if ((index = v4l2_get_index(fd)) == -1)
-    return syscall(SYS_close, fd);
+    return SYS_CLOSE(fd);
 
   /* Abuse stream_lock to stop 2 closes from racing and trying to free the
      resources twice */
@@ -558,15 +641,18 @@
 
   /* Free resources */
   v4l2_unmap_buffers(index);
-  v4lconvert_destroy(devices[index].convert);
   if (devices[index].convert_mmap_buf != MAP_FAILED) {
     if (v4l2_buffers_mapped(index))
       V4L2_LOG_WARN("v4l2 mmap buffers still mapped on close()\n");
     else
-      syscall(SYS_munmap, devices[index].convert_mmap_buf,
+      SYS_MUNMAP(devices[index].convert_mmap_buf,
 	      devices[index].no_frames * V4L2_FRAME_BUF_SIZE);
     devices[index].convert_mmap_buf = MAP_FAILED;
   }
+  v4lconvert_destroy(devices[index].convert);
+  free(devices[index].readbuf);
+  devices[index].readbuf = NULL;
+  devices[index].readbuf_size = 0;
 
   /* Remove the fd from our list of managed fds before closing it, because as
      soon as we've done the actual close the fd maybe returned by an open in
@@ -576,7 +662,7 @@
   /* Since we've marked the fd as no longer used, and freed the resources,
      redo the close in case it was interrupted */
   do {
-    result = syscall(SYS_close, fd);
+    result = SYS_CLOSE(fd);
   } while (result == -1 && errno == EINTR);
 
   V4L2_LOG("close: %d\n", fd);
@@ -613,7 +699,7 @@
   /* We may change from convert to non conversion mode and
      v4l2_unrequest_read_buffers may change the no_frames, so free the
      convert mmap buffer */
-  syscall(SYS_munmap, devices[index].convert_mmap_buf,
+  SYS_MUNMAP(devices[index].convert_mmap_buf,
     devices[index].no_frames * V4L2_FRAME_BUF_SIZE);
   devices[index].convert_mmap_buf = MAP_FAILED;
 
@@ -659,7 +745,7 @@
 {
   void *arg;
   va_list ap;
-  int result, converting, index, saved_err;
+  int result, index, saved_err;
   int is_capture_request = 0, stream_needs_locking = 0;
 
   va_start (ap, request);
@@ -667,7 +753,7 @@
   va_end (ap);
 
   if ((index = v4l2_get_index(fd)) == -1)
-    return syscall(SYS_ioctl, fd, request, arg);
+    return SYS_IOCTL(fd, request, arg);
 
   /* Appearantly the kernel and / or glibc ignore the 32 most significant bits
      when long = 64 bits, and some applications pass an int holding the req to
@@ -676,21 +762,28 @@
 
   /* Is this a capture request and do we need to take the stream lock? */
   switch (request) {
+    case VIDIOC_QUERYCTRL:
+    case VIDIOC_G_CTRL:
+    case VIDIOC_S_CTRL:
+      if (!(devices[index].flags & V4L2_DISABLE_CONVERSION))
+	is_capture_request = 1;
+      break;
     case VIDIOC_QUERYCAP:
       is_capture_request = 1;
       break;
     case VIDIOC_ENUM_FMT:
       if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
-	  (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION))
+	  !(devices[index].flags & V4L2_DISABLE_CONVERSION))
 	is_capture_request = 1;
       break;
     case VIDIOC_ENUM_FRAMESIZES:
     case VIDIOC_ENUM_FRAMEINTERVALS:
-      if (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION)
+      if (!(devices[index].flags & V4L2_DISABLE_CONVERSION))
 	is_capture_request = 1;
       break;
     case VIDIOC_TRY_FMT:
-      if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+      if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	  !(devices[index].flags & V4L2_DISABLE_CONVERSION))
 	is_capture_request = 1;
       break;
     case VIDIOC_S_FMT:
@@ -724,7 +817,7 @@
   }
 
   if (!is_capture_request) {
-    result = syscall(SYS_ioctl, fd, request, arg);
+    result = SYS_IOCTL(fd, request, arg);
     saved_err = errno;
     v4l2_log_ioctl(request, arg, result);
     errno = saved_err;
@@ -732,18 +825,48 @@
   }
 
 
-  if (stream_needs_locking)
+  if (stream_needs_locking) {
     pthread_mutex_lock(&devices[index].stream_lock);
-
-  converting = v4lconvert_needs_conversion(devices[index].convert,
-			 &devices[index].src_fmt, &devices[index].dest_fmt);
+    /* If this is the first stream related ioctl, and we should only allow
+       libv4lconvert supported destination formats (so that it can do flipping,
+       processing, etc.) and the current destination format is not supported,
+       try setting the format to RGB24 (which is a supported dest. format). */
+    if (!(devices[index].flags & V4L2_STREAM_TOUCHED) &&
+	!(devices[index].flags & V4L2_DISABLE_CONVERSION) &&
+	v4lconvert_supported_dst_fmt_only(devices[index].convert) &&
+	!v4lconvert_supported_dst_format(
+			      devices[index].dest_fmt.fmt.pix.pixelformat)) {
+      struct v4l2_format fmt = devices[index].dest_fmt;
+
+      V4L2_LOG("Setting pixelformat to RGB24 (supported_dst_fmt_only)");
+      devices[index].flags |= V4L2_STREAM_TOUCHED;
+      fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
+      pthread_mutex_unlock(&devices[index].stream_lock);
+      v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt);
+      pthread_mutex_lock(&devices[index].stream_lock);
+      V4L2_LOG("Done setting pixelformat (supported_dst_fmt_only)");
+    }
+    devices[index].flags |= V4L2_STREAM_TOUCHED;
+  }
 
   switch (request) {
+    case VIDIOC_QUERYCTRL:
+      result = v4lconvert_vidioc_queryctrl(devices[index].convert, arg);
+      break;
+
+    case VIDIOC_G_CTRL:
+      result = v4lconvert_vidioc_g_ctrl(devices[index].convert, arg);
+      break;
+
+    case VIDIOC_S_CTRL:
+      result = v4lconvert_vidioc_s_ctrl(devices[index].convert, arg);
+      break;
+
     case VIDIOC_QUERYCAP:
       {
 	struct v4l2_capability *cap = arg;
 
-	result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYCAP, cap);
+	result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYCAP, cap);
 	if (result == 0)
 	  /* We always support read() as we fake it using mmap mode */
 	  cap->capabilities |= V4L2_CAP_READWRITE;
@@ -794,7 +917,7 @@
 	}
 
 	if (devices[index].flags & V4L2_DISABLE_CONVERSION) {
-	  result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT,
+	  result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT,
 			   dest_fmt);
 	  src_fmt = *dest_fmt;
 	} else {
@@ -834,7 +957,7 @@
 	  break;
 
 	req_pix_fmt = src_fmt.fmt.pix;
-	result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT, &src_fmt);
+	result = SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT, &src_fmt);
 	if (result) {
 	  saved_err = errno;
 	  V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno));
@@ -884,7 +1007,7 @@
 	if (req->count > V4L2_MAX_NO_FRAMES)
 	  req->count = V4L2_MAX_NO_FRAMES;
 
-	result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, req);
+	result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, req);
 	if (result < 0)
 	  break;
 	result = 0; /* some drivers return the number of buffers on success */
@@ -904,8 +1027,8 @@
 
 	/* Do a real query even when converting to let the driver fill in
 	   things like buf->field */
-	result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, buf);
-	if (result || !converting)
+	result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, buf);
+	if (result || !v4l2_needs_conversion(index))
 	  break;
 
 	buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index;
@@ -923,11 +1046,11 @@
 	  break;
 
       /* With some drivers the buffers must be mapped before queuing */
-      if (converting)
+      if (v4l2_needs_conversion(index))
 	if ((result = v4l2_map_buffers(index)))
 	  break;
 
-      result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, arg);
+      result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, arg);
       break;
 
     case VIDIOC_DQBUF:
@@ -938,8 +1061,8 @@
 	  if ((result = v4l2_deactivate_read_stream(index)))
 	    break;
 
-	if (!converting) {
-	  result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf);
+	if (!v4l2_needs_conversion(index)) {
+	  result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
 	  if (result) {
 	    int saved_err = errno;
 	    V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno));
@@ -952,7 +1075,7 @@
 	   but we need the buffer _now_ to write our converted data
 	   to it! */
 	if (devices[index].convert_mmap_buf == MAP_FAILED) {
-	  devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, NULL,
+	  devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL,
 						   (size_t)(
 						     devices[index].no_frames *
 						     V4L2_FRAME_BUF_SIZE),
@@ -997,7 +1120,7 @@
       break;
 
     default:
-      result = syscall(SYS_ioctl, fd, request, arg);
+      result = SYS_IOCTL(fd, request, arg);
   }
 
   if (stream_needs_locking)
@@ -1015,39 +1138,47 @@
 {
   ssize_t result;
   int index;
-  struct v4l2_buffer buf;
 
   if ((index = v4l2_get_index(fd)) == -1)
-    return syscall(SYS_read, fd, dest, n);
+    return SYS_READ(fd, dest, n);
 
   pthread_mutex_lock(&devices[index].stream_lock);
 
   /* When not converting and the device supports read let the kernel handle
      it */
   if ((devices[index].flags & V4L2_SUPPORTS_READ) &&
-      !v4lconvert_needs_conversion(devices[index].convert,
-		   &devices[index].src_fmt, &devices[index].dest_fmt)) {
-    result = syscall(SYS_read, fd, dest, n);
+      !v4l2_needs_conversion(index)) {
+    result = SYS_READ(fd, dest, n);
     goto leave;
   }
 
-  if (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ)) {
-    if ((devices[index].flags & V4L2_STREAMON) ||
-	devices[index].frame_queued) {
-      errno = EBUSY;
-      result = -1;
-      goto leave;
-    }
+  /* Since we need to do conversion try to use mmap (streaming) mode under
+     the hood as that safes a memcpy for each frame read.
+
+     Note sometimes this will fail as some drivers (atleast gspca) do not allow
+     switching from read mode to mmap mode and they assume read() mode if a
+     select or poll() is done before any buffers are requested. So using mmap
+     mode under the hood will fail if a select() or poll() is done before the
+     first emulated read() call. */
+  if (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) &&
+      !(devices[index].flags & V4L2_USE_READ_FOR_READ)) {
     if ((result = v4l2_activate_read_stream(index)))
-      goto leave;
+      /* Activating mmap mode failed, use read() instead */
+      devices[index].flags |= V4L2_USE_READ_FOR_READ;
   }
 
-  buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-  buf.memory = V4L2_MEMORY_MMAP;
-  result = v4l2_dequeue_and_convert(index, &buf, dest, n);
+  if (devices[index].flags & V4L2_USE_READ_FOR_READ) {
+    result = v4l2_read_and_convert(index, dest, n);
+  } else {
+    struct v4l2_buffer buf;
+
+    buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buf.memory = V4L2_MEMORY_MMAP;
+    result = v4l2_dequeue_and_convert(index, &buf, dest, n);
 
-  if (result >= 0)
-    v4l2_queue_read_buffer(index, buf.index);
+    if (result >= 0)
+      v4l2_queue_read_buffer(index, buf.index);
+  }
 
 leave:
   pthread_mutex_unlock(&devices[index].stream_lock);
@@ -1056,7 +1187,7 @@
 }
 
 void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd,
-  __off64_t offset)
+  int64_t offset)
 {
   int index;
   unsigned int buffer_index;
@@ -1076,8 +1207,7 @@
       return MAP_FAILED;
     }
 
-    return (void *)syscall(SYS_mmap2, start, length, prot, flags, fd,
-			   (__off_t)(offset >> MMAP2_PAGE_SHIFT));
+    return (void *)SYS_MMAP(start, length, prot, flags, fd, offset);
   }
 
   pthread_mutex_lock(&devices[index].stream_lock);
@@ -1085,15 +1215,14 @@
   buffer_index = offset & 0xff;
   if (buffer_index >= devices[index].no_frames ||
       /* Got magic offset and not converting ?? */
-      !v4lconvert_needs_conversion(devices[index].convert,
-		   &devices[index].src_fmt, &devices[index].dest_fmt)) {
+      !v4l2_needs_conversion(index)) {
     errno = EINVAL;
     result = MAP_FAILED;
     goto leave;
   }
 
   if (devices[index].convert_mmap_buf == MAP_FAILED) {
-    devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, NULL,
+    devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL,
 					     (size_t)(
 					       devices[index].no_frames *
 					       V4L2_FRAME_BUF_SIZE),
@@ -1166,7 +1295,7 @@
 
   V4L2_LOG("v4l2 unknown munmap %p, %d\n", start, (int)length);
 
-  return syscall(SYS_munmap, _start, length);
+  return SYS_MUNMAP(_start, length);
 }
 
 /* Misc utility functions */
@@ -1174,9 +1303,15 @@
 {
   struct v4l2_queryctrl qctrl = { .id = cid };
   struct v4l2_control ctrl = { .id = cid };
-  int result;
+  int index, result;
 
-  if ((result = syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl)))
+  if ((index = v4l2_get_index(fd)) == -1) {
+    V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd);
+    errno = EBADF;
+    return -1;
+  }
+
+  if ((result = v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl)))
     return result;
 
   if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) &&
@@ -1187,7 +1322,7 @@
       ctrl.value = (value * (qctrl.maximum - qctrl.minimum) + 32767) / 65535 +
 		   qctrl.minimum;
 
-    result = syscall(SYS_ioctl, fd, VIDIOC_S_CTRL, &ctrl);
+    result = v4lconvert_vidioc_s_ctrl(devices[index].convert, &ctrl);
   }
 
   return result;
@@ -1197,14 +1332,21 @@
 {
   struct v4l2_queryctrl qctrl = { .id = cid };
   struct v4l2_control ctrl = { .id = cid };
+  int index;
+
+  if ((index = v4l2_get_index(fd)) == -1) {
+    V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd);
+    errno = EBADF;
+    return -1;
+  }
 
-  if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl))
+  if (v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl))
     return 0;
 
   if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
     return 0;
 
-  if (syscall(SYS_ioctl, fd, VIDIOC_G_CTRL, &ctrl))
+  if (v4lconvert_vidioc_g_ctrl(devices[index].convert, &ctrl))
     return 0;
 
   return ((ctrl.value - qctrl.minimum) * 65535 +
--- libv4l2/log.c
+++ libv4l2/log.c
@@ -1,5 +1,7 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -20,12 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <linux/ioctl.h>
-/* These headers are not needed by us, but by linux/videodev2.h,
-   which is broken on some systems and doesn't include them itself :( */
-#include <sys/time.h>
-#include <asm/types.h>
-/* end broken header workaround includes */
+#include "../libv4lconvert/libv4lsyscall-priv.h"
 #include <linux/videodev2.h>
 #include "libv4l2.h"
 #include "libv4l2-priv.h"
@@ -161,7 +158,7 @@
 	struct v4l2_frmsizeenum *frmsize = arg;
 	int pixfmt = frmsize->pixel_format;
 
-	fprintf(v4l2_log_file, "  index: %u pixelformat: %c%c%c%c",
+	fprintf(v4l2_log_file, "  index: %u pixelformat: %c%c%c%c\n",
 	  frmsize->index,
 	  pixfmt & 0xff,
 	  (pixfmt >> 8) & 0xff,
@@ -169,12 +166,12 @@
 	  pixfmt >> 24);
 	switch (frmsize->type) {
 	  case V4L2_FRMSIZE_TYPE_DISCRETE:
-	    fprintf(v4l2_log_file, " %ux%u\n", frmsize->discrete.width,
+	    fprintf(v4l2_log_file, "  %ux%u\n", frmsize->discrete.width,
 	      frmsize->discrete.height);
 	    break;
 	  case V4L2_FRMSIZE_TYPE_CONTINUOUS:
 	  case V4L2_FRMSIZE_TYPE_STEPWISE:
-	    fprintf(v4l2_log_file, " %ux%u -> %ux%u\n",
+	    fprintf(v4l2_log_file, "  %ux%u -> %ux%u\n",
 	      frmsize->stepwise.min_width, frmsize->stepwise.min_height,
 	      frmsize->stepwise.max_width, frmsize->stepwise.max_height);
 	    break;
@@ -186,7 +183,7 @@
 	struct v4l2_frmivalenum *frmival = arg;
 	int pixfmt = frmival->pixel_format;
 
-	fprintf(v4l2_log_file, "  index: %u pixelformat: %c%c%c%c %ux%u: ",
+	fprintf(v4l2_log_file, "  index: %u pixelformat: %c%c%c%c %ux%u:\n",
 	  frmival->index,
 	  pixfmt & 0xff,
 	  (pixfmt >> 8) & 0xff,
@@ -196,12 +193,12 @@
 	  frmival->height);
 	switch (frmival->type) {
 	  case V4L2_FRMIVAL_TYPE_DISCRETE:
-	    fprintf(v4l2_log_file, "%u/%u\n", frmival->discrete.numerator,
+	    fprintf(v4l2_log_file, "  %u/%u\n", frmival->discrete.numerator,
 	      frmival->discrete.denominator);
 	    break;
 	  case V4L2_FRMIVAL_TYPE_CONTINUOUS:
 	  case V4L2_FRMIVAL_TYPE_STEPWISE:
-	    fprintf(v4l2_log_file, "%u/%u -> %u/%u\n",
+	    fprintf(v4l2_log_file, "  %u/%u -> %u/%u\n",
 	      frmival->stepwise.min.numerator,
 	      frmival->stepwise.min.denominator,
 	      frmival->stepwise.max.numerator,
--- libv4l2/v4l2convert.c
+++ libv4l2/v4l2convert.c
@@ -3,7 +3,7 @@
 # for v4l2 applications which want to be able to simply capture bgr24 / yuv420
 # from v4l2 devices with more exotic frame formats.
 
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -24,17 +24,11 @@
 
 #include <stdarg.h>
 #include <stdlib.h>
-#include <syscall.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
-/* These headers are not needed by us, but by linux/videodev2.h,
-   which is broken on some systems and doesn't include them itself :( */
-#include <sys/time.h>
-#include <asm/types.h>
-#include <linux/ioctl.h>
-/* end broken header workaround includes */
+#include "../libv4lconvert/libv4lsyscall-priv.h"
 #include <linux/videodev2.h>
 #include <libv4l2.h>
 
@@ -72,18 +66,18 @@
     va_start (ap, oflag);
     mode = va_arg (ap, mode_t);
 
-    fd = syscall(SYS_open, file, oflag, mode);
+    fd = SYS_OPEN(file, oflag, mode);
 
     va_end(ap);
   } else
-    fd = syscall(SYS_open, file, oflag);
+    fd = SYS_OPEN(file, oflag, 0);
   /* end of original open code */
 
   if (fd == -1 || !v4l_device)
     return fd;
 
   /* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */
-  if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap))
+  if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap))
     return fd;
 
   /* libv4l2 only adds functionality to capture capable devices */
@@ -92,11 +86,12 @@
 
   /* Try to Register with libv4l2 (in case of failure pass the fd to the
      application as is) */
-  v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION);
+  v4l2_fd_open(fd, 0);
 
   return fd;
 }
 
+#ifdef linux
 LIBV4L_PUBLIC int open64(const char *file, int oflag, ...)
 {
   int fd;
@@ -119,6 +114,7 @@
 
   return fd;
 }
+#endif
 
 LIBV4L_PUBLIC int close(int fd)
 {
@@ -153,11 +149,13 @@
   return v4l2_mmap(start, length, prot, flags, fd, offset);
 }
 
+#ifdef linux
 LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd,
   __off64_t offset)
 {
   return v4l2_mmap(start, length, prot, flags, fd, offset);
 }
+#endif
 
 LIBV4L_PUBLIC int munmap(void *start, size_t length)
 {
--- libv4lconvert/Makefile
+++ libv4lconvert/Makefile
@@ -3,6 +3,8 @@
 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
@@ -12,8 +14,12 @@
 
 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
-TARGETS       = $(CONVERT_LIB) libv4lconvert.pc
+		rgbyuv.o sn9c2028-decomp.o spca501.o sq905c.o bayer.o hm12.o \
+		stv0680.o cpia1.o \
+		control/libv4lcontrol.o processing/libv4lprocessing.o \
+		processing/whitebalance.o processing/autogain.o \
+		processing/gamma.o helper.o
+TARGETS       = $(CONVERT_LIB) libv4lconvert.pc ov511-decomp ov518-decomp
 INCLUDES      = ../include/libv4lconvert.h
 
 ifeq ($(LIB_RELEASE),)
@@ -28,8 +34,16 @@
 LIBDIR = $(PREFIX)/lib
 endif
 
+ifeq ($(LIBSUBDIR),)
+LIBSUBDIR = libv4l
+endif
+
+override CPPFLAGS += -DLIBDIR=\"$(LIBDIR)\" -DLIBSUBDIR=\"$(LIBSUBDIR)\"
+
 all: $(TARGETS)
 
+-include $(CONVERT_OBJS:.o=.d)
+
 $(CONVERT_LIB): $(CONVERT_OBJS)
 
 libv4lconvert.pc:
@@ -40,32 +54,37 @@
 	@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)/$(LIBSUBDIR)
 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)/$(LIBSUBDIR)
 	mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig
 	install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig
 
 clean::
-	rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~ *.orig *.rej
+	rm -f *.a *.so* *.o *.d */*.o */*.d libv4lconvert.pc log *~ */*~
+	rm -f *.orig *.rej */*.orig */*.rej 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
--- libv4lconvert/bayer.c
+++ libv4lconvert/bayer.c
@@ -1,6 +1,6 @@
 /*
  * lib4lconvert, video4linux2 format conversion lib
- *             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+ *             (C) 2008 Hans de Goede <hdegoede at redhat.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
--- libv4lconvert/control
+++ libv4lconvert/control
+(directory)
--- libv4lconvert/control/libv4lcontrol-priv.h
+++ libv4lconvert/control/libv4lcontrol-priv.h
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008-2009 Radjnies Bhansingh <radjnies at gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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
+#define V4LCONTROL_MEMORY_IS_MALLOCED 0x02
+
+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;
+/* Some seldom used dmi strings (for notebooks with bogus info in the board
+   entries, but usefull info elsewhere). We keep this at the end as to not
+   polute the initalizers for the normal case. */
+  /* System (product) vendor / name */
+  const char *dmi_system_vendor;
+  const char *dmi_system_name;
+  /* Board and System versions */
+  const char *dmi_board_version;
+  const char *dmi_system_version;
+};
+
+#endif
--- libv4lconvert/control/libv4lcontrol.c
+++ libv4lconvert/control/libv4lcontrol.c
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008-2009 Radjnies Bhansingh <radjnies at gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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 <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <pwd.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 },
+  /* Laptops */
+  { 0x046d, 0x09b2, 0, "FUJITSU", "FJNB1C9",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0,
+    "FUJITSU SIEMENS", "LIFEBOOK P7230" },
+  { 0x046d, 0x09b2, 0, "FUJITSU", "FJNB1C9",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0,
+    "FUJITSU", "LifeBook P7230" },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "F7E       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "F7F       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "F7L       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "F7Se      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "F7SR      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "G50VT     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "W7S       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "W7Sg      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "X55SR     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "X55SV     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "X71Vn     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc.        ", "X71Q      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "PEGATRON CORPORATION         ", "X71SL     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  /* Note no whitespace padding for board vendor, this is not a typo */
+  { 0x04f2, 0xb012, 0, "PEGATRON CORPORATION", "X71TL     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  /* These 3 PACKARD BELL's seem to be Asus notebook in disguise */
+  { 0x04f2, 0xb012, 0, "Packard Bell BV", "T32A      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "PACKARD BELL BV              ", "EasyNote_BG45",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb012, 0, "PACKARD BELL BV              ", "EasyNote_BG46",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb034, 0, "ASUSTeK Computer Inc.        ", "G70Sg     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc.        ", "U6S       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc.        ", "U6Sg      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc.        ", "U6V       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc.        ", "UL30A     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc.        ", "UL30VT    ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb036, 0, "ASUSTeK Computer Inc.        ", "UL50Ag    ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K40IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K40IN      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K50IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K50IN      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K61IC     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K70AB     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K70IC     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K70IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "K70IO     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "N10J      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "N10Jc     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "N20A      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "N61Vg     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUS CORPORATION            ", "N90SC     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer Inc.        ", "U6Vc      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  /* Note no whitespace padding for these b071 models, this is not a typo */
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer INC.", "K40AB",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer INC.", "K50AB",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb071, 0, "ASUSTeK Computer INC.", "N5051Tp",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb072, 0, "ASUSTeK Computer Inc.        ", "K50IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc.        ", "N50Vc      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc.        ", "N50Vn      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc.        ", "N51Vf      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc.        ", "N51Vg      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc.        ", "N51Vn     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  /* Note no whitespace padding for this model, this is not a typo */
+  { 0x04f2, 0xb106, 0, "ASUSTeK Computer INC.", "N5051Tp",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb16b, 0, "ASUSTeK Computer Inc.        ", "U20A      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb16b, 0, "ASUSTeK Computer Inc.        ", "U80A      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x04f2, 0xb16b, 0, "ASUSTeK Computer Inc.        ", "U80V      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  /* Note no whitespace padding for board vendor, this is not a typo */
+  { 0x064e, 0xa111, 0, "ASUSTeK Computer Inc.", "F5RL      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa111, 0, "PEGATRON CORPORATION         ", "F5SR    ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUS CORPORATION            ", "F70SL     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "F82Q      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K40IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K40IN      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K50IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K51AC     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K60IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K60IN      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K70AB     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "K70IO     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "N10E      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "N10J      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "N10Jb     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "N10Jc     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "N20A      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "N61Vn     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUS CORPORATION            ", "N70SV     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUS CORPORATION            ", "N90SV     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer Inc.        ", "X58LE     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  /* Note no whitespace padding for this model, this is not a typo */
+  { 0x064e, 0xa116, 0, "ASUSTeK Computer INC.", "K40AB",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa136, 0, "ASUSTeK Computer Inc.        ", "UL30A     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa136, 0, "ASUSTeK Computer Inc.        ", "UL50A     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x064e, 0xa136, 0, "ASUSTeK Computer Inc.        ", "UL50Ag    ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x090c, 0xe370, 0, "ASUSTeK Computer Inc.        ", "U6S       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x090c, 0xe370, 0, "ASUSTeK Computer Inc.        ", "VX3       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x13d3, 0x5094, 0, "ASUSTeK Computer Inc.        ", "P50IJ     ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F3Ka      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F3Ke      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F3Q       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F3Sa      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F3Sg      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F3Sr      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F5N       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F5GL       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "F5SL    ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "G1S       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "G1Sn      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0x5a35, 0, "ASUSTeK Computer Inc.        ", "G2S       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x174f, 0xa311, 0, "ASUSTeK Computer Inc.        ", "A3F       ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x1d4d, 0x1002, 0, "ASUSTeK Computer Inc.        ", "B50A      ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED },
+  { 0x5986, 0x0200, 0, "LENOVO", "SPEEDY    ",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
+    "Lenovo IdeaPad Y510" },
+  { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
+    "Lenovo IdeaPad U330" },
+  { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name",
+    V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
+    "Lenovo IdeaPad Y330" },
+
+/* Second: devices which should use some software processing by default */
+  /* sn9c101 / sn9c102 based devices (sonixb) */
+  { 0x0c45, 0x6001, 0,    NULL, NULL, V4LCONTROL_WANTS_WB }, /* TAS5110C */
+  { 0x0c45, 0x6005, 0,    NULL, NULL, V4LCONTROL_WANTS_WB }, /* TAS5110C */
+  { 0x0c45, 0x6007, 0,    NULL, NULL, V4LCONTROL_WANTS_WB }, /* TAS5110D */
+  /* 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 },
+  { 0x06f8, 0x3009, 0,    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 2 versions:
+     pb0100   only needs whitebalance, see software autogain code enable below
+     hdcs10xx needs both whitebalance and autogain. */
+  { 0x046d, 0x0840, 0,    NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
+  /* 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 },
+  /* mr97310a cams, note some models do not have the necessary controls, for
+     those we will only do whitebal. see software autogain code enable below */
+  { 0x08ca, 0x0111, 0,    NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
+  { 0x093a, 0x010e, 1,    NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
+  /* stv0680 based cams */
+  { 0x0553, 0x0202, 0,    NULL, NULL, V4LCONTROL_WANTS_WB },
+  { 0x041e, 0x4007, 0,    NULL, NULL, V4LCONTROL_WANTS_WB },
+};
+
+static const struct v4l2_queryctrl fake_controls[];
+
+static void v4lcontrol_get_dmi_string(const char *string, char *buf, int size)
+{
+  FILE *f;
+  char *s, sysfs_name[512];
+
+  snprintf(sysfs_name, sizeof(sysfs_name),
+	   "/sys/class/dmi/id/%s", string);
+  f = fopen(sysfs_name, "r");
+  if (!f) {
+    /* Try again with a different sysfs path, not sure if this is needed
+       but we used to look under /sys/devices/virtual/dmi/id in older
+       libv4l versions, but this did not work with some kernels */
+    snprintf(sysfs_name, sizeof(sysfs_name),
+	     "/sys/devices/virtual/dmi/id/%s", string);
+    f = fopen(sysfs_name, "r");
+    if (!f) {
+      buf[0] = 0;
+      return;
+    }
+  }
+
+  s = fgets(buf, size, f);
+  if (s)
+    s[strlen(s) - 1] = 0;
+  fclose(f);
+}
+
+static int v4lcontrol_get_usb_ids(struct v4lcontrol_data *data,
+  unsigned short *vendor_id, unsigned short *product_id)
+{
+  FILE *f;
+  int i, minor;
+  struct stat st;
+  char sysfs_name[512];
+  char c, *s, buf[32];
+
+  if (fstat(data->fd, &st) || !S_ISCHR(st.st_mode)) {
+    return 0; /* 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 0; /* 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 0; /* 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 0; /* 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 0; /* 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 0; /* Should never happen */
+
+    s = fgets(buf, sizeof(buf), f);
+    fclose(f);
+
+    if (!s || sscanf(s, "%04hx%c", product_id, &c) != 2 || c != '\n')
+      return 0; /* Should never happen */
+  }
+
+  return 1;
+}
+
+static void v4lcontrol_get_flags_from_db(struct v4lcontrol_data *data,
+  unsigned short vendor_id, unsigned short product_id)
+{
+  char dmi_system_vendor[512], dmi_system_name[512], dmi_system_version[512];
+  char dmi_board_vendor[512], dmi_board_name[512], dmi_board_version[512];
+  int i;
+
+  /* Get DMI board and system strings */
+  v4lcontrol_get_dmi_string("sys_vendor", dmi_system_vendor,
+			    sizeof(dmi_system_vendor));
+  v4lcontrol_get_dmi_string("product_name", dmi_system_name,
+			    sizeof(dmi_system_name));
+  v4lcontrol_get_dmi_string("product_version", dmi_system_version,
+			    sizeof(dmi_system_version));
+
+  v4lcontrol_get_dmi_string("board_vendor", dmi_board_vendor,
+			    sizeof(dmi_board_vendor));
+  v4lcontrol_get_dmi_string("board_name", dmi_board_name,
+			    sizeof(dmi_board_name));
+  v4lcontrol_get_dmi_string("board_version", dmi_board_version,
+			    sizeof(dmi_board_version));
+
+  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_system_vendor == NULL ||
+	 !strcmp(v4lcontrol_flags[i].dmi_system_vendor, dmi_system_vendor)) &&
+	(v4lcontrol_flags[i].dmi_system_name == NULL ||
+	 !strcmp(v4lcontrol_flags[i].dmi_system_name, dmi_system_name)) &&
+	(v4lcontrol_flags[i].dmi_system_version == NULL ||
+	 !strcmp(v4lcontrol_flags[i].dmi_system_version, dmi_system_version)) &&
+
+	(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)) &&
+	(v4lcontrol_flags[i].dmi_board_version == NULL ||
+	 !strcmp(v4lcontrol_flags[i].dmi_board_version, dmi_board_version))) {
+      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, got_usb_ids, init = 0;
+  char *s, shm_name[256], pwd_buf[1024];
+  struct v4l2_capability cap;
+  struct v4l2_queryctrl ctrl;
+  struct passwd pwd, *pwd_p;
+  unsigned short vendor_id = 0;
+  unsigned short product_id = 0;
+  struct v4l2_input input;
+
+  struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data));
+
+  if (!data) {
+    fprintf(stderr, "libv4lcontrol: error: out of memory!\n");
+    return NULL;
+  }
+
+  data->fd = fd;
+
+  /* Check if the driver has indicated some form of flipping is needed */
+  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;
+  }
+
+  got_usb_ids = v4lcontrol_get_usb_ids(data, &vendor_id, &product_id);
+  if (got_usb_ids)
+    v4lcontrol_get_flags_from_db(data, vendor_id, product_id);
+
+  /* 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;
+    }
+  }
+
+  /* Check if a camera does not have hardware autogain and has the necessary
+     controls, before enabling sw autogain, even if this is requested by flags.
+     This is necessary because some cameras share a USB-ID, but can have
+     different sensors with / without autogain or the necessary controls. */
+  while (data->flags & V4LCONTROL_WANTS_AUTOGAIN) {
+    ctrl.id = V4L2_CID_AUTOGAIN;
+    rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+    if (rc == 0 && !(ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
+      break;
+
+    ctrl.id = V4L2_CID_EXPOSURE;
+    rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+    if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
+      break;
+
+    ctrl.id = V4L2_CID_GAIN;
+    rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+    if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
+      break;
+
+    data->controls |= 1 << V4LCONTROL_AUTOGAIN |
+		      1 << V4LCONTROL_AUTOGAIN_TARGET;
+    break;
+  }
+
+  /* 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 */
+
+  if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) {
+    perror("libv4lcontrol: error querying device capabilities");
+    goto error;
+  }
+
+  if (getpwuid_r(geteuid(), &pwd, pwd_buf, sizeof(pwd_buf), &pwd_p) == 0) {
+    if (got_usb_ids)
+      snprintf(shm_name, 256, "/libv4l-%s:%s:%04x:%04x:%s", pwd.pw_name,
+	       cap.bus_info, (int)vendor_id, (int)product_id, cap.card);
+    else
+      snprintf(shm_name, 256, "/libv4l-%s:%s:%s", pwd.pw_name,
+	       cap.bus_info, cap.card);
+  } else {
+    perror("libv4lcontrol: error getting username using uid instead");
+    if (got_usb_ids)
+      snprintf(shm_name, 256, "/libv4l-%lu:%s:%04x:%04x:%s",
+	       (unsigned long)geteuid(), cap.bus_info,
+	       (int)vendor_id, (int)product_id, cap.card);
+    else
+      snprintf(shm_name, 256, "/libv4l-%lu:%s:%s", (unsigned long)geteuid(),
+	       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
+    shm_fd = shm_open(shm_name, O_RDWR, (S_IREAD | S_IWRITE));
+
+  if (shm_fd >= 0) {
+    /* 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) {
+      perror("libv4lcontrol: error shm mmap failed");
+      data->shm_values = NULL;
+    }
+  } else
+    perror("libv4lcontrol: error creating shm segment failed");
+
+  /* Fall back to malloc */
+  if (data->shm_values == NULL) {
+    fprintf(stderr,
+	    "libv4lcontrol: falling back to malloc-ed memory for controls\n");
+    data->shm_values = malloc(V4LCONTROL_SHM_SIZE);
+    if (!data->shm_values) {
+      fprintf(stderr, "libv4lcontrol: error: out of memory!\n");
+      goto error;
+    }
+    init = 1;
+    data->priv_flags |= V4LCONTROL_MEMORY_IS_MALLOCED;
+  }
+
+  if (init) {
+    /* Initialize the new shm object we created */
+    memset(data->shm_values, 0, 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) {
+    if (data->priv_flags & V4LCONTROL_MEMORY_IS_MALLOCED)
+      free(data->shm_values);
+    else
+      munmap(data->shm_values, V4LCONTROL_SHM_SIZE);
+  }
+  free(data);
+}
+
+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;
+  uint32_t 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;
+}
--- libv4lconvert/control/libv4lcontrol.h
+++ libv4lconvert/control/libv4lcontrol.h
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008-2009 Radjnies Bhansingh <radjnies at gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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
--- libv4lconvert/cpia1.c
+++ libv4lconvert/cpia1.c
+/*
+#             (C) 2010 Hans de Goede <hdegoede at 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 "libv4lconvert-priv.h"
+#include <string.h>
+
+#define MAGIC_0		0x19
+#define MAGIC_1		0x68
+#define SUBSAMPLE_420	0
+#define SUBSAMPLE_422	1
+#define YUVORDER_YUYV	0
+#define YUVORDER_UYVY	1
+#define NOT_COMPRESSED	0
+#define COMPRESSED	1
+#define NO_DECIMATION	0
+#define DECIMATION_ENAB	1
+#define EOI		0xff	/* End Of Image */
+#define EOL		0xfd	/* End Of Line */
+#define FRAME_HEADER_SIZE	64
+
+/* CPIA YUYV (sometimes sort of compressed) */
+int v4lconvert_cpia1_to_yuv420(struct v4lconvert_data *data,
+  const unsigned char *src, int src_size,
+  unsigned char *dest, int width, int height, int yvu)
+{
+  int x, y, ll, compressed;
+  unsigned char *udest, *vdest;
+
+  if (width > 352 || height > 288) {
+    fprintf(stderr, "FATAL ERROR CPIA1 size > 352x288, please report!\n");
+    return -1;
+  }
+
+  if (data->previous_frame == NULL) {
+    data->previous_frame = malloc(352 * 288 * 3 / 2);
+    if (data->previous_frame == NULL) {
+      fprintf(stderr, "cpia1 decode error: could not allocate buffer!\n");
+      return -1;
+    }
+  }
+
+  if (yvu) {
+    vdest = dest + width * height;
+    udest = vdest + width * height / 4;
+  } else {
+    udest = dest + width * height;
+    vdest = udest + width * height / 4;
+  }
+
+  /* Verify header */
+  if (src_size < FRAME_HEADER_SIZE ||
+      src[0] != MAGIC_0 || src[1] != MAGIC_1 ||
+      src[17] != SUBSAMPLE_420 ||
+      src[18] != YUVORDER_YUYV ||
+      (src[25] - src[24]) * 8 != width ||
+      (src[27] - src[26]) * 4 != height ||
+      (src[28] != NOT_COMPRESSED && src[28] != COMPRESSED) ||
+      (src[29] != NO_DECIMATION && src[29] != DECIMATION_ENAB)) {
+    fprintf(stderr, "cpia1 decode error: invalid header\n");
+    return -1;
+  }
+
+  if (src[29] == DECIMATION_ENAB) {
+    fprintf(stderr, "cpia1 decode error: decimation is not supported\n");
+    return -1;
+  }
+
+  compressed = src[28] == COMPRESSED;
+
+  src += FRAME_HEADER_SIZE;
+  src_size -= FRAME_HEADER_SIZE;
+
+  if (!compressed) {
+    for (y = 0; y < height && src_size > 2; y++) {
+      ll = src[0] | (src[1] << 8);
+      src += 2;
+      src_size -= 2;
+      if (src_size < ll) {
+	fprintf(stderr, "cpia1 decode error: short frame\n");
+	return -1;
+      }
+      if (src[ll - 1] != EOL) {
+	fprintf(stderr, "cpia1 decode error: invalid terminated line\n");
+	return -1;
+      }
+
+      if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */
+	if (ll != 2 * width + 1) {
+	  fprintf(stderr, "cpia1 decode error: invalid uncompressed even ll\n");
+	  return -1;
+	}
+
+	/* copy the Y values */
+	for (x = 0; x < width; x += 2) {
+	  *dest++ = src[0];
+	  *dest++ = src[2];
+	  src += 4;
+	}
+
+	/* copy the UV values */
+	src -= 2 * width;
+	for (x = 0; x < width; x += 2) {
+	  *udest++ = src[1];
+	  *vdest++ = src[3];
+	  src += 4;
+	}
+      } else { /* Odd line only Y values */
+	if (ll != width + 1) {
+	  fprintf(stderr, "cpia1 decode error: invalid uncompressed odd ll\n");
+	  return -1;
+	}
+
+	memcpy(dest, src, width);
+	dest += width;
+	src += width;
+      }
+      src++; /* Skip EOL */
+      src_size -= ll;
+    }
+  } else { /* compressed */
+    int ydest_index, uvdest_index;
+
+    /* Pre-fill dest with previous frame, as the cpia1 "compression" consists
+       of simply ommitting certain pixels */
+    memcpy(dest, data->previous_frame, width * height * 3 / 2);
+
+    for (y = 0; y < height && src_size > 2; y++) {
+      ll = src[0] | (src[1] << 8);
+      src += 2;
+      src_size -= 2;
+      if (src_size < ll) {
+	fprintf(stderr, "cpia1 decode error: short frame\n");
+	return -1;
+      }
+      if (src[ll - 1] != EOL) {
+	fprintf(stderr, "cpia1 decode error: invalid terminated line\n");
+	return -1;
+      }
+
+      /* Do this now as we use ll as loop variable below */
+      src_size -= ll;
+      for (x = 0; x < width && ll > 1; ) {
+	if (*src & 1) { /* skip N pixels */
+	  int skip = *src >> 1;
+
+	  if (skip & 1) {
+	    fprintf(stderr,
+		    "cpia1 decode error: odd number of pixels to skip");
+	    return -1;
+	  }
+
+	  if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */
+	    dest += skip;
+	    udest += skip / 2;
+	    vdest += skip / 2;
+	  } else { /* Odd line only Y values */
+	    dest += skip;
+	  }
+	  x += skip;
+	  src++;
+	  ll--;
+	} else {
+	  if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */
+	    *dest++ = *src++;
+	    *udest++ = *src++;
+	    *dest++ = *src++;
+	    *vdest++ = *src++;
+	    ll -= 4;
+	  } else { /* Odd line only Y values */
+	    *dest++ = *src++;
+	    *dest++ = *src++;
+	    ll -= 2;
+	  }
+	  x += 2;
+	}
+      }
+      if (ll != 1 || x != width) {
+	fprintf(stderr, "cpia1 decode error: line length mismatch\n");
+	return -1;
+      }
+      src++; /* Skip EOL */
+    }
+  }
+
+  if (y != height) {
+    fprintf(stderr, "cpia1 decode error: frame height mismatch\n");
+    return -1;
+  }
+
+  if (src_size < 4 ||
+      src[src_size - 4] != EOI || src[src_size - 3] != EOI ||
+      src[src_size - 2] != EOI || src[src_size - 1] != EOI) {
+    fprintf(stderr, "cpia1 decode error: invaled EOI marker\n");
+    return -1;
+  }
+
+  /* Safe frame for decompression of the next frame */
+  dest -= width * height;
+  memcpy(data->previous_frame, dest, width * height * 3 / 2);
+
+  return 0;
+}
--- libv4lconvert/crop.c
+++ libv4lconvert/crop.c
@@ -2,7 +2,7 @@
 
 # RGB and YUV crop routines
 
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -69,8 +69,8 @@
   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;
-  int starty = src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height;
+  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 */
@@ -113,8 +113,8 @@
   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;
+  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 */
@@ -143,26 +143,146 @@
   }
 }
 
+/* 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  >= 2 * dest_fmt->fmt.pix.width &&
-	  src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height)
+      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  >= 2 * dest_fmt->fmt.pix.width &&
-	  src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height)
+      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;
   }
 }
--- libv4lconvert/flip.c
+++ libv4lconvert/flip.c
@@ -2,7 +2,7 @@
 
 # RGB / YUV flip/rotate routines
 
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -20,8 +20,100 @@
 
 */
 
+#include <string.h>
 #include "libv4lconvert-priv.h"
 
+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)
 {
@@ -106,37 +198,70 @@
     }
 }
 
-void v4lconvert_rotate(unsigned char *src, unsigned char *dest,
-  int width, int height, unsigned int pix_fmt, int rotate)
+void v4lconvert_rotate90(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt)
+{
+  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;
+  }
+  v4lconvert_fixup_fmt(fmt);
+}
+
+void v4lconvert_flip(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt, int hflip, int vflip)
 {
-  switch (rotate) {
-  case 0:
-    break;
-  case 90:
-    switch (pix_fmt) {
+  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_rotate90_rgbbgr24(src, dest, width, height);
+	v4lconvert_hflip_rgbbgr24(src, dest, fmt);
 	break;
       case V4L2_PIX_FMT_YUV420:
       case V4L2_PIX_FMT_YVU420:
-	v4lconvert_rotate90_yuv420(src, dest, width, height);
+	v4lconvert_hflip_yuv420(src, dest, fmt);
 	break;
     }
-    break;
-  case 180:
-    switch (pix_fmt) {
+  } else if (vflip) {
+    switch (fmt->fmt.pix.pixelformat) {
       case V4L2_PIX_FMT_RGB24:
       case V4L2_PIX_FMT_BGR24:
-	v4lconvert_rotate180_rgbbgr24(src, dest, width, height);
+	v4lconvert_vflip_rgbbgr24(src, dest, fmt);
 	break;
       case V4L2_PIX_FMT_YUV420:
       case V4L2_PIX_FMT_YVU420:
-	v4lconvert_rotate180_yuv420(src, dest, width, height);
+	v4lconvert_vflip_yuv420(src, dest, fmt);
 	break;
     }
-    break;
-  default:
-    printf("FIXME add %d degrees rotation\n", rotate);
   }
+
+  /* Our newly written data has no padding */
+  v4lconvert_fixup_fmt(fmt);
 }
--- libv4lconvert/helper-funcs.h
+++ libv4lconvert/helper-funcs.h
+/* Utility functions for decompression helpers
+ *
+ * Copyright (c) 2009 Hans de Goede <hdegoede at 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;
+}
--- libv4lconvert/helper.c
+++ libv4lconvert/helper.c
+/*
+#             (C) 2009 Hans de Goede <hdegoede at 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;
+  }
+}
--- libv4lconvert/libv4lconvert-priv.h
+++ libv4lconvert/libv4lconvert-priv.h
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -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
@@ -47,6 +52,10 @@
 #define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M','3','1','0')
 #endif
 
+#ifndef V4L2_PIX_FMT_SN9C2028
+#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X')
+#endif
+
 #ifndef V4L2_PIX_FMT_SQ905C
 #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C')
 #endif
@@ -79,6 +88,28 @@
 #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
+
+#ifndef V4L2_PIX_FMT_STV0680
+#define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0')
+#endif
+
+#ifndef V4L2_PIX_FMT_CPIA1
+#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
+#endif
+
+#ifndef V4L2_FMT_FLAG_EMULATED
+#define V4L2_FMT_FLAG_EMULATED 0x0002
+#endif
+
+#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0]))
+
 #define V4LCONVERT_ERROR_MSG_SIZE 256
 #define V4LCONVERT_MAX_FRAMESIZES 16
 
@@ -87,37 +118,47 @@
   "v4l-convert: error " __VA_ARGS__)
 
 /* Card flags */
-#define V4LCONVERT_ROTATE_90  0x01
-#define V4LCONVERT_ROTATE_180 0x02
-#define V4LCONVERT_IS_UVC     0x04
+#define V4LCONVERT_IS_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 */
+#define V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION 0x03
 
 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;
-  int convert_buf_size;
-  int rotate_buf_size;
+  int convert1_buf_size;
+  int convert2_buf_size;
+  int rotate90_buf_size;
+  int flip_buf_size;
   int convert_pixfmt_buf_size;
-  unsigned char *convert_buf;
-  unsigned char *rotate_buf;
+  unsigned char *convert1_buf;
+  unsigned char *convert2_buf;
+  unsigned char *rotate90_buf;
+  unsigned char *flip_buf;
   unsigned char *convert_pixfmt_buf;
-};
+  struct v4lcontrol_data *control;
+  struct v4lprocessing_data *processing;
 
-struct v4lconvert_flags_info {
-  unsigned short vendor_id;
-  unsigned short product_id;
-/* We could also use the USB manufacturer and product strings some devices have
-  const char *manufacturer;
-  const char *product; */
-  int flags;
+  /* 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 */
+
+  /* For mr97310a decoder */
+  int frames_dropped;
+
+  /* For cpia1 decoder */
+  unsigned char* previous_frame;
 };
 
 struct v4lconvert_pixfmt {
@@ -125,6 +166,8 @@
   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);
 
@@ -164,6 +207,15 @@
 void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dst,
   const struct v4l2_format *src_fmt);
 
+void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest,
+  int width, int height);
+
+void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest,
+  int width, int height);
+
+void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest,
+  const struct v4l2_format *src_fmt, int yvu);
+
 void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst,
   int width, int height, int yvu);
 
@@ -173,6 +225,10 @@
 void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst,
   int width, int height, int yvu);
 
+int v4lconvert_cpia1_to_yuv420(struct v4lconvert_data *data,
+  const unsigned char *src, int src_size,
+  unsigned char *dst, int width, int height, int yvu);
+
 void v4lconvert_sn9c20x_to_yuv420(const unsigned char *src, unsigned char *dst,
   int width, int height, int yvu);
 
@@ -182,15 +238,23 @@
 void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst,
   int width, int height);
 
-void v4lconvert_decode_pac207(const unsigned char *src, unsigned char *dst,
+int v4lconvert_decode_pac207(struct v4lconvert_data *data,
+  const unsigned char *inp, int src_size, unsigned char *outp,
+  int width, int height);
+
+int v4lconvert_decode_mr97310a(struct v4lconvert_data *data,
+  const unsigned char *src, int src_size, unsigned char *dst,
   int width, int height);
 
-void v4lconvert_decode_mr97310a(const unsigned char *src, unsigned char *dst,
+void v4lconvert_decode_sn9c2028(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_decode_stv0680(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);
 
@@ -209,10 +273,19 @@
 void v4lconvert_hm12_to_yuv420(const unsigned char *src,
   unsigned char *dst, int width, int height, int yvu);
 
-void v4lconvert_rotate(unsigned char *src, unsigned char *dest,
-  int width, int height, unsigned int pix_fmt, int rotate);
+void v4lconvert_rotate90(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt);
+
+void v4lconvert_flip(unsigned char *src, unsigned char *dest,
+  struct v4l2_format *fmt, int 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
--- libv4lconvert/libv4lconvert.c
+++ libv4lconvert/libv4lconvert.c
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
@@ -19,15 +19,14 @@
 #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 */
@@ -47,37 +46,35 @@
   { V4L2_PIX_FMT_YUYV,         0 },
   { V4L2_PIX_FMT_YVYU,         0 },
   { V4L2_PIX_FMT_UYVY,         0 },
-  { V4L2_PIX_FMT_SN9C20X_I420, 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_HM12,         0 },
+  { V4L2_PIX_FMT_RGB565,       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_STV0680,      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_CPIA1,        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_SPCA561,      V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_SN9C10X,      V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_SN9C2028,     V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_PAC207,       V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_MR97310A,     V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_SQ905C,       V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_PJPG,         V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_OV511,        V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
+  { V4L2_PIX_FMT_OV518,        V4LCONVERT_COMPRESSED_AND_NEEDS_CONVERSION },
 };
 
 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[] = {
-  { 0x0471, 0x0325, V4LCONVERT_ROTATE_180 }, /* Philips SPC200NC */
-  { 0x0471, 0x0326, V4LCONVERT_ROTATE_180 }, /* Philips SPC300NC */
-  { 0x0471, 0x032d, V4LCONVERT_ROTATE_180 }, /* Philips SPC210NC */
-  { 0x093a, 0x2476, V4LCONVERT_ROTATE_180 }, /* Genius E-M 112 */
-};
-
 /* List of well known resolutions which we can get by cropping somewhat larger
    resolutions */
 static const int v4lconvert_crop_res[][2] = {
@@ -91,56 +88,23 @@
   { 176, 144 },
 };
 
-static int v4lconvert_get_flags(int fd)
-{
-  struct stat st;
-  FILE *f;
-  char sysfs_name[512];
-  unsigned short vendor_id = 0;
-  unsigned short product_id = 0;
-  int i;
-
-  if (fstat(fd, &st) || !S_ISCHR(st.st_mode))
-    return 0; /* Should never happen */
-
-  /* Get product ID */
-  snprintf(sysfs_name, sizeof(sysfs_name),
-	   "/sys/class/video4linux/video%d/device/idVendor",
-	   minor(st.st_rdev));
-  f = fopen(sysfs_name, "r");
-  if (!f)
-    return 0; /* Not an USB device (or no sysfs) */
-  i = fscanf(f, "%hx", &vendor_id);
-  fclose(f);
-
-  /* Get product ID */
-  snprintf(sysfs_name, sizeof(sysfs_name),
-	   "/sys/class/video4linux/video%d/device/idProduct",
-	   minor(st.st_rdev));
-  f = fopen(sysfs_name, "r");
-  if (!f)
-    return 0; /* Should never happen */
-  i = fscanf(f, "%hx", &product_id);
-  fclose(f);
-
-  for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++)
-    if (v4lconvert_flags[i].vendor_id == vendor_id &&
-	v4lconvert_flags[i].product_id == product_id)
-      return v4lconvert_flags[i].flags;
-
-  return 0;
-}
-
 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)
+  if (!data) {
+    fprintf(stderr, "libv4lconvert: error: out of memory!\n");
     return NULL;
+  }
 
   data->fd = fd;
+  data->decompress_pid = -1;
 
   /* Check supported formats */
   for (i = 0; ; i++) {
@@ -148,24 +112,47 @@
 
     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 & V4LCONVERT_NEEDS_CONVERSION))
+	  always_needs_conversion = 0;
 	break;
       }
+
+    if (j == ARRAY_SIZE(supported_src_pixfmts))
+      always_needs_conversion = 0;
   }
 
   data->no_formats = i;
 
   /* Check if this cam has any special flags */
-  data->flags = v4lconvert_get_flags(data->fd);
-  if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) {
+  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;
@@ -173,18 +160,24 @@
 
 void v4lconvert_destroy(struct v4lconvert_data *data)
 {
+  v4lprocessing_destroy(data->processing);
+  v4lcontrol_destroy(data->control);
   if (data->jdec) {
     unsigned char *comps[3] = { NULL, NULL, NULL };
     tinyjpeg_set_components(data->jdec, comps, 3);
     tinyjpeg_free(data->jdec);
   }
-  free(data->convert_buf);
-  free(data->rotate_buf);
+  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->previous_frame);
   free(data);
 }
 
-static int v4lconvert_supported_dst_format(unsigned int pixelformat)
+int v4lconvert_supported_dst_format(unsigned int pixelformat)
 {
   int i;
 
@@ -195,6 +188,12 @@
   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)
 {
@@ -202,23 +201,28 @@
   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;
   }
 
-  fmt->flags = 0;
+  fmt->flags = V4L2_FMT_FLAG_EMULATED;
   fmt->pixelformat = faked_fmts[i];
   fmt->description[0] = faked_fmts[i] & 0xff;
   fmt->description[1] = (faked_fmts[i] >> 8) & 0xff;
@@ -307,7 +311,7 @@
     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 -
@@ -339,7 +343,7 @@
   return 0;
 }
 
-static void v4lconvert_fixup_fmt(struct v4l2_format *fmt)
+void v4lconvert_fixup_fmt(struct v4l2_format *fmt)
 {
   switch (fmt->fmt.pix.pixelformat) {
   case V4L2_PIX_FMT_RGB24:
@@ -362,27 +366,58 @@
   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 = *dest_fmt;
+  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 = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt);
+    result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt);
     if (src_fmt)
       *src_fmt = *dest_fmt;
     return result;
   }
 
+  /* 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;
+    }
+  }
+
   /* 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 */
+     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) {
-	struct v4l2_format try2_src, try2_dest = *dest_fmt;
+	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 */
@@ -390,13 +425,20 @@
 	try2_dest.fmt.pix.height = desired_height * 124 / 100;
 	result = v4lconvert_do_try_format(data, &try2_dest, &try2_src);
 	if (result == 0 &&
-	    ((try2_dest.fmt.pix.width >= desired_width &&
+	    (/* 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 &&
+	      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;
@@ -409,7 +451,15 @@
     }
   }
 
-  /* Are we converting? */
+  /* 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)
@@ -427,19 +477,42 @@
   const struct v4l2_format *src_fmt,  /* in */
   const struct v4l2_format *dest_fmt) /* in */
 {
-  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)
-    return 1; /* Formats differ */
-
-  if (!(data->flags & (V4LCONVERT_ROTATE_90|V4LCONVERT_ROTATE_180)))
-    return 0; /* Formats identical and we don't need flip */
-
-  /* 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 */
+  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;
+
+  return 0;
+}
+
+static int v4lconvert_processing_needs_double_conversion(
+  unsigned int src_pix_fmt, unsigned int dest_pix_fmt)
+{
+  switch (src_pix_fmt) {
+    case V4L2_PIX_FMT_RGB24:
+    case V4L2_PIX_FMT_BGR24:
+    case V4L2_PIX_FMT_SPCA561:
+    case V4L2_PIX_FMT_SN9C10X:
+    case V4L2_PIX_FMT_PAC207:
+    case V4L2_PIX_FMT_MR97310A:
+    case V4L2_PIX_FMT_SN9C2028:
+    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:
+    case V4L2_PIX_FMT_STV0680:
+      return 0;
+  }
+  switch (dest_pix_fmt) {
+    case V4L2_PIX_FMT_RGB24:
+    case V4L2_PIX_FMT_BGR24:
+      return 0;
+  }
+
+  return 1;
 }
 
 static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data,
@@ -460,15 +533,15 @@
 }
 
 static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
-  unsigned char *src, int src_size, unsigned char *dest,
-  const struct v4l2_format *src_fmt, unsigned int dest_pix_fmt)
+  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 = src_fmt->fmt.pix.pixelformat;
-  unsigned int width  = src_fmt->fmt.pix.width;
-  unsigned int height = src_fmt->fmt.pix.height;
+  unsigned int src_pix_fmt = fmt->fmt.pix.pixelformat;
+  unsigned int width  = fmt->fmt.pix.width;
+  unsigned int height = fmt->fmt.pix.height;
 
   switch (src_pix_fmt) {
     case V4L2_PIX_FMT_PJPG:
@@ -496,12 +569,14 @@
       if (header_width != width || header_height != height) {
 	/* Check for (pixart) rotated JPEG */
 	if (header_width == height && header_height == width) {
-	  if (!(data->flags & V4LCONVERT_ROTATE_90)) {
-	    V4LCONVERT_ERR("JPEG needs 90 degree rotation\n");
-	    data->flags |= V4LCONVERT_ROTATE_90;
-	    errno = EAGAIN;
+	  if (!(data->control_flags & V4LCONTROL_ROTATED_90_JPEG)) {
+	    V4LCONVERT_ERR("JPEG needs 90° rotation, please report "
+			   "this to <hdegoede at 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", width, height,
@@ -509,6 +584,13 @@
 	  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 at 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;
@@ -541,7 +623,8 @@
 	   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_pix_fmt == V4L2_PIX_FMT_PJPG) {
+	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;
@@ -561,8 +644,12 @@
     case V4L2_PIX_FMT_SPCA505:
     case V4L2_PIX_FMT_SPCA508:
     case V4L2_PIX_FMT_SN9C20X_I420:
+    case V4L2_PIX_FMT_CPIA1:
+    case V4L2_PIX_FMT_OV511:
+    case V4L2_PIX_FMT_OV518:
     {
       unsigned char *d;
+      int d_size;
       int yvu = 0;
 
       if (dest_pix_fmt != V4L2_PIX_FMT_YUV420 &&
@@ -571,8 +658,11 @@
 	      &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size);
 	if (!d)
 	  return -1;
-      } else
+	d_size = width * height * 3 / 2;
+      } else {
 	d = dest;
+	d_size = dest_size;
+      }
 
       if (dest_pix_fmt == V4L2_PIX_FMT_YVU420)
 	yvu = 1;
@@ -590,6 +680,30 @@
 	case V4L2_PIX_FMT_SN9C20X_I420:
 	  v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu);
 	  break;
+	case V4L2_PIX_FMT_CPIA1:
+	  if (v4lconvert_cpia1_to_yuv420(data, src, src_size, d,
+					 width, height, yvu)) {
+	    /* Corrupt frame, better get another one */
+	    errno = EAGAIN;
+	    return -1;
+	  }
+	  break;
+	case V4L2_PIX_FMT_OV511:
+	  if (v4lconvert_helper_decompress(data, LIBDIR "/" LIBSUBDIR "/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 "/" LIBSUBDIR "/ov518-decomp",
+		     src, src_size, d, d_size, width, height, yvu)) {
+	    /* Corrupt frame, better get another one */
+	    errno = EAGAIN;
+	    return -1;
+	  }
+	  break;
       }
 
       switch (dest_pix_fmt) {
@@ -628,9 +742,12 @@
     case V4L2_PIX_FMT_SN9C10X:
     case V4L2_PIX_FMT_PAC207:
     case V4L2_PIX_FMT_MR97310A:
+    case V4L2_PIX_FMT_SN9C2028:
     case V4L2_PIX_FMT_SQ905C:
+    case V4L2_PIX_FMT_STV0680: /* Not compressed but needs some shuffling */
     {
       unsigned char *tmpbuf;
+      struct v4l2_format tmpfmt = *fmt;
 
       tmpbuf = v4lconvert_alloc_buffer(data, width * height,
 	    &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size);
@@ -640,27 +757,51 @@
       switch (src_pix_fmt) {
 	case V4L2_PIX_FMT_SPCA561:
 	  v4lconvert_decode_spca561(src, tmpbuf, width, height);
-	  src_pix_fmt = V4L2_PIX_FMT_SGBRG8;
+	  tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG8;
 	  break;
 	case V4L2_PIX_FMT_SN9C10X:
 	  v4lconvert_decode_sn9c10x(src, tmpbuf, width, height);
-	  src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+	  tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
 	  break;
 	case V4L2_PIX_FMT_PAC207:
-	  v4lconvert_decode_pac207(src, tmpbuf, width, height);
-	  src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+	  if (v4lconvert_decode_pac207(data, src, src_size, tmpbuf,
+				       width, height)) {
+	    /* Corrupt frame, better get another one */
+	    errno = EAGAIN;
+	    return -1;
+	  }
+	  tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
 	  break;
 	case V4L2_PIX_FMT_MR97310A:
-	  v4lconvert_decode_mr97310a(src, tmpbuf, width, height);
-	  src_pix_fmt = V4L2_PIX_FMT_SBGGR8;
+	  if (v4lconvert_decode_mr97310a(data, src, src_size, tmpbuf,
+					 width, height)) {
+	    /* Corrupt frame, better get another one */
+	    errno = EAGAIN;
+	    return -1;
+	  }
+	  tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
+	  break;
+	case V4L2_PIX_FMT_SN9C2028:
+	  v4lconvert_decode_sn9c2028(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);
-	  src_pix_fmt = V4L2_PIX_FMT_SRGGB8;
+	  tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
+	  break;
+	case V4L2_PIX_FMT_STV0680:
+	  v4lconvert_decode_stv0680(src, tmpbuf, width, height);
+	  tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
 	  break;
       }
-      src = tmpbuf;
+      /* Do processing on the tmp buffer, because doing it on bayer data is
+	 cheaper, and bayer == rgb and our dest_fmt may be yuv */
+      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;
     }
 
     /* Raw bayer formats */
@@ -684,16 +825,33 @@
       }
       break;
 
+    case V4L2_PIX_FMT_RGB565:
+      switch (dest_pix_fmt) {
+      case V4L2_PIX_FMT_RGB24:
+	v4lconvert_rgb565_to_rgb24(src, dest, width, height);
+	break;
+      case V4L2_PIX_FMT_BGR24:
+	v4lconvert_rgb565_to_bgr24(src, dest, width, height);
+	break;
+      case V4L2_PIX_FMT_YUV420:
+	v4lconvert_rgb565_to_yuv420(src, dest, fmt, 0);
+	break;
+      case V4L2_PIX_FMT_YVU420:
+	v4lconvert_rgb565_to_yuv420(src, dest, fmt, 1);
+	break;
+      }
+      break;
+
     case V4L2_PIX_FMT_RGB24:
       switch (dest_pix_fmt) {
       case V4L2_PIX_FMT_BGR24:
 	v4lconvert_swap_rgb(src, dest, width, height);
 	break;
       case V4L2_PIX_FMT_YUV420:
-	v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 0);
+	v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0);
 	break;
       case V4L2_PIX_FMT_YVU420:
-	v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 1);
+	v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1);
 	break;
       }
       break;
@@ -704,10 +862,10 @@
 	v4lconvert_swap_rgb(src, dest, width, height);
 	break;
       case V4L2_PIX_FMT_YUV420:
-	v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 0);
+	v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0);
 	break;
       case V4L2_PIX_FMT_YVU420:
-	v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 1);
+	v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1);
 	break;
       }
       break;
@@ -723,7 +881,7 @@
 				   height, 0);
 	break;
       case V4L2_PIX_FMT_YVU420:
-	v4lconvert_swap_uv(src, dest, src_fmt);
+	v4lconvert_swap_uv(src, dest, fmt);
 	break;
       }
       break;
@@ -739,7 +897,7 @@
 				   height, 1);
 	break;
       case V4L2_PIX_FMT_YUV420:
-	v4lconvert_swap_uv(src, dest, src_fmt);
+	v4lconvert_swap_uv(src, dest, fmt);
 	break;
       }
       break;
@@ -802,6 +960,10 @@
       errno = EINVAL;
       return -1;
   }
+
+  fmt->fmt.pix.pixelformat = dest_pix_fmt;
+  v4lconvert_fixup_fmt(fmt);
+
   return 0;
 }
 
@@ -810,14 +972,31 @@
   const struct v4l2_format *dest_fmt, /* in */
   unsigned char *src, int src_size, unsigned char *dest, int dest_size)
 {
-  int res, dest_needed, temp_needed, convert = 0, rotate = 0, crop = 0;
-  unsigned char *convert_dest = dest, *rotate_src = src, *rotate_dest = dest;
+  int res, dest_needed, temp_needed, processing, convert = 0;
+  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;
 
-  /* Special case when no conversion is needed */
-  if (!v4lconvert_needs_conversion(data, src_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;
@@ -851,57 +1030,102 @@
   }
 
   if (dest_size < dest_needed) {
-    V4LCONVERT_ERR("destination buffer too small\n");
+    V4LCONVERT_ERR("destination buffer too small (%d < %d)\n",
+		   dest_size, dest_needed);
     errno = EFAULT;
     return -1;
   }
 
-  if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat)
+
+  /* Sometimes we need foo -> rgb -> bar as video processing (whitebalance,
+     etc.) can only be done on rgb data */
+  if (processing && v4lconvert_processing_needs_double_conversion(
+					     my_src_fmt.fmt.pix.pixelformat,
+					     my_dest_fmt.fmt.pix.pixelformat))
+    convert = 2;
+  else if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat)
     convert = 1;
 
-  if (data->flags & V4LCONVERT_ROTATE_90)
-    rotate += 90;
-  if (data->flags & V4LCONVERT_ROTATE_180)
-    rotate += 180;
-
-  if (my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width ||
-      my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height)
-    crop = 1;
-
-  /* convert_pixfmt -> rotate -> crop, all steps are optional */
-  if (convert && (rotate || crop)) {
-    convert_dest = v4lconvert_alloc_buffer(data, temp_needed,
-		     &data->convert_buf, &data->convert_buf_size);
-    if (!convert_dest)
+  /* convert_pixfmt (only if convert == 2) -> processing -> convert_pixfmt ->
+     rotate -> flip -> crop, all steps are optional */
+  if (convert == 2) {
+    convert1_dest = v4lconvert_alloc_buffer(data,
+		     my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3,
+		     &data->convert1_buf, &data->convert1_buf_size);
+    if (!convert1_dest)
+      return -1;
+
+    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;
 
-    rotate_src = crop_src = convert_dest;
+    flip_src = crop_src = rotate90_dest;
   }
 
-  if (rotate && crop) {
-    rotate_dest = v4lconvert_alloc_buffer(data, temp_needed,
-		    &data->rotate_buf, &data->rotate_buf_size);
-    if (!rotate_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 = rotate_dest;
+    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, src, src_size, convert_dest,
+    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;
 
-    my_src_fmt.fmt.pix.pixelformat = my_dest_fmt.fmt.pix.pixelformat;
-    v4lconvert_fixup_fmt(&my_src_fmt);
+    src_size = my_src_fmt.fmt.pix.sizeimage;
+
+    /* 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 (rotate)
-    v4lconvert_rotate(rotate_src, rotate_dest,
-		      my_src_fmt.fmt.pix.width, my_src_fmt.fmt.pix.height,
-		      my_src_fmt.fmt.pix.pixelformat, rotate);
+  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);
@@ -922,7 +1146,7 @@
 
   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 */
@@ -977,8 +1201,13 @@
 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;
@@ -989,6 +1218,9 @@
   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:
@@ -1006,7 +1238,11 @@
   struct v4l2_format src_fmt, dest_fmt;
 
   if (!v4lconvert_supported_dst_format(frmival->pixel_format)) {
-    res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+    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;
@@ -1050,7 +1286,7 @@
   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;
@@ -1076,3 +1312,15 @@
 
   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);
+}
--- libv4lconvert/libv4lsyscall-priv.h
+++ libv4lconvert/libv4lsyscall-priv.h
+/*-
+ * 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	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));
+
+#ifdef __FreeBSD__
+#define SYS_MMAP(addr, len, prot, flags, fd, off) \
+    __syscall(SYS_mmap, (void *)(addr), (size_t)(len), \
+	(int)(prot), (int)(flags), (int)(fd), (__off_t)(off))
+#else
+#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))
+#endif
+
+#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_ */
--- libv4lconvert/mr97310a.c
+++ libv4lconvert/mr97310a.c
@@ -1,7 +1,7 @@
 /*
  * MR97310A decoder
  *
- * Copyright (C) 2004 Theodore Kilgore <kilgota at auburn.edu>
+ * Copyright (C) 2004-2009 Theodore Kilgore <kilgota at auburn.edu>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,10 +19,14 @@
  * Boston, MA 02111-1307, USA.
  */
 
+#include <unistd.h>
 #include "libv4lconvert-priv.h"
+#include "libv4lsyscall-priv.h"
 
 #define CLIP(x) ((x)<0?0:((x)>0xff)?0xff:(x))
 
+#define MIN_CLOCKDIV_CID V4L2_CID_PRIVATE_BASE
+
 /* FIXME not threadsafe */
 static int decoder_initialized = 0;
 
@@ -55,19 +59,19 @@
 			len = 3;
 		} else if ((i & 0xf0) == 0x80) {
 			/* code 1000 */
-			val = +7;
+			val = +8;
 			len = 4;
 		} else if ((i & 0xf0) == 0x90) {
 			/* code 1001 */
-			val = -7;
+			val = -8;
 			len = 4;
 		} else if ((i & 0xf0) == 0xf0) {
 			/* code 1111 */
-			val = -15;
+			val = -20;
 			len = 4;
 		} else if ((i & 0xf8) == 0xe0) {
 			/* code 11100 */
-			val = +15;
+			val = +20;
 			len = 5;
 		} else if ((i & 0xf8) == 0xe8) {
 			/* code 11101xxxxx */
@@ -90,20 +94,22 @@
 	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 v4lconvert_decode_mr97310a(struct v4lconvert_data *data,
+			       const unsigned char *inp, int src_size,
+			       unsigned char *outp, int width, int height)
 {
 	int row, col;
 	int val;
 	int bitpos;
 	unsigned char code;
 	unsigned char lp, tp, tlp, trp;
+	struct v4l2_control min_clockdiv = { .id = MIN_CLOCKDIV_CID };
 
 	if (!decoder_initialized)
 		init_mr97310a_decoder();
 
 	/* remove the header */
-		inp += 12;
+	inp += 12;
 
 	bitpos = 0;
 
@@ -152,21 +158,51 @@
 				} else if (col < 2) {
 					/* left column: relative to top pixel */
 					/* initial estimate */
-					val += (2*tp + 2*trp + 1)/4;
+					val += (tp + trp)/2;
 				} 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 */
+					val += (tp + lp + tlp + 1)/3;
+				/* main area: weighted average of tlp, trp,
+				 * lp, and tp */
 				} else {
+					tlp>>=1;
+					trp>>=1;
 					/* initial estimate for predictor */
-					val += (2*lp + tp + trp + 1)/4;
+					val += (lp + tp + tlp + trp + 1)/3;
 				}
 			}
 			/* store pixel */
 			*outp++ = CLIP(val);
 			++col;
 		}
+
+		/* src_size - 12 because of 12 byte footer */
+		if (((bitpos - 1) / 8) >= (src_size - 12)) {
+			data->frames_dropped++;
+			if (data->frames_dropped == 3) {
+				/* Tell the driver to go slower as
+				   the compression engine is not able to
+				   compress the image enough, we may
+				   fail to do this because older
+				   drivers don't support this */
+				SYS_IOCTL(data->fd, VIDIOC_G_CTRL,
+					  &min_clockdiv);
+				min_clockdiv.value++;
+				SYS_IOCTL(data->fd, VIDIOC_S_CTRL,
+					  &min_clockdiv);
+				/* We return success here, because if we
+				   return failure for too many frames in a row
+				   libv4l2 will return an error to the
+				   application and some applications abort
+				   on the first error received. */
+				data->frames_dropped = 0;
+				return 0;
+			}
+			V4LCONVERT_ERR("incomplete mr97310a frame\n");
+			return -1;
+		}
 	}
 
-	return;
+	data->frames_dropped = 0;
+	return 0;
 }
--- libv4lconvert/ov511-decomp.c
+++ libv4lconvert/ov511-decomp.c
+/* 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 */
+  }
+}
--- libv4lconvert/ov518-decomp.c
+++ libv4lconvert/ov518-decomp.c
+/* 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 */
+  }
+}
--- libv4lconvert/pac207.c
+++ libv4lconvert/pac207.c
@@ -52,35 +52,35 @@
 	    len = 2;
 	} else if ((i & 0xC0) == 0x40) {
 	    /* code 01 */
-	    val = -5;
+	    val = -1;
 	    len = 2;
 	} else if ((i & 0xC0) == 0x80) {
 	    /* code 10 */
-	    val = +5;
+	    val = +1;
 	    len = 2;
 	} else if ((i & 0xF0) == 0xC0) {
 	    /* code 1100 */
-	    val = -10;
+	    val = -2;
 	    len = 4;
 	} else if ((i & 0xF0) == 0xD0) {
 	    /* code 1101 */
-	    val = +10;
+	    val = +2;
 	    len = 4;
 	} else if ((i & 0xF8) == 0xE0) {
 	    /* code 11100 */
-	    val = -15;
+	    val = -3;
 	    len = 5;
 	} else if ((i & 0xF8) == 0xE8) {
 	    /* code 11101 */
-	    val = +15;
+	    val = +3;
 	    len = 5;
 	} else if ((i & 0xFC) == 0xF0) {
 	    /* code 111100 */
-	    val = -20;
+	    val = -4;
 	    len = 6;
 	} else if ((i & 0xFC) == 0xF4) {
 	    /* code 111101 */
-	    val = +20;
+	    val = +4;
 	    len = 6;
 	} else if ((i & 0xF8) == 0xF8) {
 	    /* code 11111xxxxxx */
@@ -109,7 +109,8 @@
 }
 
 static int
-pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width)
+pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width,
+    int step_size, int abs_bits)
 {
     int col;
     int val;
@@ -135,11 +136,11 @@
 	if (table[code].is_abs) {
 	    /* absolute value: get 6 more bits */
 	    code = getByte(inp, bitpos);
-	    bitpos += 6;
-	    *outp++ = code & 0xFC;
+	    bitpos += abs_bits;
+	    *outp++ = code & ~(0xff >> abs_bits);
 	} else {
 	    /* relative to left pixel */
-	    val = outp[-2] + table[code].val;
+	    val = outp[-2] + table[code].val * step_size;
 	    *outp++ = CLIP(val);
 	}
     }
@@ -148,18 +149,24 @@
     return 2 * ((bitpos + 15) / 16);
 }
 
-void v4lconvert_decode_pac207(const unsigned char *inp, unsigned char *outp,
+int v4lconvert_decode_pac207(struct v4lconvert_data *data,
+  const unsigned char *inp, int src_size, unsigned char *outp,
   int width, int height)
 {
 /* we should received a whole frame with header and EOL marker
 in myframe->data and return a GBRG pattern in frame->tmpbuffer
 remove the header then copy line by line EOL is set with 0x0f 0xf0 marker
 or 0x1e 0xe1 for compressed line*/
+    const unsigned char *end = inp + src_size;
     unsigned short word;
     int row;
 
     /* iterate over all rows */
     for (row = 0; row < height; row++) {
+	if ((inp + 2) > end) {
+	    V4LCONVERT_ERR("incomplete pac207 frame\n");
+	    return -1;
+	}
 	word = getShort(inp);
 	switch (word) {
 	case 0x0FF0:
@@ -167,21 +174,31 @@
 	    inp += (2 + width);
 	    break;
 	case 0x1EE1:
-	    inp += pac_decompress_row(inp, outp, width);
+	    inp += pac_decompress_row(inp, outp, width, 5, 6);
+	    break;
+
+	case 0x2DD2:
+	    inp += pac_decompress_row(inp, outp, width, 9, 5);
 	    break;
 
-	case 0x2DD2: /* prefix for "stronger" compressed lines, currently the
-			kernel driver programs the cam so that we should not
-			get any of these */
+	case 0x3CC3:
+	    inp += pac_decompress_row(inp, outp, width, 17, 4);
+	    break;
+
+	case 0x4BB4:
+	    /* skip or copy line? */
+	    memcpy(outp, outp - 2 * width, width);
+	    inp += 2;
+	    break;
 
 	default: /* corrupt frame */
-	    /* FIXME add error reporting */
-	    return;
+	    V4LCONVERT_ERR("unknown pac207 row header: 0x%04x\n", (int)word);
+	    return -1;
 	}
 	outp += width;
     }
 
-    return;
+    return 0;
 }
 
 
--- libv4lconvert/processing
+++ libv4lconvert/processing
+(directory)
--- libv4lconvert/processing/autogain.c
+++ libv4lconvert/processing/autogain.c
+/*
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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) {
+  int autogain;
+
+  autogain = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN);
+  if (!autogain) {
+    /* Reset last_correction val */
+    data->last_gain_correction = 0;
+  }
+
+  return autogain;
+}
+
+/* Adjust ctrl value with steps steps, while not crossing limit */
+static int autogain_adjust(struct v4l2_queryctrl *ctrl, int *value,
+  int steps, int limit)
+{
+  int ctrl_range = (ctrl->maximum - ctrl->minimum) / ctrl->step;
+
+  /* If we are of 3 * deadzone or more, and we have a very fine grained
+     control, take larger steps, otherwise we take ages to get to the
+     right setting point. We use 256 as tripping point for determineing fine
+     grained controls here, as avg_lum has a range of 0 - 255. */
+  if (abs(steps) >= 3 && ctrl_range > 256)
+    *value += steps * ctrl->step * (ctrl_range / 256);
+  else
+    *value += steps * ctrl->step;
+
+  if (steps > 0) {
+    if (*value > limit)
+      *value = limit;
+  } else {
+    if (*value < limit)
+      *value = limit;
+  }
+}
+
+/* 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, exposure_low;
+  struct v4l2_control ctrl;
+  struct v4l2_queryctrl gainctrl, expoctrl;
+  const int deadzone = 6;
+
+  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;
+  /* Determine a value below which we try to not lower the exposure,
+     as most exposure controls tend to jump with big steps in the low
+     range, causing oscilation, so we prefer to use gain when exposure
+     has hit this value */
+  exposure_low = expoctrl.maximum / 10;
+  /* If we have a fine grained exposure control only avoid the last 10 steps */
+  steps = exposure_low / expoctrl.step;
+  if (steps > 10)
+    steps = 10;
+  exposure_low = steps * expoctrl.step + expoctrl.minimum;
+
+  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 = (target - avg_lum) / deadzone;
+
+  /* If we were decreasing and are now increasing, or vica versa, half the
+     number of steps to avoid overshooting and oscilating */
+  if ((steps > 0 && data->last_gain_correction < 0) ||
+      (steps < 0 && data->last_gain_correction > 0))
+    steps /= 2;
+
+  if (steps == 0)
+    return 0; /* Nothing to do */
+
+  if (steps < 0) {
+    if (exposure > expoctrl.default_value)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.default_value);
+    else if (gain > gainctrl.default_value)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.default_value);
+    else if (exposure > exposure_low)
+      autogain_adjust(&expoctrl, &exposure, steps, exposure_low);
+    else if (gain > gainctrl.minimum)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.minimum);
+    else if (exposure > expoctrl.minimum)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.minimum);
+    else
+      steps = 0;
+  } else {
+    if (exposure < exposure_low)
+      autogain_adjust(&expoctrl, &exposure, steps, exposure_low);
+    else if (gain < gainctrl.default_value)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.default_value);
+    else if (exposure < expoctrl.default_value)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.default_value);
+    else if (gain < gainctrl.maximum)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.maximum);
+    else if (exposure < expoctrl.maximum)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.maximum);
+    else
+      steps = 0;
+  }
+
+  if (steps) {
+    data->last_gain_correction = steps;
+    /* We are still settling down, force the next update sooner. Note we
+       skip the next frame as that is still captured with the old settings,
+       and another one just to be sure (because if we re-adjust based
+       on the old settings we might overshoot). */
+    data->lookup_table_update_counter = V4L2PROCESSING_UPDATE_RATE - 2;
+  }
+
+  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 };
--- libv4lconvert/processing/gamma.c
+++ libv4lconvert/processing/gamma.c
+/*
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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 };
--- libv4lconvert/processing/libv4lprocessing-priv.h
+++ libv4lconvert/processing/libv4lprocessing-priv.h
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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;
+  int controls_changed;
+  /* 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 */
+  /* whitebalance.c data */
+  int green_avg;
+  int comp1_avg;
+  int comp2_avg;
+  /* gamma.c data */
+  int last_gamma;
+  unsigned char gamma_table[256];
+  /* autogain.c data */
+  int last_gain_correction;
+};
+
+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
--- libv4lconvert/processing/libv4lprocessing.c
+++ libv4lconvert/processing/libv4lprocessing.c
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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) {
+    fprintf(stderr, "libv4lprocessing: error: out of memory!\n");
+    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;
+  }
+
+  data->controls_changed |= v4lcontrol_controls_changed(data->control);
+
+  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 (data->controls_changed ||
+      data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) {
+    data->controls_changed = 0;
+    data->lookup_table_update_counter = 0;
+    /* Do this after resetting lookup_table_update_counter so that filters can
+       force the next update to be sooner when they changed camera settings */
+    v4lprocessing_update_lookup_tables(data, buf, fmt);
+  } else
+    data->lookup_table_update_counter++;
+
+  if (data->lookup_table_active)
+    v4lprocessing_do_processing(data, buf, fmt);
+
+  data->do_process = 0;
+}
--- libv4lconvert/processing/libv4lprocessing.h
+++ libv4lconvert/processing/libv4lprocessing.h
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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
--- libv4lconvert/processing/whitebalance.c
+++ libv4lconvert/processing/whitebalance.c
+/*
+#             (C) 2008-2009 Elmar Kleijn <elmar_kleijn at hotmail.com>
+#             (C) 2008-2009 Sjoerd Piepenbrink <need4weed at gmail.com>
+#             (C) 2008-2009 Hans de Goede <hdegoede at 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 CLIP256(color) (((color)>0xff)?0xff:(((color)<0)?0:(color)))
+#define CLIP(color, min, max) (((color)>(max))?(max):(((color)<(min))?(min):(color)))
+
+static int whitebalance_active(struct v4lprocessing_data *data) {
+  int wb;
+
+  wb = v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE);
+  if (!wb) {
+    /* Reset cached color averages */
+    data->green_avg = 0;
+  }
+
+  return wb;
+}
+
+static int whitebalance_calculate_lookup_tables_generic(
+  struct v4lprocessing_data *data, int green_avg, int comp1_avg, int comp2_avg)
+{
+  int i, avg_avg;
+  const int max_step = 64;
+
+  /* Clip averages (restricts maximum white balance correction) */
+  green_avg = CLIP(green_avg, 512, 3072);
+  comp1_avg = CLIP(comp1_avg, 512, 3072);
+  comp2_avg = CLIP(comp2_avg, 512, 3072);
+
+  /* First frame ? */
+  if (data->green_avg == 0) {
+    data->green_avg = green_avg;
+    data->comp1_avg = comp1_avg;
+    data->comp2_avg = comp2_avg;
+  } else {
+    /* Slowly adjust the averages used for the correction, so that we
+       do not get a sudden change in colors */
+    if (abs(data->green_avg - green_avg) > max_step) {
+      if (data->green_avg < green_avg)
+	data->green_avg += max_step;
+      else
+	data->green_avg -= max_step;
+    }
+    else
+      data->green_avg = green_avg;
+
+    if (abs(data->comp1_avg - comp1_avg) > max_step) {
+      if (data->comp1_avg < comp1_avg)
+	data->comp1_avg += max_step;
+      else
+	data->comp1_avg -= max_step;
+    }
+    else
+      data->comp1_avg = comp1_avg;
+
+    if (abs(data->comp2_avg - comp2_avg) > max_step) {
+      if (data->comp2_avg < comp2_avg)
+	data->comp2_avg += max_step;
+      else
+	data->comp2_avg -= max_step;
+    }
+    else
+      data->comp2_avg = comp2_avg;
+  }
+
+  if (abs(data->green_avg - data->comp1_avg) < max_step &&
+      abs(data->green_avg - data->comp2_avg) < max_step &&
+      abs(data->comp1_avg - data->comp2_avg) < max_step)
+    return 0;
+
+  avg_avg = (data->green_avg + data->comp1_avg + data->comp2_avg) / 3;
+
+  for (i = 0; i < 256; i++) {
+    data->comp1[i] = CLIP256(data->comp1[i] * avg_avg / data->comp1_avg);
+    data->green[i] = CLIP256(data->green[i] * avg_avg / data->green_avg);
+    data->comp2[i] = CLIP256(data->comp2[i] * avg_avg / data->comp2_avg);
+  }
+
+  return 1;
+}
+
+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;
+
+  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 / 2 + b2 / 2;
+    comp1_avg = a2;
+    comp2_avg = b1;
+  } else {
+    green_avg = a2 / 2 + b1 / 2;
+    comp1_avg = a1;
+    comp2_avg = b2;
+  }
+
+  /* Norm avg to ~ 0 - 4095 */
+  green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64;
+  comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64;
+  comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64;
+
+  return whitebalance_calculate_lookup_tables_generic(data, green_avg,
+						      comp1_avg, comp2_avg);
+}
+
+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;
+
+  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;
+  }
+
+  /* Norm avg to ~ 0 - 4095 */
+  green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16;
+  comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16;
+  comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16;
+
+  return whitebalance_calculate_lookup_tables_generic(data, green_avg,
+						      comp1_avg, comp2_avg);
+}
+
+
+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 };
--- libv4lconvert/rgbyuv.c
+++ libv4lconvert/rgbyuv.c
@@ -1,8 +1,10 @@
 /*
 
 # RGB <-> YUV conversion routines
+#             (C) 2008 Hans de Goede <hdegoede at redhat.com>
 
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+# RGB565 conversion routines
+#             (C) 2009 Mauro Carvalho Chehab <mchehab at 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
@@ -135,7 +137,7 @@
       vsrc++;
     }
     /* Rewind u and v for next line */
-    if (i&1) {
+    if (!(i&1)) {
       usrc -= width / 2;
       vsrc -= width / 2;
     }
@@ -189,7 +191,7 @@
       vsrc++;
     }
     /* Rewind u and v for next line */
-    if (i&1) {
+    if (!(i&1)) {
       usrc -= width / 2;
       vsrc -= width / 2;
     }
@@ -409,7 +411,6 @@
   }
 
   /* copy the U and V values */
-  src++;				/* point to V */
   src1 = src + width * 2;		/* next line */
   if (yvu) {
     vdest = dest;
@@ -473,3 +474,103 @@
     src += src_fmt->fmt.pix.bytesperline / 2;
   }
 }
+
+void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest,
+  int width, int height)
+{
+  int j;
+  while (--height >= 0) {
+    for (j = 0; j < width; j++) {
+      unsigned short tmp = *(unsigned short *)src;
+
+      /* Original format: rrrrrggg gggbbbbb */
+      *dest++ = 0xf8 & (tmp >> 8);
+      *dest++ = 0xfc & (tmp >> 3);
+      *dest++ = 0xf8 & (tmp << 3);
+
+      src += 2;
+    }
+  }
+}
+
+void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest,
+  int width, int height)
+{
+  int j;
+  while (--height >= 0) {
+    for (j = 0; j < width; j++) {
+      unsigned short tmp = *(unsigned short *)src;
+
+      /* Original format: rrrrrggg gggbbbbb */
+      *dest++ = 0xf8 & (tmp << 3);
+      *dest++ = 0xfc & (tmp >> 3);
+      *dest++ = 0xf8 & (tmp >> 8);
+
+      src += 2;
+    }
+  }
+}
+
+void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest,
+  const struct v4l2_format *src_fmt, int yvu)
+{
+  int x, y;
+  unsigned short tmp;
+  unsigned char *udest, *vdest;
+  unsigned r[4], g[4], b[4];
+  int avg_src[3];
+
+  /* Y */
+  for (y = 0; y < src_fmt->fmt.pix.height; y++) {
+    for (x = 0; x < src_fmt->fmt.pix.width; x++) {
+      tmp = *(unsigned short *)src;
+      r[0] = 0xf8 & (tmp << 3);
+      g[0] = 0xfc & (tmp >> 3);
+      b[0] = 0xf8 & (tmp >> 8);
+      RGB2Y(r[0], g[0], b[0], *dest++);
+      src += 2;
+    }
+    src += src_fmt->fmt.pix.bytesperline - 2 * 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++) {
+      tmp = *(unsigned short *)src;
+      r[0] = 0xf8 & (tmp << 3);
+      g[0] = 0xfc & (tmp >> 3);
+      b[0] = 0xf8 & (tmp >> 8);
+
+      tmp = *(((unsigned short *)src) + 1);
+      r[1] = 0xf8 & (tmp << 3);
+      g[1] = 0xfc & (tmp >> 3);
+      b[1] = 0xf8 & (tmp >> 8);
+
+      tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline);
+      r[2] = 0xf8 & (tmp << 3);
+      g[2] = 0xfc & (tmp >> 3);
+      b[2] = 0xf8 & (tmp >> 8);
+
+      tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline + 1);
+      r[3] = 0xf8 & (tmp << 3);
+      g[3] = 0xfc & (tmp >> 3);
+      b[3] = 0xf8 & (tmp >> 8);
+
+      avg_src[0] = (r[0] + r[1] + r[2] + r[3]) /4;
+      avg_src[1] = (g[0] + g[1] + g[2] + g[3]) /4;
+      avg_src[2] = (b[0] + b[1] + b[2] + b[3]) /4;
+      RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++);
+      src += 4;
+    }
+    src += 2 * src_fmt->fmt.pix.bytesperline - 2 * src_fmt->fmt.pix.width;
+  }
+}
--- libv4lconvert/sn9c2028-decomp.c
+++ libv4lconvert/sn9c2028-decomp.c
+/*
+ * sn9c2028-decomp.c
+ *
+ * Decompression function for the Sonix SN9C2028 dual-mode cameras.
+ *
+ * Code adapted from libgphoto2/camlibs/sonix, original version of which was
+ * Copyright (c) 2005 Theodore Kilgore <kilgota at auburn.edu>
+ *
+ * History:
+ *
+ * This decoding algorithm originates from the work of Bertrik Sikken for the
+ * SN9C102 cameras. This version is an adaptation of work done by Mattias
+ * Krauss for the webcam-osx (macam) project. There, it was further adapted
+ * for use with the Vivitar Vivicam 3350B (an SN9C2028 camera) by
+ * Harald Ruda <hrx at users.sourceforge.net>. Harald brought to my attention the
+ * work done in the macam project and suggested that I use it. One improvement
+ * of my own was to notice that the even and odd columns of the image have been
+ * reversed by the decompression algorithm, and this needs to be corrected
+ * during the decompression.
+ *
+ *
+ * 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"
+
+/* Four defines for bitstream operations, used in the decode function */
+
+#define PEEK_BITS(num,to) {\
+    if (bitBufCount < num) {\
+	do {\
+	    bitBuf = (bitBuf << 8)|(*(src++));\
+	    bitBufCount += 8; \
+	} \
+	while\
+	    (bitBufCount < 24);\
+    } \
+    to = bitBuf >> (bitBufCount-num);\
+}
+
+/*
+ * PEEK_BITS puts the next <num> bits into the low bits of <to>.
+ * when the buffer is empty, it is completely refilled.
+ * This strategy tries to reduce memory access. Note that the high bits
+ * are NOT set to zero!
+ */
+
+#define EAT_BITS(num) { bitBufCount -= num; bits_eaten += num; }
+
+/*
+ * EAT_BITS consumes <num> bits (PEEK_BITS does not consume anything,
+ * it just peeks)
+ */
+
+#define PARSE_PIXEL(val) {\
+    PEEK_BITS(10, bits);\
+    if ((bits&0x200) == 0) {\
+	EAT_BITS(1);\
+    } \
+    else if ((bits&0x380) == 0x280) {\
+	EAT_BITS(3);\
+	val += 3;\
+	if (val > 255)\
+	    val = 255;\
+    } \
+    else if ((bits&0x380) == 0x300) {\
+	EAT_BITS(3);\
+	val -= 3;\
+	if (val < 0)\
+	    val = 0;\
+    } \
+    else if ((bits&0x3c0) == 0x200) {\
+	EAT_BITS(4);\
+	val += 8;\
+	if (val > 255)\
+	    val = 255;\
+    } \
+    else if ((bits&0x3c0) == 0x240) {\
+	EAT_BITS(4);\
+	val -= 8;\
+	if (val < 0)\
+	    val = 0;\
+    } \
+    else if ((bits&0x3c0) == 0x3c0) {\
+	EAT_BITS(4);\
+	val -= 20;\
+	if (val < 0)\
+	    val = 0;\
+    } \
+    else if ((bits&0x3e0) == 0x380) {\
+	EAT_BITS(5);\
+	val += 20;\
+	if (val > 255)\
+	    val = 255;\
+    } \
+    else {\
+	EAT_BITS(10);\
+	val = 8*(bits&0x1f)+0;\
+    } \
+}
+
+
+#define PUT_PIXEL_PAIR {\
+    long pp;\
+    pp = (c1val<<8)+c2val;\
+    *((unsigned short *) (dst+dst_index)) = pp;\
+    dst_index += 2;\
+}
+
+/* Now the decode function itself */
+
+void v4lconvert_decode_sn9c2028(const unsigned char *src, unsigned char *dst,
+  int width, int height)
+{
+    long dst_index = 0;
+    int starting_row = 0;
+    unsigned short bits;
+    short c1val, c2val;
+    int x, y;
+    unsigned long bitBuf = 0;
+    unsigned long bitBufCount = 0;
+    unsigned long bits_eaten = 0;
+
+    src += 12;    /* Remove the header */
+
+    for (y = starting_row; y < height; y++) {
+	PEEK_BITS(8, bits);
+	EAT_BITS(8);
+	c2val = (bits & 0xff);
+	PEEK_BITS(8, bits);
+	EAT_BITS(8);
+	c1val = (bits & 0xff);
+
+	PUT_PIXEL_PAIR;
+
+	for (x = 2; x < width ; x += 2) {
+	    /* The compression reversed the even and odd columns.*/
+	    PARSE_PIXEL(c2val);
+	    PARSE_PIXEL(c1val);
+	    PUT_PIXEL_PAIR;
+	}
+    }
+}
--- libv4lconvert/spca501.c
+++ libv4lconvert/spca501.c
@@ -1,5 +1,5 @@
 /*
-#             (C) 2008 Hans de Goede <j.w.r.degoede at hhs.nl>
+#             (C) 2008 Hans de Goede <hdegoede at 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
--- libv4lconvert/stv0680.c
+++ libv4lconvert/stv0680.c
+/*
+ * stv0680.c
+ *
+ * Copyright (c) 2009 Hans de Goede <hdegoede at 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 "libv4lconvert-priv.h"
+
+/* The stv0640 first sends all the red/green pixels for a line (so 1/2 width)
+   and then all the green/blue pixels in that line, shuffle this to a regular
+   RGGB bayer pattern. */
+void v4lconvert_decode_stv0680(const unsigned char *src, unsigned char *dst,
+  int width, int height)
+{
+  int x, y;
+  const unsigned char *src1 = src;
+  const unsigned char *src2 = src + width / 2;
+
+  for (y = 0; y < height; y++) {
+    for (x = 0; x < width / 2; x++) {
+      *dst++ = *src1++;
+      *dst++ = *src2++;
+    }
+    src1 += width / 2;
+    src2 += width / 2;
+  }
+}
--- libv4lconvert/tinyjpeg-internal.h
+++ libv4lconvert/tinyjpeg-internal.h
@@ -103,6 +103,9 @@
   int restart_interval;
   int restarts_to_go;				/* MCUs left in this restart interval */
   int last_rst_marker_seen;			/* Rst marker is incremented each time */
+#if SANITY_CHECK
+  unsigned int current_cid;			/* For planar JPEG */
+#endif
 
   /* Temp space used after the IDCT to store each components */
   uint8_t Y[64*4], Cr[64], Cb[64];
@@ -112,6 +115,10 @@
   uint8_t *plane[COMPONENTS];
 
   char error_string[256];
+
+  /* Temp buffers for multipass planar JPG -> RGB decoding */
+  int tmp_buf_y_size;
+  uint8_t *tmp_buf[COMPONENTS];
 };
 
 #define IDCT tinyjpeg_idct_float
--- libv4lconvert/tinyjpeg.c
+++ libv4lconvert/tinyjpeg.c
@@ -1910,14 +1910,37 @@
   trace("> SOS marker\n");
 
 #if SANITY_CHECK
-  if (nr_components != 3)
+  if (nr_components != 3 && nr_components != 1)
     error("We only support YCbCr image\n");
 #endif
 
+  if (nr_components == 1)
+    priv->flags |= TINYJPEG_FLAGS_PLANAR_JPEG;
+#if SANITY_CHECK
+  else if (priv->flags & TINYJPEG_FLAGS_PLANAR_JPEG)
+    error("SOS with more then 1 component while decoding planar JPEG\n");
+#endif
+
   stream += 3;
   for (i=0;i<nr_components;i++) {
      cid = *stream++;
      table = *stream++;
+     if (nr_components == 1) {
+#if SANITY_CHECK
+       /* Find matching cid so we store the tables in the right component */
+       for (i = 0; i < COMPONENTS; i++)
+	 if (priv->component_infos[i].cid == cid)
+	   break;
+
+       if (i == COMPONENTS)
+	 error("Unknown cid in SOS: %u\n", cid);
+
+       priv->current_cid = cid;
+#else
+       i = cid - 1;
+#endif
+       trace("SOS cid: %u, using component_info: %u\n", cid, i);
+     }
 #if SANITY_CHECK
      if ((table&0xf) >= HUFFMAN_TABLES)
 	error("We do not support more than %d AC Huffman table\n",
@@ -2048,7 +2071,11 @@
       }
      /* Skip any padding ff byte (this is normal) */
      while (*stream == 0xff)
+      {
        stream++;
+       if (stream >= priv->stream_end)
+	 error("EOF while search for a RST marker.\n");
+      }
 
      marker = *stream++;
      if ((RST+priv->last_rst_marker_seen) == marker)
@@ -2066,6 +2093,32 @@
   return 0;
 }
 
+static int find_next_sos_marker(struct jdec_private *priv)
+{
+  const unsigned char *stream = priv->stream;
+
+  /* Parse marker */
+  while (1) {
+    while (*stream++ != 0xff) {
+      if (stream >= priv->stream_end)
+	error("EOF while search for a SOS marker.\n");
+    }
+    /* Skip any padding ff byte (this is normal) */
+    while (*stream == 0xff) {
+      stream++;
+      if (stream >= priv->stream_end)
+	error("EOF while search for a SOS marker.\n");
+    }
+
+    if (*stream++ == SOS)
+      break; /* Found it ! */
+   }
+
+  priv->stream = stream;
+
+  return 0;
+}
+
 static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
 {
   int chuck_len;
@@ -2154,6 +2207,10 @@
       || (priv->component_infos[cCb].Vfactor!=1)
       || (priv->component_infos[cCr].Vfactor!=1))
     error("Sampling other than 1x1 for Cr and Cb is not supported\n");
+  if ((priv->flags & TINYJPEG_FLAGS_PLANAR_JPEG) &&
+      (   (priv->component_infos[cY].Hfactor!=2)
+       || (priv->component_infos[cY].Hfactor!=2)))
+    error("Sampling other than 2x2 for Y is not supported with planar JPEG\n");
 #endif
 
   return 0;
@@ -2197,10 +2254,12 @@
 {
   int i;
   for (i=0; i<COMPONENTS; i++) {
-     if (priv->components[i])
-       free(priv->components[i]);
+     free(priv->components[i]);
+     free(priv->tmp_buf[i]);
      priv->components[i] = NULL;
+     priv->tmp_buf[i] = NULL;
   }
+  priv->tmp_buf_y_size = 0;
   free(priv);
 }
 
@@ -2276,6 +2335,8 @@
    YCrCB_to_Grey_2x2,
 };
 
+int tinyjpeg_decode_planar(struct jdec_private *priv, int pixfmt);
+
 /**
  * Decode and convert the jpeg image into @pixfmt@ image
  *
@@ -2293,6 +2354,9 @@
   if (setjmp(priv->jump_state))
     return -1;
 
+  if (priv->flags & TINYJPEG_FLAGS_PLANAR_JPEG)
+    return tinyjpeg_decode_planar(priv, pixfmt);
+
   /* To keep gcc happy initialize some array */
   bytes_per_mcu[1] = 0;
   bytes_per_mcu[2] = 0;
@@ -2424,6 +2488,238 @@
 
   return 0;
 }
+
+int tinyjpeg_decode_planar(struct jdec_private *priv, int pixfmt)
+{
+  unsigned int i, x, y;
+  uint8_t *y_buf, *u_buf, *v_buf, *p, *p2;
+
+  switch (pixfmt) {
+  case TINYJPEG_FMT_GREY:
+    error("Greyscale output not supported with planar JPEG input\n");
+    break;
+
+  case TINYJPEG_FMT_RGB24:
+  case TINYJPEG_FMT_BGR24:
+    if (priv->tmp_buf_y_size < (priv->width * priv->height)) {
+      for (i=0; i<COMPONENTS; i++) {
+	 free(priv->tmp_buf[i]);
+	 priv->tmp_buf[i] = malloc(priv->width * priv->height / (i ? 4:1));
+	 if (!priv->tmp_buf[i])
+	   error("Could not allocate memory for temporary buffers\n");
+      }
+      priv->tmp_buf_y_size = priv->width * priv->height;
+    }
+    y_buf = priv->tmp_buf[cY];
+    u_buf = priv->tmp_buf[cCb];
+    v_buf = priv->tmp_buf[cCr];
+    break;
+
+  case TINYJPEG_FMT_YUV420P:
+    y_buf = priv->components[cY];
+    u_buf = priv->components[cCb];
+    v_buf = priv->components[cCr];
+    break;
+
+  default:
+    error("Bad pixel format\n");
+  }
+
+#if SANITY_CHECK
+  if (priv->current_cid != priv->component_infos[cY].cid)
+    error("Planar jpeg first SOS cid does not match Y cid (%u:%u)\n",
+	  priv->current_cid, priv->component_infos[cY].cid);
+#endif
+
+  resync(priv);
+
+  for (y=0; y < priv->height/8; y++) {
+    for (x=0; x < priv->width/8; x++) {
+      process_Huffman_data_unit(priv, cY);
+      IDCT(&priv->component_infos[cY], y_buf, priv->width);
+      y_buf += 8;
+    }
+    y_buf += 7 * priv->width;
+  }
+
+  priv->stream -= (priv->nbits_in_reservoir/8);
+  resync(priv);
+  if (find_next_sos_marker(priv) < 0)
+    return -1;
+  if (parse_SOS(priv, priv->stream) < 0)
+    return -1;
+
+#if SANITY_CHECK
+  if (priv->current_cid != priv->component_infos[cCb].cid)
+    error("Planar jpeg second SOS cid does not match Cn cid (%u:%u)\n",
+	  priv->current_cid, priv->component_infos[cCb].cid);
+#endif
+
+  for (y=0; y < priv->height/16; y++) {
+    for (x=0; x < priv->width/16; x++) {
+      process_Huffman_data_unit(priv, cCb);
+      IDCT(&priv->component_infos[cCb], u_buf, priv->width / 2);
+      u_buf += 8;
+    }
+    u_buf += 7 * (priv->width / 2);
+  }
+
+  priv->stream -= (priv->nbits_in_reservoir/8);
+  resync(priv);
+  if (find_next_sos_marker(priv) < 0)
+    return -1;
+  if (parse_SOS(priv, priv->stream) < 0)
+    return -1;
+
+#if SANITY_CHECK
+  if (priv->current_cid != priv->component_infos[cCr].cid)
+    error("Planar jpeg third SOS cid does not match Cr cid (%u:%u)\n",
+	  priv->current_cid, priv->component_infos[cCr].cid);
+#endif
+
+  for (y=0; y < priv->height/16; y++) {
+    for (x=0; x < priv->width/16; x++) {
+      process_Huffman_data_unit(priv, cCr);
+      IDCT(&priv->component_infos[cCr], v_buf, priv->width / 2);
+      v_buf += 8;
+    }
+    v_buf += 7 * (priv->width / 2);
+  }
+
+#define SCALEBITS       10
+#define ONE_HALF        (1UL << (SCALEBITS-1))
+#define FIX(x)          ((int)((x) * (1UL<<SCALEBITS) + 0.5))
+
+  switch (pixfmt) {
+  case TINYJPEG_FMT_RGB24:
+    y_buf = priv->tmp_buf[cY];
+    u_buf = priv->tmp_buf[cCb];
+    v_buf = priv->tmp_buf[cCr];
+    p = priv->components[0];
+    p2 = priv->components[0] + priv->width * 3;
+
+    for (y = 0; y < priv->height / 2; y++) {
+      for (x = 0; x < priv->width / 2; x++) {
+	int l, cb, cr;
+	int add_r, add_g, add_b;
+	int r, g , b;
+
+	cb = *u_buf++ - 128;
+	cr = *v_buf++ - 128;
+	add_r = FIX(1.40200) * cr + ONE_HALF;
+	add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF;
+	add_b = FIX(1.77200) * cb + ONE_HALF;
+
+	l  = (*y_buf) << SCALEBITS;
+	r = (l + add_r) >> SCALEBITS;
+	*p++ = clamp(r);
+	g = (l + add_g) >> SCALEBITS;
+	*p++ = clamp(g);
+	b = (l + add_b) >> SCALEBITS;
+	*p++ = clamp(b);
+
+	l  = (y_buf[priv->width]) << SCALEBITS;
+	r = (l + add_r) >> SCALEBITS;
+	*p2++ = clamp(r);
+	g = (l + add_g) >> SCALEBITS;
+	*p2++ = clamp(g);
+	b = (l + add_b) >> SCALEBITS;
+	*p2++ = clamp(b);
+
+	y_buf++;
+
+	l  = (*y_buf) << SCALEBITS;
+	r = (l + add_r) >> SCALEBITS;
+	*p++ = clamp(r);
+	g = (l + add_g) >> SCALEBITS;
+	*p++ = clamp(g);
+	b = (l + add_b) >> SCALEBITS;
+	*p++ = clamp(b);
+
+	l  = (y_buf[priv->width]) << SCALEBITS;
+	r = (l + add_r) >> SCALEBITS;
+	*p2++ = clamp(r);
+	g = (l + add_g) >> SCALEBITS;
+	*p2++ = clamp(g);
+	b = (l + add_b) >> SCALEBITS;
+	*p2++ = clamp(b);
+
+	y_buf++;
+      }
+      y_buf += priv->width;
+      p  += priv->width * 3;
+      p2 += priv->width * 3;
+    }
+    break;
+
+  case TINYJPEG_FMT_BGR24:
+    y_buf = priv->tmp_buf[cY];
+    u_buf = priv->tmp_buf[cCb];
+    v_buf = priv->tmp_buf[cCr];
+    p = priv->components[0];
+    p2 = priv->components[0] + priv->width * 3;
+
+    for (y = 0; y < priv->height / 2; y++) {
+      for (x = 0; x < priv->width / 2; x++) {
+	int l, cb, cr;
+	int add_r, add_g, add_b;
+	int r, g , b;
+
+	cb = *u_buf++ - 128;
+	cr = *v_buf++ - 128;
+	add_r = FIX(1.40200) * cr + ONE_HALF;
+	add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF;
+	add_b = FIX(1.77200) * cb + ONE_HALF;
+
+	l  = (*y_buf) << SCALEBITS;
+	b = (l + add_b) >> SCALEBITS;
+	*p++ = clamp(b);
+	g = (l + add_g) >> SCALEBITS;
+	*p++ = clamp(g);
+	r = (l + add_r) >> SCALEBITS;
+	*p++ = clamp(r);
+
+	l  = (y_buf[priv->width]) << SCALEBITS;
+	b = (l + add_b) >> SCALEBITS;
+	*p2++ = clamp(b);
+	g = (l + add_g) >> SCALEBITS;
+	*p2++ = clamp(g);
+	r = (l + add_r) >> SCALEBITS;
+	*p2++ = clamp(r);
+
+	y_buf++;
+
+	l  = (*y_buf) << SCALEBITS;
+	b = (l + add_b) >> SCALEBITS;
+	*p++ = clamp(b);
+	g = (l + add_g) >> SCALEBITS;
+	*p++ = clamp(g);
+	r = (l + add_r) >> SCALEBITS;
+	*p++ = clamp(r);
+
+	l  = (y_buf[priv->width]) << SCALEBITS;
+	b = (l + add_b) >> SCALEBITS;
+	*p2++ = clamp(b);
+	g = (l + add_g) >> SCALEBITS;
+	*p2++ = clamp(g);
+	r = (l + add_r) >> SCALEBITS;
+	*p2++ = clamp(r);
+
+	y_buf++;
+      }
+      y_buf += priv->width;
+      p  += priv->width * 3;
+      p2 += priv->width * 3;
+    }
+    break;
+  }
+
+#undef SCALEBITS
+#undef ONE_HALF
+#undef FIX
+
+  return 0;
+}
 
 const char *tinyjpeg_get_errorstring(struct jdec_private *priv)
 {
--- libv4lconvert/tinyjpeg.h
+++ libv4lconvert/tinyjpeg.h
@@ -44,6 +44,7 @@
 /* Flags that can be set by any applications */
 #define TINYJPEG_FLAGS_MJPEG_TABLE	(1<<1)
 #define TINYJPEG_FLAGS_PIXART_JPEG	(1<<2)
+#define TINYJPEG_FLAGS_PLANAR_JPEG	(1<<3)
 
 /* Format accepted in outout */
 enum tinyjpeg_fmt {



More information about the MeeGo-commits mailing list