cinepak.c 15.1 KB
Newer Older
1 2 3 4
/*
 * Cinepak Video Decoder
 * Copyright (C) 2003 the ffmpeg project
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * 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
 * FFmpeg is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; 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 26 27
 * @author Ewald Snel <ewald@rambo.its.tudelft.nl>
 *
 * @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
 *
 * Cinepak colorspace support (c) 2013 Rl, Aetey Global Technologies AB
 * @author Cinepak colorspace, Rl, Aetey Global Technologies AB
34 35 36 37 38 39
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

40
#include "libavutil/common.h"
41
#include "libavutil/intreadwrite.h"
42
#include "avcodec.h"
43
#include "internal.h"
44 45


46
typedef uint8_t cvid_codebook[12];
47 48 49 50 51 52 53

#define MAX_STRIPS      32

typedef struct {
    uint16_t          id;
    uint16_t          x1, y1;
    uint16_t          x2, y2;
54 55 56
    cvid_codebook     v4_codebook[256];
    cvid_codebook     v1_codebook[256];
} cvid_strip;
57 58 59 60

typedef struct CinepakContext {

    AVCodecContext *avctx;
61
    AVFrame *frame;
62

Michael Niedermayer's avatar
Michael Niedermayer committed
63
    const unsigned char *data;
64 65
    int size;

66 67
    int width, height;

68
    int palette_video;
69
    cvid_strip strips[MAX_STRIPS];
70

71 72
    int sega_film_skip_bytes;

73
    uint32_t pal[256];
74 75
} CinepakContext;

76
static void cinepak_decode_codebook (cvid_codebook *codebook,
Michael Niedermayer's avatar
Michael Niedermayer committed
77
                                     int chunk_id, int size, const uint8_t *data)
78
{
Michael Niedermayer's avatar
Michael Niedermayer committed
79
    const uint8_t *eod = (data + size);
80 81
    uint32_t flag, mask;
    int      i, n;
82
    uint8_t *p;
83 84

    /* check if this chunk contains 4- or 6-element vectors */
85
    n    = (chunk_id & 0x04) ? 4 : 6;
86 87 88
    flag = 0;
    mask = 0;

89
    p = codebook[0];
90
    for (i=0; i < 256; i++) {
91
        if ((chunk_id & 0x01) && !(mask >>= 1)) {
92 93 94
            if ((data + 4) > eod)
                break;

95
            flag  = AV_RB32 (data);
96 97 98 99
            data += 4;
            mask  = 0x80000000;
        }

100
        if (!(chunk_id & 0x01) || (flag & mask)) {
101 102
            int k, kk;

103 104 105
            if ((data + n) > eod)
                break;

106 107 108 109 110
            for (k = 0; k < 4; ++k) {
                int r = *data++;
                for (kk = 0; kk < 3; ++kk)
                    *p++ = r;
            }
111
            if (n == 6) {
112 113 114 115 116 117 118 119 120 121 122 123 124
                int r, g, b, u, v;
                u = *(int8_t *)data++;
                v = *(int8_t *)data++;
                p -= 12;
                for(k=0; k<4; ++k) {
                    r = *p++ + v*2;
                    g = *p++ - (u/2) - v;
                    b = *p   + u*2;
                    p -= 2;
                    *p++ = av_clip_uint8(r);
                    *p++ = av_clip_uint8(g);
                    *p++ = av_clip_uint8(b);
                }
125
            }
126 127
        } else {
            p += 12;
128 129 130 131
        }
    }
}

132
static int cinepak_decode_vectors (CinepakContext *s, cvid_strip *strip,
Michael Niedermayer's avatar
Michael Niedermayer committed
133
                                   int chunk_id, int size, const uint8_t *data)
