Add support for SE401 pixelformat
authorHans de Goede <hdegoede@redhat.com>
Tue, 7 Jun 2011 14:38:31 +0000 (16:38 +0200)
committerHans de Goede <hdegoede@redhat.com>
Tue, 14 Jun 2011 21:27:01 +0000 (23:27 +0200)
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
ChangeLog
Make.rules
include/linux/videodev2.h
lib/libv4lconvert/Makefile
lib/libv4lconvert/control/libv4lcontrol.c
lib/libv4lconvert/libv4lconvert-priv.h
lib/libv4lconvert/libv4lconvert.c
lib/libv4lconvert/se401.c [new file with mode: 0644]

index dc60d30..af66c24 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+v4l-utils-0.8.5
+---------------
+* libv4l changes
+  * Add some more laptop models to the upside down devices table (hdegoede)
+  * Add support for SE401 pixelformat (hdegoede)
+
 v4l-utils-0.8.4
 ---------------
 * Utils changes
index 9a96a8d..463a34f 100644 (file)
@@ -1,4 +1,4 @@
-V4L_UTILS_VERSION=0.8.4
+V4L_UTILS_VERSION=0.8.5-test
 
 # These ones can be overriden from the cmdline
 
index 8a4c309..4c80dcb 100644 (file)
@@ -402,6 +402,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */
 #define V4L2_PIX_FMT_KONICA420  v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */
 #define V4L2_PIX_FMT_JPGL      v4l2_fourcc('J', 'P', 'G', 'L') /* JPEG-Lite */
+#define V4L2_PIX_FMT_SE401    v4l2_fourcc('S', '4', '0', '1') /* se401 janggu compressed rgb */
 
 /*
  *     F O R M A T   E N U M E R A T I O N
index a9791f7..1376c21 100644 (file)
@@ -12,7 +12,7 @@ endif
 CONVERT_OBJS  = libv4lconvert.o tinyjpeg.o sn9c10x.o sn9c20x.o pac207.o \
                mr97310a.o flip.o crop.o jidctflt.o spca561-decompress.o \
                rgbyuv.o sn9c2028-decomp.o spca501.o sq905c.o bayer.o hm12.o \
-               stv0680.o cpia1.o jpgl.o jpeg.o jpeg_memsrcdest.o \
+               stv0680.o cpia1.o se401.o jpgl.o jpeg.o jpeg_memsrcdest.o \
                control/libv4lcontrol.o processing/libv4lprocessing.o \
                processing/whitebalance.o processing/autogain.o \
                processing/gamma.o helper.o
index a051482..2fddf43 100644 (file)
@@ -613,6 +613,17 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = {
        /* vicam based cams */
        { 0x04c1, 0x009d, 0,    NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN, 1500 },
        { 0x0602, 0x1001, 0,    NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN, 1500 },
