Logo Search packages:      
Sourcecode: v4l-utils version File versions  Download package

cpia1.c

/*
#             (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[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;
}

Generated by  Doxygen 1.6.0   Back to index