134
{
Michael Niedermayer's avatar
Michael Niedermayer committed
135
    const uint8_t   *eod = (data + size);
136
    uint32_t         flag, mask;
137
    uint8_t         *cb0, *cb1, *cb2, *cb3;
138
    unsigned int     x, y;
139
    char            *ip0, *ip1, *ip2, *ip3;
140 141 142 143 144 145

    flag = 0;
    mask = 0;

    for (y=strip->y1; y < strip->y2; y+=4) {

146
/* take care of y dimension not being multiple of 4, such streams exist */
147 148
        ip0 = ip1 = ip2 = ip3 = s->frame->data[0] +
          (s->palette_video?strip->x1:strip->x1*3) + (y * s->frame->linesize[0]);
149
        if(s->avctx->height - y > 1) {
150
            ip1 = ip0 + s->frame->linesize[0];
151
            if(s->avctx->height - y > 2) {
152
                ip2 = ip1 + s->frame->linesize[0];
153
                if(s->avctx->height - y > 3) {
154
                    ip3 = ip2 + s->frame->linesize[0];
155 156 157 158 159 160 161
                }
            }
        }
/* to get the correct picture for not-multiple-of-4 cases let us fill
 * each block from the bottom up, thus possibly overwriting the top line
 * more than once but ending with the correct data in place
 * (instead of in-loop checking) */
162 163

        for (x=strip->x1; x < strip->x2; x+=4) {
164
            if ((chunk_id & 0x01) && !(mask >>= 1)) {
165
                if ((data + 4) > eod)
166
                    return AVERROR_INVALIDDATA;
167

168
                flag  = AV_RB32 (data);
169 170 171 172
                data += 4;
                mask  = 0x80000000;
            }

173 174
            if (!(chunk_id & 0x01) || (flag & mask)) {
                if (!(chunk_id & 0x02) && !(mask >>= 1)) {
175
                    if ((data + 4) > eod)
176
                        return AVERROR_INVALIDDATA;
177

178
                    flag  = AV_RB32 (data);
179 180 181 182
                    data += 4;
                    mask  = 0x80000000;
                }

183
                if ((chunk_id & 0x02) || (~flag & mask)) {
184
                    uint8_t *p;
185
                    if (data >= eod)
186
                        return AVERROR_INVALIDDATA;
187

Michael Niedermayer's avatar
Michael Niedermayer committed
188
                    p = strip->v1_codebook[*data++];
189
                    if (s->palette_video) {
Michael Niedermayer's avatar
Michael Niedermayer committed
190 191 192 193
                        ip3[0] = ip3[1] = ip2[0] = ip2[1] = p[6];
                        ip3[2] = ip3[3] = ip2[2] = ip2[3] = p[9];
                        ip1[0] = ip1[1] = ip0[0] = ip0[1] = p[0];
                        ip1[2] = ip1[3] = ip0[2] = ip0[3] = p[3];
194
                    } else {
Michael Niedermayer's avatar
Michael Niedermayer committed
195
                        p += 6;
196 197 198 199 200 201 202 203 204 205 206
                        memcpy(ip3 + 0, p, 3); memcpy(ip3 + 3, p, 3);
                        memcpy(ip2 + 0, p, 3); memcpy(ip2 + 3, p, 3);
                        p += 3; /* ... + 9 */
                        memcpy(ip3 + 6, p, 3); memcpy(ip3 + 9, p, 3);
                        memcpy(ip2 + 6, p, 3); memcpy(ip2 + 9, p, 3);
                        p -= 9; /* ... + 0 */
                        memcpy(ip1 + 0, p, 3); memcpy(ip1 + 3, p, 3);
                        memcpy(ip0 + 0, p, 3); memcpy(ip0 + 3, p, 3);
                        p += 3; /* ... + 3 */
                        memcpy(ip1 + 6, p, 3); memcpy(ip1 + 9, p, 3);
                        memcpy(ip0 + 6, p, 3); memcpy(ip0 + 9, p, 3);
207 208 209 210
                    }

                } else if (flag & mask) {
                    if ((data + 4) > eod)
211
                        return AVERROR_INVALIDDATA;
212

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
                    cb0 = strip->v4_codebook[*data++];
                    cb1 = strip->v4_codebook[*data++];
                    cb2 = strip->v4_codebook[*data++];
                    cb3 = strip->v4_codebook[*data++];
                    if (s->palette_video) {
                        uint8_t *p;
                        p = ip3;
                        *p++ = cb2[6];
                        *p++ = cb2[9];
                        *p++ = cb3[6];
                        *p   = cb3[9];
                        p = ip2;
                        *p++ = cb2[0];
                        *p++ = cb2[3];
                        *p++ = cb3[0];
                        *p   = cb3[3];
                        p = ip1;
                        *p++ = cb0[6];
                        *p++ = cb0[9];
                        *p++ = cb1[6];
                        *p   = cb1[9];
                        p = ip0;
                        *p++ = cb0[0];
                        *p++ = cb0[3];
                        *p++ = cb1[0];
                        *p   = cb1[3];
                    } else {
                        memcpy(ip3 + 0, cb2 + 6, 6);
                        memcpy(ip3 + 6, cb3 + 6, 6);
                        memcpy(ip2 + 0, cb2 + 0, 6);
                        memcpy(ip2 + 6, cb3 + 0, 6);
                        memcpy(ip1 + 0, cb0 + 6, 6);
                        memcpy(ip1 + 6, cb1 + 6, 6);
                        memcpy(ip0 + 0, cb0 + 0, 6);
                        memcpy(ip0 + 6, cb1 + 0, 6);
248 249 250 251 252
                    }

                }
            }

253 254 255 256 257 258 259
            if (s->palette_video) {
                ip0 += 4;  ip1 += 4;
                ip2 += 4;  ip3 += 4;
            } else {
                ip0 += 12;  ip1 += 12;
                ip2 += 12;  ip3 += 12;
            }
260 261 262 263 264 265 266
        }
    }

    return 0;
}

