/* # (C) 2010 Hans de Goede # 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 #include #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 */ /* 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; }