• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • 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 Libav.
00006  *
00007  * Libav 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  * Libav 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 Libav; 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     for (i=0; i < num_strips; i++) {
00364         if ((s->data + 12) > eod)
00365             return AVERROR_INVALIDDATA;
00366 
00367         s->strips[i].id = s->data[0];
00368         s->strips[i].y1 = y0;
00369         s->strips[i].x1 = 0;
00370         s->strips[i].y2 = y0 + AV_RB16 (&s->data[8]);
00371         s->strips[i].x2 = s->avctx->width;
00372 
00373         strip_size = AV_RB24 (&s->data[1]) - 12;
00374         if (strip_size < 0)
00375             return AVERROR_INVALIDDATA;
00376         s->data   += 12;
00377         strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;
00378 
00379         if ((i > 0) && !(frame_flags & 0x01)) {
00380             memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook,
00381                 sizeof(s->strips[i].v4_codebook));
00382             memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook,
00383                 sizeof(s->strips[i].v1_codebook));
00384         }
00385 
00386         result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size);
00387 
00388         if (result != 0)
00389             return result;
00390 
00391         s->data += strip_size;
00392         y0    = s->strips[i].y2;
00393     }
00394     return 0;
00395 }
00396 
00397 static av_cold int cinepak_decode_init(AVCodecContext *avctx)
00398 {
00399     CinepakContext *s = avctx->priv_data;
00400 
00401     s->avctx = avctx;
00402     s->width = (avctx->width + 3) & ~3;
00403     s->height = (avctx->height + 3) & ~3;
00404     s->sega_film_skip_bytes = -1;  /* uninitialized state */
00405 
00406     // check for paletted data
00407     if (avctx->bits_per_coded_sample != 8) {
00408         s->palette_video = 0;
00409         avctx->pix_fmt = PIX_FMT_YUV420P;
00410     } else {
00411         s->palette_video = 1;
00412         avctx->pix_fmt = PIX_FMT_PAL8;
00413     }
00414 
00415     s->frame.data[0] = NULL;
00416 
00417     return 0;
00418 }
00419 
00420 static int cinepak_decode_frame(AVCodecContext *avctx,
00421                                 void *data, int *data_size,
00422                                 AVPacket *avpkt)
00423 {
00424     const uint8_t *buf = avpkt->data;
00425     int ret = 0, buf_size = avpkt->size;
00426     CinepakContext *s = avctx->priv_data;
00427 
00428     s->data = buf;
00429     s->size = buf_size;
00430 
00431     s->frame.reference = 1;
00432     s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
00433                             FF_BUFFER_HINTS_REUSABLE;
00434     if ((ret = avctx->reget_buffer(avctx, &s->frame))) {
00435         av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
00436         return ret;
00437     }
00438 
00439     if (s->palette_video) {
00440         const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL);
00441         if (pal) {
00442             s->frame.palette_has_changed = 1;
00443             memcpy(s->pal, pal, AVPALETTE_SIZE);
00444         }
00445     }
00446 
00447     cinepak_decode(s);
00448 
00449     if (s->palette_video)
00450         memcpy (s->frame.data[1], s->pal, AVPALETTE_SIZE);
00451 
00452     *data_size = sizeof(AVFrame);
00453     *(AVFrame*)data = s->frame;
00454 
00455     /* report that the buffer was completely consumed */
00456     return buf_size;
00457 }
00458 
00459 static av_cold int cinepak_decode_end(AVCodecContext *avctx)
00460 {
00461     CinepakContext *s = avctx->priv_data;
00462 
00463     if (s->frame.data[0])
00464         avctx->release_buffer(avctx, &s->frame);
00465 
00466     return 0;
00467 }
00468 
00469 AVCodec ff_cinepak_decoder = {
00470     .name           = "cinepak",
00471     .type           = AVMEDIA_TYPE_VIDEO,
00472     .id             = CODEC_ID_CINEPAK,
00473     .priv_data_size = sizeof(CinepakContext),
00474     .init           = cinepak_decode_init,
00475     .close          = cinepak_decode_end,
00476     .decode         = cinepak_decode_frame,
00477     .capabilities   = CODEC_CAP_DR1,
00478     .long_name = NULL_IF_CONFIG_SMALL("Cinepak"),
00479 };
Generated on Thu Jan 24 2013 17:08:50 for Libav by doxygen 1.7.1