static int cinepak_decode_strip (CinepakContext *s,
267
                                 cvid_strip *strip, const uint8_t *data, int size)
268
{
Michael Niedermayer's avatar
Michael Niedermayer committed
269
    const uint8_t *eod = (data + size);
270 271 272
    int      chunk_id, chunk_size;

    /* coordinate sanity checks */
273 274
    if (strip->x2 > s->width   ||
        strip->y2 > s->height  ||
275
        strip->x1 >= strip->x2 || strip->y1 >= strip->y2)
276
        return AVERROR_INVALIDDATA;
277 278

    while ((data + 4) <= eod) {
279 280
        chunk_id   = data[0];
        chunk_size = AV_RB24 (&data[1]) - 4;
281
        if(chunk_size < 0)
282
            return AVERROR_INVALIDDATA;
283

284 285 286 287 288
        data      += 4;
        chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size;

        switch (chunk_id) {

289 290 291 292
        case 0x20:
        case 0x21:
        case 0x24:
        case 0x25:
293
            cinepak_decode_codebook (strip->v4_codebook, chunk_id,
294 295 296
                chunk_size, data);
            break;

297 298 299 300
        case 0x22:
        case 0x23:
        case 0x26:
        case 0x27:
301
            cinepak_decode_codebook (strip->v1_codebook, chunk_id,
302 303 304
                chunk_size, data);
            break;

305 306 307
        case 0x30:
        case 0x31:
        case 0x32:
308
            return cinepak_decode_vectors (s, strip, chunk_id,
309 310 311 312 313 314
                chunk_size, data);
        }

        data += chunk_size;
    }

315
    return AVERROR_INVALIDDATA;
316 317 318 319
}

static int cinepak_decode (CinepakContext *s)
{
Michael Niedermayer's avatar
Michael Niedermayer committed
320
    const uint8_t  *eod = (s->data + s->size);
321 322
    int           i, result, strip_size, frame_flags, num_strips;
    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 333

    /* if this is the first frame, check for deviant Sega FILM data */
    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_PATCHWELCOME;
337
        }
338
        if (encoded_buf_size != s->size && (s->size % encoded_buf_size) != 0) {
339 340 341 342
            /* If the encoded frame size differs from the frame size as indicated
             * by the container file, this data likely comes from a Sega FILM/CPK file.
             * If the frame header is followed by the bytes FE 00 00 06 00 00 then
             * 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 346
            if (s->size >= 16 &&
                (s->data[10] == 0xFE) &&
347 348 349 350 351 352 353 354 355 356 357 358 359
                (s->data[11] == 0x00) &&
                (s->data[12] == 0x00) &&
                (s->data[13] == 0x06) &&
                (s->data[14] == 0x00) &&
                (s->data[15] == 0x00))
                s->sega_film_skip_bytes = 6;
            else
                s->sega_film_skip_bytes = 2;
        } else
            s->sega_film_skip_bytes = 0;
    }

    s->data += 10 + s->sega_film_skip_bytes;
360

361
    num_strips = FFMIN(num_strips, MAX_STRIPS);
362

363
    s->frame->key_frame = 0;
364

365 366
    for (i=0; i < num_strips; i++) {
        if ((s->data + 12) > eod)
367
            return AVERROR_INVALIDDATA;
368

369
        s->strips[i].id = s->data[0];
370 371 372 373 374 375 376
/* zero y1 means "relative to the previous stripe" */
        if (!(s->strips[i].y1 = AV_RB16 (&s->data[4])))
            s->strips[i].y2 = (s->strips[i].y1 = y0) + AV_RB16 (&s->data[8]);
        else
            s->strips[i].y2 = AV_RB16 (&s->data[8]);
        s->strips[i].x1 = AV_RB16 (&s->data[6]);
        s->strips[i].x2 = AV_RB16 (&s->data[10]);
377

378
        if (s->strips[i].id == 0x10)
379
            s->frame->key_frame = 1;
380

381
        strip_size = AV_RB24 (&s->data[1]) - 12;
382
        if (strip_size < 0)
383
            return AVERROR_INVALIDDATA;
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
        s->data   += 12;
        strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;

        if ((i > 0) && !(frame_flags & 0x01)) {
            memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook,
                sizeof(s->strips[i].v4_codebook));
            memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook,
                sizeof(s->strips[i].v1_codebook));
        }

        result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size);

        if (result != 0)
            return result;

        s->data += strip_size;
        y0    = s->strips[i].y2;
    }
    return 0;
}

