00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <unistd.h>
00033
00034 #include "common.h"
00035 #include "avcodec.h"
00036 #include "dsputil.h"
00037
00038
00039 typedef struct {
00040 uint8_t y0, y1, y2, y3;
00041 uint8_t u, v;
00042 } cvid_codebook_t;
00043
00044 #define MAX_STRIPS 32
00045
00046 typedef struct {
00047 uint16_t id;
00048 uint16_t x1, y1;
00049 uint16_t x2, y2;
00050 cvid_codebook_t v4_codebook[256];
00051 cvid_codebook_t v1_codebook[256];
00052 } cvid_strip_t;
00053
00054 typedef struct CinepakContext {
00055
00056 AVCodecContext *avctx;
00057 DSPContext dsp;
00058 AVFrame frame;
00059
00060 unsigned char *data;
00061 int size;
00062
00063 int width, height;
00064
00065 int palette_video;
00066 cvid_strip_t strips[MAX_STRIPS];
00067
00068 } CinepakContext;
00069
00070 static void cinepak_decode_codebook (cvid_codebook_t *codebook,
00071 int chunk_id, int size, uint8_t *data)
00072 {
00073 uint8_t *eod = (data + size);
00074 uint32_t flag, mask;
00075 int i, n;
00076
00077
00078 n = (chunk_id & 0x0400) ? 4 : 6;
00079 flag = 0;
00080 mask = 0;
00081
00082 for (i=0; i < 256; i++) {
00083 if ((chunk_id & 0x0100) && !(mask >>= 1)) {
00084 if ((data + 4) > eod)
00085 break;
00086
00087 flag = BE_32 (data);
00088 data += 4;
00089 mask = 0x80000000;
00090 }
00091
00092 if (!(chunk_id & 0x0100) || (flag & mask)) {
00093 if ((data + n) > eod)
00094 break;
00095
00096 if (n == 6) {
00097 codebook[i].y0 = *data++;
00098 codebook[i].y1 = *data++;
00099 codebook[i].y2 = *data++;
00100 codebook[i].y3 = *data++;
00101 codebook[i].u = 128 + *data++;
00102 codebook[i].v = 128 + *data++;
00103 } else {
00104
00105
00106
00107
00108 codebook[i].y0 = *data++;
00109 codebook[i].y1 = *data++;
00110 codebook[i].y2 = *data++;
00111 codebook[i].y3 = *data++;
00112 codebook[i].u = 128;
00113 codebook[i].v = 128;
00114 }
00115 }
00116 }
00117 }
00118
00119 static int cinepak_decode_vectors (CinepakContext *s, cvid_strip_t *strip,
00120 int chunk_id, int size, uint8_t *data)
00121 {
00122 uint8_t *eod = (data + size);
00123 uint32_t flag, mask;
00124 cvid_codebook_t *codebook;
00125 unsigned int x, y;
00126 uint32_t iy[4];
00127 uint32_t iu[2];
00128 uint32_t iv[2];
00129
00130 flag = 0;
00131 mask = 0;
00132
00133 for (y=strip->y1; y < strip->y2; y+=4) {
00134
00135 iy[0] = strip->x1 + (y * s->frame.linesize[0]);
00136 iy[1] = iy[0] + s->frame.linesize[0];
00137 iy[2] = iy[1] + s->frame.linesize[0];
00138 iy[3] = iy[2] + s->frame.linesize[0];
00139 iu[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[1]);
00140 iu[1] = iu[0] + s->frame.linesize[1];
00141 iv[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[2]);
00142 iv[1] = iv[0] + s->frame.linesize[2];
00143
00144 for (x=strip->x1; x < strip->x2; x+=4) {
00145 if ((chunk_id & 0x0100) && !(mask >>= 1)) {
00146 if ((data + 4) > eod)
00147 return -1;
00148
00149 flag = BE_32 (data);
00150 data += 4;
00151 mask = 0x80000000;
00152 }
00153
00154 if (!(chunk_id & 0x0100) || (flag & mask)) {
00155 if (!(chunk_id & 0x0200) && !(mask >>= 1)) {
00156 if ((data + 4) > eod)
00157 return -1;
00158
00159 flag = BE_32 (data);
00160 data += 4;
00161 mask = 0x80000000;
00162 }
00163
00164 if ((chunk_id & 0x0200) || (~flag & mask)) {
00165 if (data >= eod)
00166 return -1;
00167
00168 codebook = &strip->v1_codebook[*data++];
00169 s->frame.data[0][iy[0] + 0] = codebook->y0;
00170 s->frame.data[0][iy[0] + 1] = codebook->y0;
00171 s->frame.data[0][iy[1] + 0] = codebook->y0;
00172 s->frame.data[0][iy[1] + 1] = codebook->y0;
00173 if (!s->palette_video) {
00174 s->frame.data[1][iu[0]] = codebook->u;
00175 s->frame.data[2][iv[0]] = codebook->v;
00176 }
00177
00178 s->frame.data[0][iy[0] + 2] = codebook->y1;
00179 s->frame.data[0][iy[0] + 3] = codebook->y1;
00180 s->frame.data[0][iy[1] + 2] = codebook->y1;
00181 s->frame.data[0][iy[1] + 3] = codebook->y1;
00182 if (!s->palette_video) {
00183 s->frame.data[1][iu[0] + 1] = codebook->u;
00184 s->frame.data[2][iv[0] + 1] = codebook->v;
00185 }
00186
00187 s->frame.data[0][iy[2] + 0] = codebook->y2;
00188 s->frame.data[0][iy[2] + 1] = codebook->y2;
00189 s->frame.data[0][iy[3] + 0] = codebook->y2;
00190 s->frame.data[0][iy[3] + 1] = codebook->y2;
00191 if (!s->palette_video) {
00192 s->frame.data[1][iu[1]] = codebook->u;
00193 s->frame.data[2][iv[1]] = codebook->v;
00194 }
00195
00196 s->frame.data[0][iy[2] + 2] = codebook->y3;
00197 s->frame.data[0][iy[2] + 3] = codebook->y3;
00198 s->frame.data[0][iy[3] + 2] = codebook->y3;
00199 s->frame.data[0][iy[3] + 3] = codebook->y3;
00200 if (!s->palette_video) {
00201 s->frame.data[1][iu[1] + 1] = codebook->u;
00202 s->frame.data[2][iv[1] + 1] = codebook->v;
00203 }
00204
00205 } else if (flag & mask) {
00206 if ((data + 4) > eod)
00207 return -1;
00208
00209 codebook = &strip->v4_codebook[*data++];
00210 s->frame.data[0][iy[0] + 0] = codebook->y0;
00211 s->frame.data[0][iy[0] + 1] = codebook->y1;
00212 s->frame.data[0][iy[1] + 0] = codebook->y2;
00213 s->frame.data[0][iy[1] + 1] = codebook->y3;
00214 if (!s->palette_video) {
00215 s->frame.data[1][iu[0]] = codebook->u;
00216 s->frame.data[2][iv[0]] = codebook->v;
00217 }
00218
00219 codebook = &strip->v4_codebook[*data++];
00220 s->frame.data[0][iy[0] + 2] = codebook->y0;
00221 s->frame.data[0][iy[0] + 3] = codebook->y1;
00222 s->frame.data[0][iy[1] + 2] = codebook->y2;
00223 s->frame.data[0][iy[1] + 3] = codebook->y3;
00224 if (!s->palette_video) {
00225 s->frame.data[1][iu[0] + 1] = codebook->u;
00226 s->frame.data[2][iv[0] + 1] = codebook->v;
00227 }
00228
00229 codebook = &strip->v4_codebook[*data++];
00230 s->frame.data[0][iy[2] + 0] = codebook->y0;
00231 s->frame.data[0][iy[2] + 1] = codebook->y1;
00232 s->frame.data[0][iy[3] + 0] = codebook->y2;
00233 s->frame.data[0][iy[3] + 1] = codebook->y3;
00234 if (!s->palette_video) {
00235 s->frame.data[1][iu[1]] = codebook->u;
00236 s->frame.data[2][iv[1]] = codebook->v;
00237 }
00238
00239 codebook = &strip->v4_codebook[*data++];
00240 s->frame.data[0][iy[2] + 2] = codebook->y0;
00241 s->frame.data[0][iy[2] + 3] = codebook->y1;
00242 s->frame.data[0][iy[3] + 2] = codebook->y2;
00243 s->frame.data[0][iy[3] + 3] = codebook->y3;
00244 if (!s->palette_video) {
00245 s->frame.data[1][iu[1] + 1] = codebook->u;
00246 s->frame.data[2][iv[1] + 1] = codebook->v;
00247 }
00248
00249 }
00250 }
00251
00252 iy[0] += 4; iy[1] += 4;
00253 iy[2] += 4; iy[3] += 4;
00254 iu[0] += 2; iu[1] += 2;
00255 iv[0] += 2; iv[1] += 2;
00256 }
00257 }
00258
00259 return 0;
00260 }
00261
00262 static int cinepak_decode_strip (CinepakContext *s,
00263 cvid_strip_t *strip, uint8_t *data, int size)
00264 {
00265 uint8_t *eod = (data + size);
00266 int chunk_id, chunk_size;
00267
00268
00269 if (strip->x1 >= s->width || strip->x2 > s->width ||
00270 strip->y1 >= s->height || strip->y2 > s->height ||
00271 strip->x1 >= strip->x2 || strip->y1 >= strip->y2)
00272 return -1;
00273
00274 while ((data + 4) <= eod) {
00275 chunk_id = BE_16 (&data[0]);
00276 chunk_size = BE_16 (&data[2]) - 4;
00277 if(chunk_size < 0)
00278 return -1;
00279
00280 data += 4;
00281 chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size;
00282
00283 switch (chunk_id) {
00284
00285 case 0x2000:
00286 case 0x2100:
00287 case 0x2400:
00288 case 0x2500:
00289 cinepak_decode_codebook (strip->v4_codebook, chunk_id,
00290 chunk_size, data);
00291 break;
00292
00293 case 0x2200:
00294 case 0x2300:
00295 case 0x2600:
00296 case 0x2700:
00297 cinepak_decode_codebook (strip->v1_codebook, chunk_id,
00298 chunk_size, data);
00299 break;
00300
00301 case 0x3000:
00302 case 0x3100:
00303 case 0x3200:
00304 return cinepak_decode_vectors (s, strip, chunk_id,
00305 chunk_size, data);
00306 }
00307
00308 data += chunk_size;
00309 }
00310
00311 return -1;
00312 }
00313
00314 static int cinepak_decode (CinepakContext *s)
00315 {
00316 uint8_t *eod = (s->data + s->size);
00317 int i, result, strip_size, frame_flags, num_strips;
00318 int y0 = 0;
00319
00320 if (s->size < 10)
00321 return -1;
00322
00323 frame_flags = s->data[0];
00324 num_strips = BE_16 (&s->data[8]);
00325 s->data += 10;
00326
00327 if (num_strips > MAX_STRIPS)
00328 num_strips = MAX_STRIPS;
00329
00330 for (i=0; i < num_strips; i++) {
00331 if ((s->data + 12) > eod)
00332 return -1;
00333
00334 s->strips[i].id = BE_16 (s->data);
00335 s->strips[i].y1 = y0;
00336 s->strips[i].x1 = 0;
00337 s->strips[i].y2 = y0 + BE_16 (&s->data[8]);
00338 s->strips[i].x2 = s->avctx->width;
00339
00340 strip_size = BE_16 (&s->data[2]) - 12;
00341 s->data += 12;
00342 strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;
00343
00344 if ((i > 0) && !(frame_flags & 0x01)) {
00345 memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook,
00346 sizeof(s->strips[i].v4_codebook));
00347 memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook,
00348 sizeof(s->strips[i].v1_codebook));
00349 }
00350
00351 result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size);
00352
00353 if (result != 0)
00354 return result;
00355
00356 s->data += strip_size;
00357 y0 = s->strips[i].y2;
00358 }
00359 return 0;
00360 }
00361
00362 static int cinepak_decode_init(AVCodecContext *avctx)
00363 {
00364 CinepakContext *s = (CinepakContext *)avctx->priv_data;
00365
00366 s->avctx = avctx;
00367 s->width = (avctx->width + 3) & ~3;
00368 s->height = (avctx->height + 3) & ~3;
00369
00370
00371 if ((avctx->palctrl == NULL) || (avctx->bits_per_sample == 40)) {
00372 s->palette_video = 0;
00373 avctx->pix_fmt = PIX_FMT_YUV420P;
00374 } else {
00375 s->palette_video = 1;
00376 avctx->pix_fmt = PIX_FMT_PAL8;
00377 }
00378
00379 avctx->has_b_frames = 0;
00380 dsputil_init(&s->dsp, avctx);
00381
00382 s->frame.data[0] = NULL;
00383
00384 return 0;
00385 }
00386
00387 static int cinepak_decode_frame(AVCodecContext *avctx,
00388 void *data, int *data_size,
00389 uint8_t *buf, int buf_size)
00390 {
00391 CinepakContext *s = (CinepakContext *)avctx->priv_data;
00392
00393 s->data = buf;
00394 s->size = buf_size;
00395
00396 s->frame.reference = 1;
00397 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
00398 FF_BUFFER_HINTS_REUSABLE;
00399 if (avctx->reget_buffer(avctx, &s->frame)) {
00400 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
00401 return -1;
00402 }
00403
00404 cinepak_decode(s);
00405
00406 if (s->palette_video) {
00407 memcpy (s->frame.data[1], avctx->palctrl->palette, AVPALETTE_SIZE);
00408 if (avctx->palctrl->palette_changed) {
00409 s->frame.palette_has_changed = 1;
00410 avctx->palctrl->palette_changed = 0;
00411 } else
00412 s->frame.palette_has_changed = 0;
00413 }
00414
00415 *data_size = sizeof(AVFrame);
00416 *(AVFrame*)data = s->frame;
00417
00418
00419 return buf_size;
00420 }
00421
00422 static int cinepak_decode_end(AVCodecContext *avctx)
00423 {
00424 CinepakContext *s = (CinepakContext *)avctx->priv_data;
00425
00426 if (s->frame.data[0])
00427 avctx->release_buffer(avctx, &s->frame);
00428
00429 return 0;
00430 }
00431
00432 AVCodec cinepak_decoder = {
00433 "cinepak",
00434 CODEC_TYPE_VIDEO,
00435 CODEC_ID_CINEPAK,
00436 sizeof(CinepakContext),
00437 cinepak_decode_init,
00438 NULL,
00439 cinepak_decode_end,
00440 cinepak_decode_frame,
00441 CODEC_CAP_DR1,
00442 };