cinepak.c 15.8 KB
Newer Older
1 2
/*
 * Cinepak Video Decoder
3
 * Copyright (C) 2003 The FFmpeg project
4
 *
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

#define MAX_STRIPS      32

50
typedef struct cvid_strip {
51 52 53
    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
    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
/* to get the correct picture for not-multiple-of-4 cases let us fill each
 * block from the bottom up, thus possibly overwriting the bottommost line
160 161
 * 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
static int cinepak_predecode_check (CinepakContext *s)
319
{
320
    int           num_strips;
321
    int           encoded_buf_size;
322

323
    num_strips  = AV_RB16 (&s->data[8]);
324
    encoded_buf_size = AV_RB24(&s->data[1]);
325 326 327

    /* if this is the first frame, check for deviant Sega FILM data */
    if (s->sega_film_skip_bytes == -1) {
328
        if (!encoded_buf_size) {
329
            avpriv_request_sample(s->avctx, "encoded_buf_size 0");
330
            return AVERROR_PATCHWELCOME;
331
        }
332
        if (encoded_buf_size != s->size && (s->size % encoded_buf_size) != 0) {
333 334 335 336
            /* 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
337
             * after the frame header. Else, assume 2 extra bytes. The container
338
             * size also cannot be a multiple of the encoded size. */
339 340
            if (s->size >= 16 &&
                (s->data[10] == 0xFE) &&
341 342 343 344 345 346 347 348 349 350 351 352
                (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;
    }

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    if (s->size < 10 + s->sega_film_skip_bytes + num_strips * 12)
        return AVERROR_INVALIDDATA;

    return 0;
}

static int cinepak_decode (CinepakContext *s)
{
    const uint8_t  *eod = (s->data + s->size);
    int           i, result, strip_size, frame_flags, num_strips;
    int           y0 = 0;

    frame_flags = s->data[0];
    num_strips  = AV_RB16 (&s->data[8]);

368
    s->data += 10 + s->sega_film_skip_bytes;
369

370
    num_strips = FFMIN(num_strips, MAX_STRIPS);
371

372
    s->frame->key_frame = 0;
373

374 375
    for (i=0; i < num_strips; i++) {
        if ((s->data + 12) > eod)
376
            return AVERROR_INVALIDDATA;
377

378
        s->strips[i].id = s->data[0];
379 380 381 382 383 384 385
/* 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]);
386

387
        if (s->strips[i].id == 0x10)
388
            s->frame->key_frame = 1;
389

390
        strip_size = AV_RB24 (&s->data[1]) - 12;
391
        if (strip_size < 0)
392
            return AVERROR_INVALIDDATA;
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
        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;
}

414
static av_cold int cinepak_decode_init(AVCodecContext *avctx)
415
{
416
    CinepakContext *s = avctx->priv_data;
417 418

    s->avctx = avctx;
419 420
    s->width = (avctx->width + 3) & ~3;
    s->height = (avctx->height + 3) & ~3;
Michael Niedermayer's avatar
Michael Niedermayer committed
421

422
    s->sega_film_skip_bytes = -1;  /* uninitialized state */
423

Roberto Togni's avatar
Roberto Togni committed
424
    // check for paletted data
425
    if (avctx->bits_per_coded_sample != 8) {
Roberto Togni's avatar
Roberto Togni committed
426
        s->palette_video = 0;
427
        avctx->pix_fmt = AV_PIX_FMT_RGB24;
Roberto Togni's avatar
Roberto Togni committed
428 429
    } else {
        s->palette_video = 1;
430
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
Roberto Togni's avatar
Roberto Togni committed
431
    }
432

433 434 435
    s->frame = av_frame_alloc();
    if (!s->frame)
        return AVERROR(ENOMEM);
436 437 438 439 440

    return 0;
}

static int cinepak_decode_frame(AVCodecContext *avctx,
441
                                void *data, int *got_frame,
442
                                AVPacket *avpkt)
443
{
444
    const uint8_t *buf = avpkt->data;
445
    int ret = 0, buf_size = avpkt->size;
446
    CinepakContext *s = avctx->priv_data;
447
    int num_strips;
448 449 450 451

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

452 453 454
    if (s->size < 10)
        return AVERROR_INVALIDDATA;

455 456 457 458 459 460
    num_strips = AV_RB16 (&s->data[8]);

    //Empty frame, do not waste time
    if (!num_strips && (!s->palette_video || !av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL)))
        return buf_size;

461 462 463 464 465
    if ((ret = cinepak_predecode_check(s)) < 0) {
        av_log(avctx, AV_LOG_ERROR, "cinepak_predecode_check failed\n");
        return ret;
    }

466
    if ((ret = ff_reget_buffer(avctx, s->frame)) < 0)
467
        return ret;
468

Roberto Togni's avatar
Roberto Togni committed
469
    if (s->palette_video) {
470 471 472
        int size;
        const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, &size);
        if (pal && size == AVPALETTE_SIZE) {
473
            s->frame->palette_has_changed = 1;
474
            memcpy(s->pal, pal, AVPALETTE_SIZE);
475 476
        } else if (pal) {
            av_log(avctx, AV_LOG_ERROR, "Palette size %d is wrong\n", size);
477
        }
Roberto Togni's avatar
Roberto Togni committed
478 479
    }

480 481 482
    if ((ret = cinepak_decode(s)) < 0) {
        av_log(avctx, AV_LOG_ERROR, "cinepak_decode failed\n");
    }
483 484

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

487
    if ((ret = av_frame_ref(data, s->frame)) < 0)
488
        return ret;
489

490
    *got_frame = 1;
491 492 493 494 495

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

496
static av_cold int cinepak_decode_end(AVCodecContext *avctx)
497
{
498
    CinepakContext *s = avctx->priv_data;
499

500
    av_frame_free(&s->frame);
501 502 503 504

    return 0;
}

505
AVCodec ff_cinepak_decoder = {
506
    .name           = "cinepak",
507
    .long_name      = NULL_IF_CONFIG_SMALL("Cinepak"),
508
    .type           = AVMEDIA_TYPE_VIDEO,
509
    .id             = AV_CODEC_ID_CINEPAK,
510 511 512 513
    .priv_data_size = sizeof(CinepakContext),
    .init           = cinepak_decode_init,
    .close          = cinepak_decode_end,
    .decode         = cinepak_decode_frame,
514
    .capabilities   = AV_CODEC_CAP_DR1,
515
};