|
@@ -0,0 +1,456 @@
|
|
|
+/*
|
|
|
+ * Cinepak Video Decoder
|
|
|
+ * Copyright (C) 2003 the ffmpeg project
|
|
|
+ *
|
|
|
+ * This library 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 of the License, or (at your option) any later version.
|
|
|
+ *
|
|
|
+ * This library 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 library; if not, write to the Free Software
|
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * @file cinepak.c
|
|
|
+ * Cinepak video decoder
|
|
|
+ * by Ewald Snel <ewald@rambo.its.tudelft.nl>
|
|
|
+ * For more information on the Cinepak algorithm, visit:
|
|
|
+ * http://www.csse.monash.edu.au/~timf/
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <unistd.h>
|
|
|
+
|
|
|
+#include "common.h"
|
|
|
+#include "avcodec.h"
|
|
|
+#include "dsputil.h"
|
|
|
+
|
|
|
+#define PALETTE_COUNT 256
|
|
|
+
|
|
|
+#define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
|
|
|
+#define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \
|
|
|
+ (((uint8_t*)(x))[1] << 16) | \
|
|
|
+ (((uint8_t*)(x))[2] << 8) | \
|
|
|
+ ((uint8_t*)(x))[3])
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ uint8_t y0, y1, y2, y3;
|
|
|
+ uint8_t u, v;
|
|
|
+} cvid_codebook_t;
|
|
|
+
|
|
|
+#define MAX_STRIPS 32
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ uint16_t id;
|
|
|
+ uint16_t x1, y1;
|
|
|
+ uint16_t x2, y2;
|
|
|
+ cvid_codebook_t v4_codebook[256];
|
|
|
+ cvid_codebook_t v1_codebook[256];
|
|
|
+} cvid_strip_t;
|
|
|
+
|
|
|
+typedef struct CinepakContext {
|
|
|
+
|
|
|
+ AVCodecContext *avctx;
|
|
|
+ DSPContext dsp;
|
|
|
+ AVFrame frame;
|
|
|
+ AVFrame prev_frame;
|
|
|
+
|
|
|
+ unsigned char *data;
|
|
|
+ int size;
|
|
|
+
|
|
|
+ unsigned char palette[PALETTE_COUNT * 4];
|
|
|
+ int palette_video;
|
|
|
+ cvid_strip_t strips[MAX_STRIPS];
|
|
|
+
|
|
|
+} CinepakContext;
|
|
|
+
|
|
|
+static void cinepak_decode_codebook (cvid_codebook_t *codebook,
|
|
|
+ int chunk_id, int size, uint8_t *data)
|
|
|
+{
|
|
|
+ uint8_t *eod = (data + size);
|
|
|
+ uint32_t flag, mask;
|
|
|
+ int i, n;
|
|
|
+
|
|
|
+ /* check if this chunk contains 4- or 6-element vectors */
|
|
|
+ n = (chunk_id & 0x0400) ? 4 : 6;
|
|
|
+ flag = 0;
|
|
|
+ mask = 0;
|
|
|
+
|
|
|
+ for (i=0; i < 256; i++) {
|
|
|
+ if ((chunk_id & 0x0100) && !(mask >>= 1)) {
|
|
|
+ if ((data + 4) > eod)
|
|
|
+ break;
|
|
|
+
|
|
|
+ flag = BE_32 (data);
|
|
|
+ data += 4;
|
|
|
+ mask = 0x80000000;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(chunk_id & 0x0100) || (flag & mask)) {
|
|
|
+ if ((data + n) > eod)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (n == 6) {
|
|
|
+ codebook[i].y0 = *data++;
|
|
|
+ codebook[i].y1 = *data++;
|
|
|
+ codebook[i].y2 = *data++;
|
|
|
+ codebook[i].y3 = *data++;
|
|
|
+ codebook[i].u = 128 + *data++;
|
|
|
+ codebook[i].v = 128 + *data++;
|
|
|
+ } else {
|
|
|
+ /* this codebook type indicates either greyscale or
|
|
|
+ * palettized video; if palettized, U & V components will
|
|
|
+ * not be used so it is safe to set them to 128 for the
|
|
|
+ * benefit of greyscale rendering in YUV420P */
|
|
|
+ codebook[i].y0 = *data++;
|
|
|
+ codebook[i].y1 = *data++;
|
|
|
+ codebook[i].y2 = *data++;
|
|
|
+ codebook[i].y3 = *data++;
|
|
|
+ codebook[i].u = 128;
|
|
|
+ codebook[i].v = 128;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int cinepak_decode_vectors (CinepakContext *s, cvid_strip_t *strip,
|
|
|
+ int chunk_id, int size, uint8_t *data)
|
|
|
+{
|
|
|
+ uint8_t *eod = (data + size);
|
|
|
+ uint32_t flag, mask;
|
|
|
+ cvid_codebook_t *codebook;
|
|
|
+ unsigned int i, j, x, y;
|
|
|
+ uint32_t iy[4];
|
|
|
+ uint32_t iu[2];
|
|
|
+ uint32_t iv[2];
|
|
|
+
|
|
|
+ flag = 0;
|
|
|
+ mask = 0;
|
|
|
+
|
|
|
+ for (y=strip->y1; y < strip->y2; y+=4) {
|
|
|
+
|
|
|
+ iy[0] = strip->x1 + (y * s->frame.linesize[0]);
|
|
|
+ iy[1] = iy[0] + s->frame.linesize[0];
|
|
|
+ iy[2] = iy[1] + s->frame.linesize[0];
|
|
|
+ iy[3] = iy[2] + s->frame.linesize[0];
|
|
|
+ iu[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[1]);
|
|
|
+ iu[1] = iu[0] + s->frame.linesize[1];
|
|
|
+ iv[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[2]);
|
|
|
+ iv[1] = iv[0] + s->frame.linesize[2];
|
|
|
+
|
|
|
+ for (x=strip->x1; x < strip->x2; x+=4) {
|
|
|
+ if ((chunk_id & 0x0100) && !(mask >>= 1)) {
|
|
|
+ if ((data + 4) > eod)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ flag = BE_32 (data);
|
|
|
+ data += 4;
|
|
|
+ mask = 0x80000000;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(chunk_id & 0x0100) || (flag & mask)) {
|
|
|
+ if (!(chunk_id & 0x0200) && !(mask >>= 1)) {
|
|
|
+ if ((data + 4) > eod)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ flag = BE_32 (data);
|
|
|
+ data += 4;
|
|
|
+ mask = 0x80000000;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((chunk_id & 0x0200) || (~flag & mask)) {
|
|
|
+ if (data >= eod)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ codebook = &strip->v1_codebook[*data++];
|
|
|
+ s->frame.data[0][iy[0] + 0] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[0] + 1] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[1] + 0] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[1] + 1] = codebook->y0;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[0]] = codebook->u;
|
|
|
+ s->frame.data[2][iv[0]] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ s->frame.data[0][iy[0] + 2] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[0] + 3] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[1] + 2] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[1] + 3] = codebook->y0;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[0] + 1] = codebook->u;
|
|
|
+ s->frame.data[2][iv[0] + 1] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ s->frame.data[0][iy[2] + 0] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[2] + 1] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[3] + 0] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[3] + 1] = codebook->y0;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[1]] = codebook->u;
|
|
|
+ s->frame.data[2][iv[1]] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ s->frame.data[0][iy[2] + 2] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[2] + 3] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[3] + 2] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[3] + 3] = codebook->y0;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[1] + 1] = codebook->u;
|
|
|
+ s->frame.data[2][iv[1] + 1] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (flag & mask) {
|
|
|
+ if ((data + 4) > eod)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ codebook = &strip->v4_codebook[*data++];
|
|
|
+ s->frame.data[0][iy[0] + 0] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[0] + 1] = codebook->y1;
|
|
|
+ s->frame.data[0][iy[1] + 0] = codebook->y2;
|
|
|
+ s->frame.data[0][iy[1] + 1] = codebook->y3;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[0]] = codebook->u;
|
|
|
+ s->frame.data[2][iv[0]] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ codebook = &strip->v4_codebook[*data++];
|
|
|
+ s->frame.data[0][iy[0] + 2] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[0] + 3] = codebook->y1;
|
|
|
+ s->frame.data[0][iy[1] + 2] = codebook->y2;
|
|
|
+ s->frame.data[0][iy[1] + 3] = codebook->y3;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[0] + 1] = codebook->u;
|
|
|
+ s->frame.data[2][iv[0] + 1] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ codebook = &strip->v4_codebook[*data++];
|
|
|
+ s->frame.data[0][iy[2] + 0] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[2] + 1] = codebook->y1;
|
|
|
+ s->frame.data[0][iy[3] + 0] = codebook->y2;
|
|
|
+ s->frame.data[0][iy[3] + 1] = codebook->y3;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[1]] = codebook->u;
|
|
|
+ s->frame.data[2][iv[1]] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ codebook = &strip->v4_codebook[*data++];
|
|
|
+ s->frame.data[0][iy[2] + 2] = codebook->y0;
|
|
|
+ s->frame.data[0][iy[2] + 3] = codebook->y1;
|
|
|
+ s->frame.data[0][iy[3] + 2] = codebook->y2;
|
|
|
+ s->frame.data[0][iy[3] + 3] = codebook->y3;
|
|
|
+ if (!s->palette_video) {
|
|
|
+ s->frame.data[1][iu[1] + 1] = codebook->u;
|
|
|
+ s->frame.data[2][iv[1] + 1] = codebook->v;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* copy from the previous frame */
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
+ for (j = 0; j < 4; j++) {
|
|
|
+ s->frame.data[0][iy[i] + j] =
|
|
|
+ s->prev_frame.data[0][iy[i] + j];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (i = 0; i < 2; i++) {
|
|
|
+ for (j = 0; j < 2; j++) {
|
|
|
+ s->frame.data[1][iu[i] + j] =
|
|
|
+ s->prev_frame.data[1][iu[i] + j];
|
|
|
+ s->frame.data[2][iv[i] + j] =
|
|
|
+ s->prev_frame.data[2][iv[i] + j];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ iy[0] += 4; iy[1] += 4;
|
|
|
+ iy[2] += 4; iy[3] += 4;
|
|
|
+ iu[0] += 2; iu[1] += 2;
|
|
|
+ iv[0] += 2; iv[1] += 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cinepak_decode_strip (CinepakContext *s,
|
|
|
+ cvid_strip_t *strip, uint8_t *data, int size)
|
|
|
+{
|
|
|
+ uint8_t *eod = (data + size);
|
|
|
+ int chunk_id, chunk_size;
|
|
|
+
|
|
|
+ /* coordinate sanity checks */
|
|
|
+ if (strip->x1 >= s->avctx->width || strip->x2 > s->avctx->width ||
|
|
|
+ strip->y1 >= s->avctx->height || strip->y2 > s->avctx->height ||
|
|
|
+ strip->x1 >= strip->x2 || strip->y1 >= strip->y2)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ while ((data + 4) <= eod) {
|
|
|
+ chunk_id = BE_16 (&data[0]);
|
|
|
+ chunk_size = BE_16 (&data[2]) - 4;
|
|
|
+ data += 4;
|
|
|
+ chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size;
|
|
|
+
|
|
|
+ switch (chunk_id) {
|
|
|
+
|
|
|
+ case 0x2000:
|
|
|
+ case 0x2100:
|
|
|
+ case 0x2400:
|
|
|
+ case 0x2500:
|
|
|
+ cinepak_decode_codebook (strip->v4_codebook, chunk_id,
|
|
|
+ chunk_size, data);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 0x2200:
|
|
|
+ case 0x2300:
|
|
|
+ case 0x2600:
|
|
|
+ case 0x2700:
|
|
|
+ cinepak_decode_codebook (strip->v1_codebook, chunk_id,
|
|
|
+ chunk_size, data);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 0x3000:
|
|
|
+ case 0x3100:
|
|
|
+ case 0x3200:
|
|
|
+ return cinepak_decode_vectors (s, strip, chunk_id,
|
|
|
+ chunk_size, data);
|
|
|
+ }
|
|
|
+
|
|
|
+ data += chunk_size;
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+static int cinepak_decode (CinepakContext *s)
|
|
|
+{
|
|
|
+ uint8_t *eod = (s->data + s->size);
|
|
|
+ int i, result, strip_size, frame_flags, num_strips;
|
|
|
+ int y0 = 0;
|
|
|
+
|
|
|
+ if (s->size < 10)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ frame_flags = s->data[0];
|
|
|
+ num_strips = BE_16 (&s->data[8]);
|
|
|
+ s->data += 10;
|
|
|
+
|
|
|
+ if (num_strips > MAX_STRIPS)
|
|
|
+ num_strips = MAX_STRIPS;
|
|
|
+
|
|
|
+ for (i=0; i < num_strips; i++) {
|
|
|
+ if ((s->data + 12) > eod)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ s->strips[i].id = BE_16 (s->data);
|
|
|
+ s->strips[i].y1 = y0;
|
|
|
+ s->strips[i].x1 = 0;
|
|
|
+ s->strips[i].y2 = y0 + BE_16 (&s->data[8]);
|
|
|
+ s->strips[i].x2 = s->avctx->width;
|
|
|
+
|
|
|
+ strip_size = BE_16 (&s->data[2]) - 12;
|
|
|
+ s->data += 12;
|
|
|
+ strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;
|
|
|
+
|
|
|
+ if ((i > 0) && !(frame_flags & 0x01)) {
|
|
|
+ memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook,
|
|
|
+ sizeof(s->strips[i].v4_codebook));
|
|
|
+ memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook,
|
|
|
+ sizeof(s->strips[i].v1_codebook));
|
|
|
+ }
|
|
|
+
|
|
|
+ result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size);
|
|
|
+
|
|
|
+ if (result != 0)
|
|
|
+ return result;
|
|
|
+
|
|
|
+ s->data += strip_size;
|
|
|
+ y0 = s->strips[i].y2;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cinepak_decode_init(AVCodecContext *avctx)
|
|
|
+{
|
|
|
+ CinepakContext *s = (CinepakContext *)avctx->priv_data;
|
|
|
+/*
|
|
|
+ int i;
|
|
|
+ unsigned char r, g, b;
|
|
|
+ unsigned char *raw_palette;
|
|
|
+ unsigned int *palette32;
|
|
|
+*/
|
|
|
+
|
|
|
+ s->avctx = avctx;
|
|
|
+
|
|
|
+// check for paletted data
|
|
|
+s->palette_video = 0;
|
|
|
+
|
|
|
+
|
|
|
+ avctx->pix_fmt = PIX_FMT_YUV420P;
|
|
|
+ avctx->has_b_frames = 0;
|
|
|
+ dsputil_init(&s->dsp, avctx);
|
|
|
+
|
|
|
+ s->frame.data[0] = s->prev_frame.data[0] = NULL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cinepak_decode_frame(AVCodecContext *avctx,
|
|
|
+ void *data, int *data_size,
|
|
|
+ uint8_t *buf, int buf_size)
|
|
|
+{
|
|
|
+ CinepakContext *s = (CinepakContext *)avctx->priv_data;
|
|
|
+
|
|
|
+ s->data = buf;
|
|
|
+ s->size = buf_size;
|
|
|
+
|
|
|
+ if (avctx->get_buffer(avctx, &s->frame)) {
|
|
|
+ printf (" Cinepak: get_buffer() failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ cinepak_decode(s);
|
|
|
+
|
|
|
+ if (s->prev_frame.data[0])
|
|
|
+ avctx->release_buffer(avctx, &s->prev_frame);
|
|
|
+
|
|
|
+ /* shuffle frames */
|
|
|
+ s->prev_frame = s->frame;
|
|
|
+
|
|
|
+ *data_size = sizeof(AVFrame);
|
|
|
+ *(AVFrame*)data = s->frame;
|
|
|
+
|
|
|
+ /* report that the buffer was completely consumed */
|
|
|
+ return buf_size;
|
|
|
+}
|
|
|
+
|
|
|
+static int cinepak_decode_end(AVCodecContext *avctx)
|
|
|
+{
|
|
|
+ CinepakContext *s = (CinepakContext *)avctx->priv_data;
|
|
|
+
|
|
|
+ if (s->prev_frame.data[0])
|
|
|
+ avctx->release_buffer(avctx, &s->prev_frame);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+AVCodec cinepak_decoder = {
|
|
|
+ "cinepak",
|
|
|
+ CODEC_TYPE_VIDEO,
|
|
|
+ CODEC_ID_CINEPAK,
|
|
|
+ sizeof(CinepakContext),
|
|
|
+ cinepak_decode_init,
|
|
|
+ NULL,
|
|
|
+ cinepak_decode_end,
|
|
|
+ cinepak_decode_frame,
|
|
|
+ CODEC_CAP_DR1,
|
|
|
+};
|