libv4l: Add support for decoding CPIA1 compressed YUV
authorHans de Goede <hdegoede@redhat.com>
Mon, 4 Jan 2010 08:55:15 +0000 (09:55 +0100)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 24 Feb 2010 00:44:40 +0000 (21:44 -0300)
Priority: normal

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
lib/libv4lconvert/Makefile
lib/libv4lconvert/cpia1.c [new file with mode: 0644]
lib/libv4lconvert/libv4lconvert-priv.h
lib/libv4lconvert/libv4lconvert.c

index 839cbd5..e7e2146 100644 (file)
@@ -15,7 +15,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 \
+               stv0680.o cpia1.o \
                control/libv4lcontrol.o processing/libv4lprocessing.o \
                processing/whitebalance.o processing/autogain.o \
                processing/gamma.o helper.o
diff --git a/lib/libv4lconvert/cpia1.c b/lib/libv4lconvert/cpia1.c
new file mode 100644 (file)
index 0000000..dd6466e
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+#             (C) 2010 Hans de Goede <hdegoede@redhat.com>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "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[0] != EOI || src[1] != EOI || src[2] != EOI || src[3] != 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;
+}
index 9a4fae9..b2817ff 100644 (file)
 #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
@@ -152,6 +156,9 @@ struct v4lconvert_data {
 
   /* For mr97310a decoder */
   int frames_dropped;
+
+  /* For cpia1 decoder */
+  unsigned char* previous_frame;
 };
 
 struct v4lconvert_pixfmt {
@@ -218,6 +225,10 @@ void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst,
 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);
 
index e715a13..355bdd6 100644 (file)
@@ -56,6 +56,7 @@ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = {
   { 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 },
@@ -172,6 +173,7 @@ void v4lconvert_destroy(struct v4lconvert_data *data)
   free(data->rotate90_buf);
   free(data->flip_buf);
   free(data->convert_pixfmt_buf);
+  free(data->previous_frame);
   free(data);
 }
 
@@ -642,6 +644,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
     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:
     {
@@ -677,6 +680,14 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
        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)) {