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