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

Generated on Sat Feb 16 2013 09:23:11 for ffmpeg by  doxygen 1.7.1