• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavcodec/cinepak.c

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