Libav
|
00001 /* 00002 * Cinepak Video Decoder 00003 * Copyright (C) 2003 the ffmpeg project 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00032 #include <stdio.h> 00033 #include <stdlib.h> 00034 #include <string.h> 00035 00036 #include "libavutil/intreadwrite.h" 00037 #include "avcodec.h" 00038 00039 00040 typedef struct { 00041 uint8_t y0, y1, y2, y3; 00042 uint8_t u, v; 00043 } cvid_codebook; 00044 00045 #define MAX_STRIPS 32 00046 00047 typedef struct { 00048 uint16_t id; 00049 uint16_t x1, y1; 00050 uint16_t x2, y2; 00051 cvid_codebook v4_codebook[256]; 00052 cvid_codebook v1_codebook[256]; 00053 } cvid_strip; 00054 00055 typedef struct CinepakContext { 00056 00057 AVCodecContext *avctx; 00058 AVFrame frame; 00059 00060 const unsigned char *data; 00061 int size; 00062 00063 int width, height; 00064 00065 int palette_video; 00066 cvid_strip strips[MAX_STRIPS]; 00067 00068 int sega_film_skip_bytes; 00069 00070 } CinepakContext; 00071 00072 static void cinepak_decode_codebook (cvid_codebook *codebook, 00073 int chunk_id, int size, const uint8_t *data) 00074 { 00075 const uint8_t *eod = (data + size); 00076 uint32_t flag, mask; 00077 int i, n; 00078 00079 /* check if this chunk contains 4- or 6-element vectors */ 00080 n = (chunk_id & 0x04) ? 4 : 6; 00081 flag = 0; 00082 mask = 0; 00083 00084 for (i=0; i < 256; i++) { 00085 if ((chunk_id & 0x01) && !(mask >>= 1)) { 00086 if ((data + 4) > eod) 00087 break; 00088 00089 flag = AV_RB32 (data); 00090 data += 4; 00091 mask = 0x80000000; 00092 } 00093 00094 if (!(chunk_id & 0x01) || (flag & mask)) { 00095 if ((data + n) > eod) 00096 break; 00097 00098 if (n == 6) { 00099 codebook[i].y0 = *data++; 00100 codebook[i].y1 = *data++; 00101 codebook[i].y2 = *data++; 00102 codebook[i].y3 = *data++; 00103 codebook[i].u = 128 + *data++; 00104 codebook[i].v = 128 + *data++; 00105 } else { 00106 /* this codebook type indicates either greyscale or 00107 * palettized video; if palettized, U & V components will 00108 * not be used so it is safe to set them to 128 for the 00109 * benefit of greyscale rendering in YUV420P */ 00110 codebook[i].y0 = *data++; 00111 codebook[i].y1 = *data++; 00112 codebook[i].y2 = *data++; 00113 codebook[i].y3 = *data++; 00114 codebook[i].u = 128; 00115 codebook[i].v = 128; 00116 } 00117 } 00118 } 00119 } 00120 00121 static int cinepak_decode_vectors (CinepakContext *s, cvid_strip *strip, 00122 int chunk_id, int size, const uint8_t *data) 00123 { 00124 const uint8_t *eod = (data + size); 00125 uint32_t flag, mask; 00126 cvid_codebook *codebook; 00127 unsigned int x, y; 00128 uint32_t iy[4]; 00129 uint32_t iu[2]; 00130 uint32_t iv[2]; 00131 00132 flag = 0; 00133 mask = 0; 00134 00135 for (y=strip->y1; y < strip->y2; y+=4) { 00136 00137 iy[0] = strip->x1 + (y * s->frame.linesize[0]); 00138 iy[1] = iy[0] + s->frame.linesize[0]; 00139 iy[2] = iy[1] + s->frame.linesize[0]; 00140 iy[3] = iy[2] + s->frame.linesize[0]; 00141 iu[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[1]); 00142 iu[1] = iu[0] + s->frame.linesize[1]; 00143 iv[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[2]); 00144 iv[1] = iv[0] + s->frame.linesize[2]; 00145 00146 for (x=strip->x1; x < strip->x2; x+=4) { 00147 if ((chunk_id & 0x01) && !(mask >>= 1)) { 00148 if ((data + 4) > eod) 00149 return -1; 00150 00151 flag = AV_RB32 (data); 00152 data += 4; 00153 mask = 0x80000000; 00154 } 00155 00156 if (!(chunk_id & 0x01) || (flag & mask)) { 00157 if (!(chunk_id & 0x02) && !(mask >>= 1)) { 00158 if ((data + 4) > eod) 00159 return -1; 00160 00161 flag = AV_RB32 (data); 00162 data += 4; 00163 mask = 0x80000000; 00164 } 00165 00166 if ((chunk_id & 0x02) || (~flag & mask)) { 00167 if (data >= eod) 00168 return -1; 00169 00170 codebook = &strip->v1_codebook[*data++]; 00171 s->frame.data[0][iy[0] + 0] = codebook->y0; 00172 s->frame.data[0][iy[0] + 1] = codebook->y0; 00173 s->frame.data[0][iy[1] + 0] = codebook->y0; 00174 s->frame.data[0][iy[1] + 1] = codebook->y0; 00175 if (!s->palette_video) { 00176 s->frame.data[1][iu[0]] = codebook->u; 00177 s->frame.data[2][iv[0]] = codebook->v; 00178 } 00179 00180 s->frame.data[0][iy[0] + 2] = codebook->y1; 00181 s->frame.data[0][iy[0] + 3] = codebook->y1; 00182 s->frame.data[0][iy[1] + 2] = codebook->y1; 00183 s->frame.data[0][iy[1] + 3] = codebook->y1; 00184 if (!s->palette_video) { 00185 s->frame.data[1][iu[0] + 1] = codebook->u; 00186 s->frame.data[2][iv[0] + 1] = codebook->v; 00187 } 00188 00189 s->frame.data[0][iy[2] + 0] = codebook->y2; 00190 s->frame.data[0][iy[2] + 1] = codebook->y2; 00191 s->frame.data[0][iy[3] + 0] = codebook->y2; 00192 s->frame.data[0][iy[3] + 1] = codebook->y2; 00193 if (!s->palette_video) { 00194 s->frame.data[1][iu[1]] = codebook->u; 00195 s->frame.data[2][iv[1]] = codebook->v; 00196 } 00197 00198 s->frame.data[0][iy[2] + 2] = codebook->y3; 00199 s->frame.data[0][iy[2] + 3] = codebook->y3; 00200 s->frame.data[0][iy[3] + 2] = codebook->y3; 00201 s->frame.data[0][iy[3] + 3] = codebook->y3; 00202 if (!s->palette_video) { 00203 s->frame.data[1][iu[1] + 1] = codebook->u; 00204 s->frame.data[2][iv[1] + 1] = codebook->v; 00205 } 00206 00207 } else if (flag & mask) { 00208 if ((data + 4) > eod) 00209 return -1; 00210 00211 codebook = &strip->v4_codebook[*data++]; 00212 s->frame.data[0][iy[0] + 0] = codebook->y0; 00213 s->frame.data[0][iy[0] + 1] = codebook->y1; 00214 s->frame.data[0][iy[1] + 0] = codebook->y2; 00215 s->frame.data[0][iy[1] + 1] = codebook->y3; 00216 if (!s->palette_video) { 00217 s->frame.data[1][iu[0]] = codebook->u; 00218 s->frame.data[2][iv[0]] = codebook->v; 00219 } 00220 00221 codebook = &strip->v4_codebook[*data++]; 00222 s->frame.data[0][iy[0] + 2] = codebook->y0; 00223 s->frame.data[0][iy[0] + 3] = codebook->y1; 00224 s->frame.data[0][iy[1] + 2] = codebook->y2; 00225 s->frame.data[0][iy[1] + 3] = codebook->y3; 00226 if (!s->palette_video) { 00227 s->frame.data[1][iu[0] + 1] = codebook->u; 00228 s->frame.data[2][iv[0] + 1] = codebook->v; 00229 } 00230 00231 codebook = &strip->v4_codebook[*data++]; 00232 s->frame.data[0][iy[2] + 0] = codebook->y0; 00233 s->frame.data[0][iy[2] + 1] = codebook->y1; 00234 s->frame.data[0][iy[3] + 0] = codebook->y2; 00235 s->frame.data[0][iy[3] + 1] = codebook->y3; 00236 if (!s->palette_video) { 00237 s->frame.data[1][iu[1]] = codebook->u; 00238 s->frame.data[2][iv[1]] = codebook->v; 00239 } 00240 00241 codebook = &strip->v4_codebook[*data++]; 00242 s->frame.data[0][iy[2] + 2] = codebook->y0; 00243 s->frame.data[0][iy[2] + 3] = codebook->y1; 00244 s->frame.data[0][iy[3] + 2] = codebook->y2; 00245 s->frame.data[0][iy[3] + 3] = codebook->y3; 00246 if (!s->palette_video) { 00247 s->frame.data[1][iu[1] + 1] = codebook->u; 00248 s->frame.data[2][iv[1] + 1] = codebook->v; 00249 } 00250 00251 } 00252 } 00253 00254 iy[0] += 4; iy[1] += 4; 00255 iy[2] += 4; iy[3] += 4; 00256 iu[0] += 2; iu[1] += 2; 00257 iv[0] += 2; iv[1] += 2; 00258 } 00259 } 00260 00261 return 0; 00262 } 00263 00264 static int cinepak_decode_strip (CinepakContext *s, 00265 cvid_strip *strip, const uint8_t *data, int size) 00266 { 00267 const uint8_t *eod = (data + size); 00268 int chunk_id, chunk_size; 00269 00270 /* coordinate sanity checks */ 00271 if (strip->x1 >= s->width || strip->x2 > s->width || 00272 strip->y1 >= s->height || strip->y2 > s->height || 00273 strip->x1 >= strip->x2 || strip->y1 >= strip->y2) 00274 return -1; 00275 00276 while ((data + 4) <= eod) { 00277 chunk_id = data[0]; 00278 chunk_size = AV_RB24 (&data[1]) - 4; 00279 if(chunk_size < 0) 00280 return -1; 00281 00282 data += 4; 00283 chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size; 00284 00285 switch (chunk_id) { 00286 00287 case 0x20: 00288 case 0x21: 00289 case 0x24: 00290 case 0x25: 00291 cinepak_decode_codebook (strip->v4_codebook, chunk_id, 00292 chunk_size, data); 00293 break; 00294 00295 case 0x22: 00296 case 0x23: 00297 case 0x26: 00298 case 0x27: 00299 cinepak_decode_codebook (strip->v1_codebook, chunk_id, 00300 chunk_size, data); 00301 break; 00302 00303 case 0x30: 00304 case 0x31: 00305 case 0x32: 00306 return cinepak_decode_vectors (s, strip, chunk_id, 00307 chunk_size, data); 00308 } 00309 00310 data += chunk_size; 00311 } 00312 00313 return -1; 00314 } 00315 00316 static int cinepak_decode (CinepakContext *s) 00317 { 00318 const uint8_t *eod = (s->data + s->size); 00319 int i, result, strip_size, frame_flags, num_strips; 00320 int y0 = 0; 00321 int encoded_buf_size; 00322 00323 if (s->size < 10) 00324 return -1; 00325 00326 frame_flags = s->data[0]; 00327 num_strips = AV_RB16 (&s->data[8]); 00328 encoded_buf_size = ((s->data[1] << 16) | AV_RB16 (&s->data[2])); 00329 00330 /* if this is the first frame, check for deviant Sega FILM data */ 00331 if (s->sega_film_skip_bytes == -1) { 00332 if (encoded_buf_size != s->size) { 00333 /* If the encoded frame size differs from the frame size as indicated 00334 * by the container file, this data likely comes from a Sega FILM/CPK file. 00335 * If the frame header is followed by the bytes FE 00 00 06 00 00 then 00336 * this is probably one of the two known files that have 6 extra bytes 00337 * after the frame header. Else, assume 2 extra bytes. */ 00338 if ((s->data[10] == 0xFE) && 00339 (s->data[11] == 0x00) && 00340 (s->data[12] == 0x00) && 00341 (s->data[13] == 0x06) && 00342 (s->data[14] == 0x00) && 00343 (s->data[15] == 0x00)) 00344 s->sega_film_skip_bytes = 6; 00345 else 00346 s->sega_film_skip_bytes = 2; 00347 } else 00348 s->sega_film_skip_bytes = 0; 00349 } 00350 00351 s->data += 10 + s->sega_film_skip_bytes; 00352 00353 if (num_strips > MAX_STRIPS) 00354 num_strips = MAX_STRIPS; 00355 00356 for (i=0; i < num_strips; i++) { 00357 if ((s->data + 12) > eod) 00358 return -1; 00359 00360 s->strips[i].id = s->data[0]; 00361 s->strips[i].y1 = y0; 00362 s->strips[i].x1 = 0; 00363 s->strips[i].y2 = y0 + AV_RB16 (&s->data[8]); 00364 s->strips[i].x2 = s->avctx->width; 00365 00366 strip_size = AV_RB24 (&s->data[1]) - 12; 00367 s->data += 12; 00368 strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size; 00369 00370 if ((i > 0) && !(frame_flags & 0x01)) { 00371 memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook, 00372 sizeof(s->strips[i].v4_codebook)); 00373 memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook, 00374 sizeof(s->strips[i].v1_codebook)); 00375 } 00376 00377 result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size); 00378 00379 if (result != 0) 00380 return result; 00381 00382 s->data += strip_size; 00383 y0 = s->strips[i].y2; 00384 } 00385 return 0; 00386 } 00387 00388 static av_cold int cinepak_decode_init(AVCodecContext *avctx) 00389 { 00390 CinepakContext *s = avctx->priv_data; 00391 00392 s->avctx = avctx; 00393 s->width = (avctx->width + 3) & ~3; 00394 s->height = (avctx->height + 3) & ~3; 00395 s->sega_film_skip_bytes = -1; /* uninitialized state */ 00396 00397 // check for paletted data 00398 if ((avctx->palctrl == NULL) || (avctx->bits_per_coded_sample == 40)) { 00399 s->palette_video = 0; 00400 avctx->pix_fmt = PIX_FMT_YUV420P; 00401 } else { 00402 s->palette_video = 1; 00403 avctx->pix_fmt = PIX_FMT_PAL8; 00404 } 00405 00406 s->frame.data[0] = NULL; 00407 00408 return 0; 00409 } 00410 00411 static int cinepak_decode_frame(AVCodecContext *avctx, 00412 void *data, int *data_size, 00413 AVPacket *avpkt) 00414 { 00415 const uint8_t *buf = avpkt->data; 00416 int buf_size = avpkt->size; 00417 CinepakContext *s = avctx->priv_data; 00418 00419 s->data = buf; 00420 s->size = buf_size; 00421 00422 s->frame.reference = 1; 00423 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | 00424 FF_BUFFER_HINTS_REUSABLE; 00425 if (avctx->reget_buffer(avctx, &s->frame)) { 00426 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 00427 return -1; 00428 } 00429 00430 cinepak_decode(s); 00431 00432 if (s->palette_video) { 00433 memcpy (s->frame.data[1], avctx->palctrl->palette, AVPALETTE_SIZE); 00434 if (avctx->palctrl->palette_changed) { 00435 s->frame.palette_has_changed = 1; 00436 avctx->palctrl->palette_changed = 0; 00437 } else 00438 s->frame.palette_has_changed = 0; 00439 } 00440 00441 *data_size = sizeof(AVFrame); 00442 *(AVFrame*)data = s->frame; 00443 00444 /* report that the buffer was completely consumed */ 00445 return buf_size; 00446 } 00447 00448 static av_cold int cinepak_decode_end(AVCodecContext *avctx) 00449 { 00450 CinepakContext *s = avctx->priv_data; 00451 00452 if (s->frame.data[0]) 00453 avctx->release_buffer(avctx, &s->frame); 00454 00455 return 0; 00456 } 00457 00458 AVCodec cinepak_decoder = { 00459 "cinepak", 00460 AVMEDIA_TYPE_VIDEO, 00461 CODEC_ID_CINEPAK, 00462 sizeof(CinepakContext), 00463 cinepak_decode_init, 00464 NULL, 00465 cinepak_decode_end, 00466 cinepak_decode_frame, 00467 CODEC_CAP_DR1, 00468 .long_name = NULL_IF_CONFIG_SMALL("Cinepak"), 00469 };