405
static av_cold int cinepak_decode_init(AVCodecContext *avctx)
406
{
407
    CinepakContext *s = avctx->priv_data;
408 409

    s->avctx = avctx;
410 411
    s->width = (avctx->width + 3) & ~3;
    s->height = (avctx->height + 3) & ~3;
Michael Niedermayer's avatar
Michael Niedermayer committed
412

413
    s->sega_film_skip_bytes = -1;  /* uninitialized state */
414

Roberto Togni's avatar
Roberto Togni committed
415
    // check for paletted data
416
    if (avctx->bits_per_coded_sample != 8) {
Roberto Togni's avatar
Roberto Togni committed
417
        s->palette_video = 0;
418
        avctx->pix_fmt = AV_PIX_FMT_RGB24;
Roberto Togni's avatar
Roberto Togni committed
419 420
    } else {
        s->palette_video = 1;
421
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
Roberto Togni's avatar
Roberto Togni committed
422
    }
423

424 425 426
    s->frame = av_frame_alloc();
    if (!s->frame)
        return AVERROR(ENOMEM);
427 428 429 430 431

    return 0;
}

static int cinepak_decode_frame(AVCodecContext *avctx,
432
                                void *data, int *got_frame,
433
                                AVPacket *avpkt)
434
{
435
    const uint8_t *buf = avpkt->data;
436
    int ret = 0, buf_size = avpkt->size;
437
    CinepakContext *s = avctx->priv_data;
438 439 440 441

    s->data = buf;
    s->size = buf_size;

442
    if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) {
443
        av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
444
        return ret;
445 446
    }

Roberto Togni's avatar
Roberto Togni committed
447
    if (s->palette_video) {
448 449
        const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL);
        if (pal) {
450
            s->frame->palette_has_changed = 1;
451 452
            memcpy(s->pal, pal, AVPALETTE_SIZE);
        }
Roberto Togni's avatar
Roberto Togni committed
453 454
    }

455 456 457
    if ((ret = cinepak_decode(s)) < 0) {
        av_log(avctx, AV_LOG_ERROR, "cinepak_decode failed\n");
    }
458 459

    if (s->palette_video)
460
        memcpy (s->frame->data[1], s->pal, AVPALETTE_SIZE);
461

462
    if ((ret = av_frame_ref(data, s->frame)) < 0)
463
        return ret;
464

465
    *got_frame = 1;
466 467 468 469 470

    /* report that the buffer was completely consumed */
    return buf_size;
}

471
static av_cold int cinepak_decode_end(AVCodecContext *avctx)
472
{
473
    CinepakContext *s = avctx->priv_data;
474

475
    av_frame_free(&s->frame);
476 477 478 479

    return 0;
}

480
AVCodec ff_cinepak_decoder = {
481 482
    .name           = "cinepak",
    .type           = AVMEDIA_TYPE_VIDEO,
483
    .id             = AV_CODEC_ID_CINEPAK,
484 485 486 487 488
    .priv_data_size = sizeof(CinepakContext),
    .init           = cinepak_decode_init,
    .close          = cinepak_decode_end,
    .decode         = cinepak_decode_frame,
    .capabilities   = CODEC_CAP_DR1,
489
    .long_name      = NULL_IF_CONFIG_SMALL("Cinepak"),
490
};