+       /* se401 cams, mirrored and need wb + autogain */
+       { 0x03e8, 0x0004, 0,    NULL, NULL,
+               V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
+       { 0x0471, 0x030b, 0,    NULL, NULL,
+               V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
+       { 0x047d, 0x5001, 0,    NULL, NULL,
+               V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
+       { 0x047d, 0x5002, 0,    NULL, NULL,
+               V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
+       { 0x047d, 0x5003, 0,    NULL, NULL,
+               V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
 };
 
 static const struct v4l2_queryctrl fake_controls[];
index a4aa316..2cd7a22 100644 (file)
@@ -187,6 +187,10 @@ int v4lconvert_cpia1_to_yuv420(struct v4lconvert_data *data,
 void v4lconvert_sn9c20x_to_yuv420(const unsigned char *src, unsigned char *dst,
                int width, int height, int yvu);
 
+int v4lconvert_se401_to_rgb24(struct v4lconvert_data *data,
+               const unsigned char *src, int src_size,
+               unsigned char *dest, int width, int height);
+
 int v4lconvert_decode_jpeg_tinyjpeg(struct v4lconvert_data *data,
        unsigned char *src, int src_size, unsigned char *dest,
        struct v4l2_format *fmt, unsigned int dest_pix_fmt, int flags);
index 3ea270b..6935e65 100644 (file)
@@ -79,6 +79,8 @@ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = {
        { V4L2_PIX_FMT_PAC207,           0,      9,      9,     1 },
        { V4L2_PIX_FMT_MR97310A,         0,      9,      9,     1 },
        { V4L2_PIX_FMT_SQ905C,           0,      9,      9,     1 },
+       /* special */
+       { V4L2_PIX_FMT_SE401,            0,      8,      9,     1 },
        /* grey formats */
        { V4L2_PIX_FMT_GREY,             8,     20,     20,     0 },
        { V4L2_PIX_FMT_Y10BPACK,        10,     20,     20,     0 },
@@ -860,6 +862,43 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
                }
                break;
 
+       case V4L2_PIX_FMT_SE401: {
+               unsigned char *d = NULL;
+
+               switch (dest_pix_fmt) {
+               case V4L2_PIX_FMT_RGB24:
+                       d = dest;
+                       break;
+               case V4L2_PIX_FMT_BGR24:
+               case V4L2_PIX_FMT_YUV420:
+               case V4L2_PIX_FMT_YVU420:
+                       d = v4lconvert_alloc_buffer(width * height * 3,
+                                       &data->convert_pixfmt_buf,
+                                       &data->convert_pixfmt_buf_size);
+                       if (!d)
+                               return v4lconvert_oom_error(data);
+
+                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
+                       v4lconvert_fixup_fmt(fmt);
+                       break;
+               }
+
+               result = v4lconvert_se401_to_rgb24(data, src, src_size, d,
+                                                  width, height);
+               switch (dest_pix_fmt) {
+               case V4L2_PIX_FMT_BGR24:
+                       v4lconvert_swap_rgb(d, dest, width, height);
+                       break;
+               case V4L2_PIX_FMT_YUV420:
+                       v4lconvert_rgb24_to_yuv420(d, dest, fmt, 0, 0);
+                       break;
+               case V4L2_PIX_FMT_YVU420:
+                       v4lconvert_rgb24_to_yuv420(d, dest, fmt, 0, 1);
+                       break;
+               }
+               break;
+       }
+
        case V4L2_PIX_FMT_GREY:
                switch (dest_pix_fmt) {
                case V4L2_PIX_FMT_RGB24:
diff --git a/lib/libv4lconvert/se401.c b/lib/libv4lconvert/se401.c
new file mode 100644 (file)
index 0000000..ccfcda0
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+#             (C) 2011 Hans de Goede <hdegoede@redhat.com>
+
+# The compression algorithm has been taken from the v4l1 se401 linux kernel
+# driver by Jeroen B. Vreeken (pe1rxq@amsat.org)
+
+# 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., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
+ */
+
+#include "libv4lconvert-priv.h"
+#include <errno.h>
+
+/* The se401 compression algorithm uses a fixed quant factor, which
+   can be configured by setting the high nibble of the SE401_OPERATINGMODE
+   feature. This needs to exactly match what is in the SE401 driver! */
+#define SE401_QUANT_FACT 8
+
+static void wr_pixel(int p, uint8_t **dest, int pitch, int *x)
+{
+       int i = *x;
+
+       /* First 3 pixels of each line are absolute */
+       if (i < 3) {
+               (*dest)[i] = p * SE401_QUANT_FACT;
+       } else {
+               (*dest)[i] = (*dest)[i - 3] + p * SE401_QUANT_FACT;
+       }
+
+       *x += 1;
+       if (*x == pitch) {
+               *x = 0;
+               *dest += pitch;
+       }
+}
+
+enum decode_state {
+        get_len,
+        sign_bit,
+        other_bits,
+};
+
+static int decode_JangGu(const uint8_t *data, int bits, int plen, int pixels,
+                        uint8_t **dest, int pitch, int *x)
+{
+       enum decode_state state = get_len;
+       int len = 0;
+       int value = 0;
+       int bitnr;
+       int bit;
+
+       while (plen) {
+               bitnr = 8;
+               while (bitnr && bits) {
+                       bit = ((*data) >> (bitnr-1))&1;
+                       switch (state) {
+                       case get_len:
+                               if (bit) {
+                                       len++;
+                               } else {
+                                       if (!len) {
+                                               wr_pixel(0, dest, pitch, x);
+                                               if (!--pixels)
+                                                       return 0;
+                                       } else {
+                                               state = sign_bit;
+                                               value = 0;
+                                       }
+                               }
+                               break;
+                        case sign_bit:
+                               if (!bit)
+                                       value = -(1 << len) + 1;
+                                /* fall through for positive number and
+                                   len == 1 handling */
+                        case other_bits:
+                               len--;
+                               value += bit << len;
+                               if (len == 0) {
+                                       /* Done write pixel and get bit len of
+                                          the next one */
+                                       state = get_len;
+                                       wr_pixel(value, dest, pitch, x);
+                                       if (!--pixels)
+                                               return 0;
+                               }
+                               break;
+                       }
+                       bitnr--;
+                       bits--;
+               }
+               data++;
+               plen--;
+       }
+       return -1;
+}
+
+int v4lconvert_se401_to_rgb24(struct v4lconvert_data *data,
+               const unsigned char *src, int src_size,
+               unsigned char *dest, int width, int height)
+{
+       int in, plen, bits, pixels, info;
+       int x = 0, total_pixels = 0;
+
+       for (in = 0; in + 4 < src_size; in += plen) {
+               bits   = src[in + 3] + (src[in + 2] << 8);
+               pixels = src[in + 1] + ((src[in + 0] & 0x3f) << 8);
+               info   = (src[in + 0] & 0xc0) >> 6;
+               plen   = ((bits + 47) >> 4) << 1;
+               /* Sanity checks */
+               if (plen > 1024) {
+                       V4LCONVERT_ERR("invalid se401 packet len %d", plen);
+                       goto error;
+               }
+               if (in + plen > src_size) {
+                       V4LCONVERT_ERR("incomplete se401 packet");
+                       goto error;
+               }
+               if (total_pixels + pixels > width * height) {
+                       V4LCONVERT_ERR("se401 frame overflow");
+                       goto error;
+               }
+               /* info: 0 inter packet, 1 eof, 2 sof, 3 not used */
+               if ((in == 0 && info != 2) ||
+                   (in > 0 && in + plen < src_size && info != 0) ||
+                   (in + plen == src_size && info != 1)) {
+                       V4LCONVERT_ERR("invalid se401 frame info value");
+                       goto error;
+               }
+               if (decode_JangGu(&src[in + 4], bits, plen, pixels * 3,
+                                 &dest, width * 3, &x)) {
+                       V4LCONVERT_ERR("short se401 packet");
+                       goto error;
+               }
+               total_pixels += pixels;
+       }
+
+       if (in != src_size || total_pixels != width * height) {
+               V4LCONVERT_ERR("se401 frame size mismatch");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       errno = EIO;
+       return -1;
+}