annotate ffmpeg/libavcodec/cinepak.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents 6840f77b83aa
children
rev   line source
yading@10 1 /*
yading@10 2 * Cinepak Video Decoder
yading@10 3 * Copyright (C) 2003 the ffmpeg project
yading@10 4 *
yading@10 5 * This file is part of FFmpeg.
yading@10 6 *
yading@10 7 * FFmpeg is free software; you can redistribute it and/or
yading@10 8 * modify it under the terms of the GNU Lesser General Public
yading@10 9 * License as published by the Free Software Foundation; either
yading@10 10 * version 2.1 of the License, or (at your option) any later version.
yading@10 11 *
yading@10 12 * FFmpeg is distributed in the hope that it will be useful,
yading@10 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@10 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@10 15 * Lesser General Public License for more details.
yading@10 16 *
yading@10 17 * You should have received a copy of the GNU Lesser General Public
yading@10 18 * License along with FFmpeg; if not, write to the Free Software
yading@10 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@10 20 */
yading@10 21
yading@10 22 /**
yading@10 23 * @file
yading@10 24 * Cinepak video decoder
yading@10 25 * @author Ewald Snel <ewald@rambo.its.tudelft.nl>
yading@10 26 *
yading@10 27 * @see For more information on the Cinepak algorithm, visit:
yading@10 28 * http://www.csse.monash.edu.au/~timf/
yading@10 29 * @see For more information on the quirky data inside Sega FILM/CPK files, visit:
yading@10 30 * http://wiki.multimedia.cx/index.php?title=Sega_FILM
yading@10 31 *
yading@10 32 * Cinepak colorspace support (c) 2013 Rl, Aetey Global Technologies AB
yading@10 33 * @author Cinepak colorspace, Rl, Aetey Global Technologies AB
yading@10 34 */
yading@10 35
yading@10 36 #include <stdio.h>
yading@10 37 #include <stdlib.h>
yading@10 38 #include <string.h>
yading@10 39
yading@10 40 #include "libavutil/common.h"
yading@10 41 #include "libavutil/intreadwrite.h"
yading@10 42 #include "avcodec.h"
yading@10 43 #include "internal.h"
yading@10 44
yading@10 45
yading@10 46 typedef uint8_t cvid_codebook[12];
yading@10 47
yading@10 48 #define MAX_STRIPS 32
yading@10 49
yading@10 50 typedef struct {
yading@10 51 uint16_t id;
yading@10 52 uint16_t x1, y1;
yading@10 53 uint16_t x2, y2;
yading@10 54 cvid_codebook v4_codebook[256];
yading@10 55 cvid_codebook v1_codebook[256];
yading@10 56 } cvid_strip;
yading@10 57
yading@10 58 typedef struct CinepakContext {
yading@10 59
yading@10 60 AVCodecContext *avctx;
yading@10 61 AVFrame *frame;
yading@10 62
yading@10 63 const unsigned char *data;
yading@10 64 int size;
yading@10 65
yading@10 66 int width, height;
yading@10 67
yading@10 68 int palette_video;
yading@10 69 cvid_strip strips[MAX_STRIPS];
yading@10 70
yading@10 71 int sega_film_skip_bytes;
yading@10 72
yading@10 73 uint32_t pal[256];
yading@10 74 } CinepakContext;
yading@10 75
yading@10 76 static void cinepak_decode_codebook (cvid_codebook *codebook,
yading@10 77 int chunk_id, int size, const uint8_t *data)
yading@10 78 {
yading@10 79 const uint8_t *eod = (data + size);
yading@10 80 uint32_t flag, mask;
yading@10 81 int i, n;
yading@10 82 uint8_t *p;
yading@10 83
yading@10 84 /* check if this chunk contains 4- or 6-element vectors */
yading@10 85 n = (chunk_id & 0x04) ? 4 : 6;
yading@10 86 flag = 0;
yading@10 87 mask = 0;
yading@10 88
yading@10 89 p = codebook[0];
yading@10 90 for (i=0; i < 256; i++) {
yading@10 91 if ((chunk_id & 0x01) && !(mask >>= 1)) {
yading@10 92 if ((data + 4) > eod)
yading@10 93 break;
yading@10 94
yading@10 95 flag = AV_RB32 (data);
yading@10 96 data += 4;
yading@10 97 mask = 0x80000000;
yading@10 98 }
yading@10 99
yading@10 100 if (!(chunk_id & 0x01) || (flag & mask)) {
yading@10 101 int k, kk;
yading@10 102
yading@10 103 if ((data + n) > eod)
yading@10 104 break;
yading@10 105
yading@10 106 for (k = 0; k < 4; ++k) {
yading@10 107 int r = *data++;
yading@10 108 for (kk = 0; kk < 3; ++kk)
yading@10 109 *p++ = r;
yading@10 110 }
yading@10 111 if (n == 6) {
yading@10 112 int r, g, b, u, v;
yading@10 113 u = *(int8_t *)data++;
yading@10 114 v = *(int8_t *)data++;
yading@10 115 p -= 12;
yading@10 116 for(k=0; k<4; ++k) {
yading@10 117 r = *p++ + v*2;
yading@10 118 g = *p++ - (u/2) - v;
yading@10 119 b = *p + u*2;
yading@10 120 p -= 2;
yading@10 121 *p++ = av_clip_uint8(r);
yading@10 122 *p++ = av_clip_uint8(g);
yading@10 123 *p++ = av_clip_uint8(b);
yading@10 124 }
yading@10 125 }
yading@10 126 } else {
yading@10 127 p += 12;
yading@10 128 }
yading@10 129 }
yading@10 130 }
yading@10 131
yading@10 132 static int cinepak_decode_vectors (CinepakContext *s, cvid_strip *strip,
yading@10 133 int chunk_id, int size, const uint8_t *data)
yading@10 134 {
yading@10 135 const uint8_t *eod = (data + size);
yading@10 136 uint32_t flag, mask;
yading@10 137 uint8_t *cb0, *cb1, *cb2, *cb3;
yading@10 138 unsigned int x, y;
yading@10 139 char *ip0, *ip1, *ip2, *ip3;
yading@10 140
yading@10 141 flag = 0;
yading@10 142 mask = 0;
yading@10 143
yading@10 144 for (y=strip->y1; y < strip->y2; y+=4) {
yading@10 145
yading@10 146 /* take care of y dimension not being multiple of 4, such streams exist */
yading@10 147 ip0 = ip1 = ip2 = ip3 = s->frame->data[0] +
yading@10 148 (s->palette_video?strip->x1:strip->x1*3) + (y * s->frame->linesize[0]);
yading@10 149 if(s->avctx->height - y > 1) {
yading@10 150 ip1 = ip0 + s->frame->linesize[0];
yading@10 151 if(s->avctx->height - y > 2) {
yading@10 152 ip2 = ip1 + s->frame->linesize[0];
yading@10 153 if(s->avctx->height - y > 3) {
yading@10 154 ip3 = ip2 + s->frame->linesize[0];
yading@10 155 }
yading@10 156 }
yading@10 157 }
yading@10 158 /* to get the correct picture for not-multiple-of-4 cases let us fill
yading@10 159 * each block from the bottom up, thus possibly overwriting the top line
yading@10 160 * more than once but ending with the correct data in place
yading@10 161 * (instead of in-loop checking) */
yading@10 162
yading@10 163 for (x=strip->x1; x < strip->x2; x+=4) {
yading@10 164 if ((chunk_id & 0x01) && !(mask >>= 1)) {
yading@10 165 if ((data + 4) > eod)
yading@10 166 return AVERROR_INVALIDDATA;
yading@10 167
yading@10 168 flag = AV_RB32 (data);
yading@10 169 data += 4;
yading@10 170 mask = 0x80000000;
yading@10 171 }
yading@10 172
yading@10 173 if (!(chunk_id & 0x01) || (flag & mask)) {
yading@10 174 if (!(chunk_id & 0x02) && !(mask >>= 1)) {
yading@10 175 if ((data + 4) > eod)
yading@10 176 return AVERROR_INVALIDDATA;
yading@10 177
yading@10 178 flag = AV_RB32 (data);
yading@10 179 data += 4;
yading@10 180 mask = 0x80000000;
yading@10 181 }
yading@10 182
yading@10 183 if ((chunk_id & 0x02) || (~flag & mask)) {
yading@10 184 uint8_t *p;
yading@10 185 if (data >= eod)
yading@10 186 return AVERROR_INVALIDDATA;
yading@10 187
yading@10 188 p = strip->v1_codebook[*data++];
yading@10 189 if (s->palette_video) {
yading@10 190 ip3[0] = ip3[1] = ip2[0] = ip2[1] = p[6];
yading@10 191 ip3[2] = ip3[3] = ip2[2] = ip2[3] = p[9];
yading@10 192 ip1[0] = ip1[1] = ip0[0] = ip0[1] = p[0];
yading@10 193 ip1[2] = ip1[3] = ip0[2] = ip0[3] = p[3];
yading@10 194 } else {
yading@10 195 p += 6;
yading@10 196 memcpy(ip3 + 0, p, 3); memcpy(ip3 + 3, p, 3);
yading@10 197 memcpy(ip2 + 0, p, 3); memcpy(ip2 + 3, p, 3);
yading@10 198 p += 3; /* ... + 9 */
yading@10 199 memcpy(ip3 + 6, p, 3); memcpy(ip3 + 9, p, 3);
yading@10 200 memcpy(ip2 + 6, p, 3); memcpy(ip2 + 9, p, 3);
yading@10 201 p -= 9; /* ... + 0 */
yading@10 202 memcpy(ip1 + 0, p, 3); memcpy(ip1 + 3, p, 3);
yading@10 203 memcpy(ip0 + 0, p, 3); memcpy(ip0 + 3, p, 3);
yading@10 204 p += 3; /* ... + 3 */
yading@10 205 memcpy(ip1 + 6, p, 3); memcpy(ip1 + 9, p, 3);
yading@10 206 memcpy(ip0 + 6, p, 3); memcpy(ip0 + 9, p, 3);
yading@10 207 }
yading@10 208
yading@10 209 } else if (flag & mask) {
yading@10 210 if ((data + 4) > eod)
yading@10 211 return AVERROR_INVALIDDATA;
yading@10 212
yading@10 213 cb0 = strip->v4_codebook[*data++];
yading@10 214 cb1 = strip->v4_codebook[*data++];
yading@10 215 cb2 = strip->v4_codebook[*data++];
yading@10 216 cb3 = strip->v4_codebook[*data++];
yading@10 217 if (s->palette_video) {
yading@10 218 uint8_t *p;
yading@10 219 p = ip3;
yading@10 220 *p++ = cb2[6];
yading@10 221 *p++ = cb2[9];
yading@10 222 *p++ = cb3[6];
yading@10 223 *p = cb3[9];
yading@10 224 p = ip2;
yading@10 225 *p++ = cb2[0];
yading@10 226 *p++ = cb2[3];
yading@10 227 *p++ = cb3[0];
yading@10 228 *p = cb3[3];
yading@10 229 p = ip1;
yading@10 230 *p++ = cb0[6];
yading@10 231 *p++ = cb0[9];
yading@10 232 *p++ = cb1[6];
yading@10 233 *p = cb1[9];
yading@10 234 p = ip0;
yading@10 235 *p++ = cb0[0];
yading@10 236 *p++ = cb0[3];
yading@10 237 *p++ = cb1[0];
yading@10 238 *p = cb1[3];
yading@10 239 } else {
yading@10 240 memcpy(ip3 + 0, cb2 + 6, 6);
yading@10 241 memcpy(ip3 + 6, cb3 + 6, 6);
yading@10 242 memcpy(ip2 + 0, cb2 + 0, 6);
yading@10 243 memcpy(ip2 + 6, cb3 + 0, 6);
yading@10 244 memcpy(ip1 + 0, cb0 + 6, 6);
yading@10 245 memcpy(ip1 + 6, cb1 + 6, 6);
yading@10 246 memcpy(ip0 + 0, cb0 + 0, 6);
yading@10 247 memcpy(ip0 + 6, cb1 + 0, 6);
yading@10 248 }
yading@10 249
yading@10 250 }
yading@10 251 }
yading@10 252
yading@10 253 if (s->palette_video) {
yading@10 254 ip0 += 4; ip1 += 4;
yading@10 255 ip2 += 4; ip3 += 4;
yading@10 256 } else {
yading@10 257 ip0 += 12; ip1 += 12;
yading@10 258 ip2 += 12; ip3 += 12;
yading@10 259 }
yading@10 260 }
yading@10 261 }
yading@10 262
yading@10 263 return 0;
yading@10 264 }
yading@10 265
yading@10 266 static int cinepak_decode_strip (CinepakContext *s,
yading@10 267 cvid_strip *strip, const uint8_t *data, int size)
yading@10 268 {
yading@10 269 const uint8_t *eod = (data + size);
yading@10 270 int chunk_id, chunk_size;
yading@10 271
yading@10 272 /* coordinate sanity checks */
yading@10 273 if (strip->x2 > s->width ||
yading@10 274 strip->y2 > s->height ||
yading@10 275 strip->x1 >= strip->x2 || strip->y1 >= strip->y2)
yading@10 276 return AVERROR_INVALIDDATA;
yading@10 277
yading@10 278 while ((data + 4) <= eod) {
yading@10 279 chunk_id = data[0];
yading@10 280 chunk_size = AV_RB24 (&data[1]) - 4;
yading@10 281 if(chunk_size < 0)
yading@10 282 return AVERROR_INVALIDDATA;
yading@10 283
yading@10 284 data += 4;
yading@10 285 chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size;
yading@10 286
yading@10 287 switch (chunk_id) {
yading@10 288
yading@10 289 case 0x20:
yading@10 290 case 0x21:
yading@10 291 case 0x24:
yading@10 292 case 0x25:
yading@10 293 cinepak_decode_codebook (strip->v4_codebook, chunk_id,
yading@10 294 chunk_size, data);
yading@10 295 break;
yading@10 296
yading@10 297 case 0x22:
yading@10 298 case 0x23:
yading@10 299 case 0x26:
yading@10 300 case 0x27:
yading@10 301 cinepak_decode_codebook (strip->v1_codebook, chunk_id,
yading@10 302 chunk_size, data);
yading@10 303 break;
yading@10 304
yading@10 305 case 0x30:
yading@10 306 case 0x31:
yading@10 307 case 0x32:
yading@10 308 return cinepak_decode_vectors (s, strip, chunk_id,
yading@10 309 chunk_size, data);
yading@10 310 }
yading@10 311
yading@10 312 data += chunk_size;
yading@10 313 }
yading@10 314
yading@10 315 return AVERROR_INVALIDDATA;
yading@10 316 }
yading@10 317
yading@10 318 static int cinepak_decode (CinepakContext *s)
yading@10 319 {
yading@10 320 const uint8_t *eod = (s->data + s->size);
yading@10 321 int i, result, strip_size, frame_flags, num_strips;
yading@10 322 int y0 = 0;
yading@10 323 int encoded_buf_size;
yading@10 324
yading@10 325 if (s->size < 10)
yading@10 326 return AVERROR_INVALIDDATA;
yading@10 327
yading@10 328 frame_flags = s->data[0];
yading@10 329 num_strips = AV_RB16 (&s->data[8]);
yading@10 330 encoded_buf_size = AV_RB24(&s->data[1]);
yading@10 331
yading@10 332 /* if this is the first frame, check for deviant Sega FILM data */
yading@10 333 if (s->sega_film_skip_bytes == -1) {
yading@10 334 if (!encoded_buf_size) {
yading@10 335 avpriv_request_sample(s->avctx, "encoded_buf_size 0");
yading@10 336 return AVERROR_PATCHWELCOME;
yading@10 337 }
yading@10 338 if (encoded_buf_size != s->size && (s->size % encoded_buf_size) != 0) {
yading@10 339 /* If the encoded frame size differs from the frame size as indicated
yading@10 340 * by the container file, this data likely comes from a Sega FILM/CPK file.
yading@10 341 * If the frame header is followed by the bytes FE 00 00 06 00 00 then
yading@10 342 * this is probably one of the two known files that have 6 extra bytes
yading@10 343 * after the frame header. Else, assume 2 extra bytes. The container
yading@10 344 * size also cannot be a multiple of the encoded size. */
yading@10 345 if (s->size >= 16 &&
yading@10 346 (s->data[10] == 0xFE) &&
yading@10 347 (s->data[11] == 0x00) &&
yading@10 348 (s->data[12] == 0x00) &&
yading@10 349 (s->data[13] == 0x06) &&
yading@10 350 (s->data[14] == 0x00) &&
yading@10 351 (s->data[15] == 0x00))
yading@10 352 s->sega_film_skip_bytes = 6;
yading@10 353 else
yading@10 354 s->sega_film_skip_bytes = 2;
yading@10 355 } else
yading@10 356 s->sega_film_skip_bytes = 0;
yading@10 357 }
yading@10 358
yading@10 359 s->data += 10 + s->sega_film_skip_bytes;
yading@10 360
yading@10 361 num_strips = FFMIN(num_strips, MAX_STRIPS);
yading@10 362
yading@10 363 s->frame->key_frame = 0;
yading@10 364
yading@10 365 for (i=0; i < num_strips; i++) {
yading@10 366 if ((s->data + 12) > eod)
yading@10 367 return AVERROR_INVALIDDATA;
yading@10 368
yading@10 369 s->strips[i].id = s->data[0];
yading@10 370 /* zero y1 means "relative to the previous stripe" */
yading@10 371 if (!(s->strips[i].y1 = AV_RB16 (&s->data[4])))
yading@10 372 s->strips[i].y2 = (s->strips[i].y1 = y0) + AV_RB16 (&s->data[8]);
yading@10 373 else
yading@10 374 s->strips[i].y2 = AV_RB16 (&s->data[8]);
yading@10 375 s->strips[i].x1 = AV_RB16 (&s->data[6]);
yading@10 376 s->strips[i].x2 = AV_RB16 (&s->data[10]);
yading@10 377
yading@10 378 if (s->strips[i].id == 0x10)
yading@10 379 s->frame->key_frame = 1;
yading@10 380
yading@10 381 strip_size = AV_RB24 (&s->data[1]) - 12;
yading@10 382 if (strip_size < 0)
yading@10 383 return AVERROR_INVALIDDATA;
yading@10 384 s->data += 12;
yading@10 385 strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;
yading@10 386
yading@10 387 if ((i > 0) && !(frame_flags & 0x01)) {
yading@10 388 memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook,
yading@10 389 sizeof(s->strips[i].v4_codebook));
yading@10 390 memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook,
yading@10 391 sizeof(s->strips[i].v1_codebook));
yading@10 392 }
yading@10 393
yading@10 394 result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size);
yading@10 395
yading@10 396 if (result != 0)
yading@10 397 return result;
yading@10 398
yading@10 399 s->data += strip_size;
yading@10 400 y0 = s->strips[i].y2;
yading@10 401 }
yading@10 402 return 0;
yading@10 403 }
yading@10 404
yading@10 405 static av_cold int cinepak_decode_init(AVCodecContext *avctx)
yading@10 406 {
yading@10 407 CinepakContext *s = avctx->priv_data;
yading@10 408
yading@10 409 s->avctx = avctx;
yading@10 410 s->width = (avctx->width + 3) & ~3;
yading@10 411 s->height = (avctx->height + 3) & ~3;
yading@10 412
yading@10 413 s->sega_film_skip_bytes = -1; /* uninitialized state */
yading@10 414
yading@10 415 // check for paletted data
yading@10 416 if (avctx->bits_per_coded_sample != 8) {
yading@10 417 s->palette_video = 0;
yading@10 418 avctx->pix_fmt = AV_PIX_FMT_RGB24;
yading@10 419 } else {
yading@10 420 s->palette_video = 1;
yading@10 421 avctx->pix_fmt = AV_PIX_FMT_PAL8;
yading@10 422 }
yading@10 423
yading@10 424 s->frame = av_frame_alloc();
yading@10 425 if (!s->frame)
yading@10 426 return AVERROR(ENOMEM);
yading@10 427
yading@10 428 return 0;
yading@10 429 }
yading@10 430
yading@10 431 static int cinepak_decode_frame(AVCodecContext *avctx,
yading@10 432 void *data, int *got_frame,
yading@10 433 AVPacket *avpkt)
yading@10 434 {
yading@10 435 const uint8_t *buf = avpkt->data;
yading@10 436 int ret = 0, buf_size = avpkt->size;
yading@10 437 CinepakContext *s = avctx->priv_data;
yading@10 438
yading@10 439 s->data = buf;
yading@10 440 s->size = buf_size;
yading@10 441
yading@10 442 if ((ret = ff_reget_buffer(avctx, s->frame)) < 0)
yading@10 443 return ret;
yading@10 444
yading@10 445 if (s->palette_video) {
yading@10 446 const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL);
yading@10 447 if (pal) {
yading@10 448 s->frame->palette_has_changed = 1;
yading@10 449 memcpy(s->pal, pal, AVPALETTE_SIZE);
yading@10 450 }
yading@10 451 }
yading@10 452
yading@10 453 if ((ret = cinepak_decode(s)) < 0) {
yading@10 454 av_log(avctx, AV_LOG_ERROR, "cinepak_decode failed\n");
yading@10 455 }
yading@10 456
yading@10 457 if (s->palette_video)
yading@10 458 memcpy (s->frame->data[1], s->pal, AVPALETTE_SIZE);
yading@10 459
yading@10 460 if ((ret = av_frame_ref(data, s->frame)) < 0)
yading@10 461 return ret;
yading@10 462
yading@10 463 *got_frame = 1;
yading@10 464
yading@10 465 /* report that the buffer was completely consumed */
yading@10 466 return buf_size;
yading@10 467 }
yading@10 468
yading@10 469 static av_cold int cinepak_decode_end(AVCodecContext *avctx)
yading@10 470 {
yading@10 471 CinepakContext *s = avctx->priv_data;
yading@10 472
yading@10 473 av_frame_free(&s->frame);
yading@10 474
yading@10 475 return 0;
yading@10 476 }
yading@10 477
yading@10 478 AVCodec ff_cinepak_decoder = {
yading@10 479 .name = "cinepak",
yading@10 480 .type = AVMEDIA_TYPE_VIDEO,
yading@10 481 .id = AV_CODEC_ID_CINEPAK,
yading@10 482 .priv_data_size = sizeof(CinepakContext),
yading@10 483 .init = cinepak_decode_init,
yading@10 484 .close = cinepak_decode_end,
yading@10 485 .decode = cinepak_decode_frame,
yading@10 486 .capabilities = CODEC_CAP_DR1,
yading@10 487 .long_name = NULL_IF_CONFIG_SMALL("Cinepak"),
yading@10 488 };