--- /dev/null
+/*
+# (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;
+}
#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
/* For mr97310a decoder */
int frames_dropped;
+
+ /* For cpia1 decoder */
+ unsigned char* previous_frame;
};
struct v4lconvert_pixfmt {
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);
{ 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 },
free(data->rotate90_buf);
free(data->flip_buf);
free(data->convert_pixfmt_buf);
+ free(data->previous_frame);
free(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:
{
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)) {