Statistics
| Branch: | Revision:

ffmpeg / libavcodec / cinepak.c @ 2029f312

History | View | Annotate | Download (14.9 KB)

1
/*
2
 * Cinepak Video Decoder
3
 * Copyright (C) 2003 the ffmpeg project
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * 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
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * 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
/**
24
 * @file cinepak.c
25
 * Cinepak video decoder
26
 * by Ewald Snel <ewald@rambo.its.tudelft.nl>
27
 * For more information on the Cinepak algorithm, visit:
28
 *   http://www.csse.monash.edu.au/~timf/
29
 * 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
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37

    
38
#include "avcodec.h"
39
#include "dsputil.h"
40

    
41

    
42
typedef struct {
43
    uint8_t  y0, y1, y2, y3;
44
    uint8_t  u, v;
45
} cvid_codebook_t;
46

    
47
#define MAX_STRIPS      32
48

    
49
typedef struct {
50
    uint16_t          id;
51
    uint16_t          x1, y1;
52
    uint16_t          x2, y2;
53
    cvid_codebook_t   v4_codebook[256];
54
    cvid_codebook_t   v1_codebook[256];
55
} cvid_strip_t;
56

    
57
typedef struct CinepakContext {
58

    
59
    AVCodecContext *avctx;
60
    DSPContext dsp;
61
    AVFrame frame;
62

    
63
    unsigned char *data;
64
    int size;
65

    
66
    int width, height;
67

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

    
71
    int sega_film_skip_bytes;
72

    
73
} CinepakContext;
74

    
75
static void cinepak_decode_codebook (cvid_codebook_t *codebook,
76
                                     int chunk_id, int size, uint8_t *data)
77
{
78
    uint8_t *eod = (data + size);
79
    uint32_t flag, mask;
80
    int      i, n;
81

    
82
    /* check if this chunk contains 4- or 6-element vectors */
83
    n    = (chunk_id & 0x0400) ? 4 : 6;
84
    flag = 0;
85
    mask = 0;
86

    
87
    for (i=0; i < 256; i++) {
88
        if ((chunk_id & 0x0100) && !(mask >>= 1)) {
89
            if ((data + 4) > eod)
90
                break;
91

    
92
            flag  = AV_RB32 (data);
93
            data += 4;
94
            mask  = 0x80000000;
95
        }
96

    
97
        if (!(chunk_id & 0x0100) || (flag & mask)) {
98
            if ((data + n) > eod)
99
                break;
100

    
101
            if (n == 6) {
102
                codebook[i].y0 = *data++;
103
                codebook[i].y1 = *data++;
104
                codebook[i].y2 = *data++;
105
                codebook[i].y3 = *data++;
106
                codebook[i].u  = 128 + *data++;
107
                codebook[i].v  = 128 + *data++;
108
            } else {
109
                /* this codebook type indicates either greyscale or
110
                 * palettized video; if palettized, U & V components will
111
                 * not be used so it is safe to set them to 128 for the
112
                 * benefit of greyscale rendering in YUV420P */
113
                codebook[i].y0 = *data++;
114
                codebook[i].y1 = *data++;
115
                codebook[i].y2 = *data++;
116
                codebook[i].y3 = *data++;
117
                codebook[i].u  = 128;
118
                codebook[i].v  = 128;
119
            }
120
        }
121
    }
122
}
123

    
124
static int cinepak_decode_vectors (CinepakContext *s, cvid_strip_t *strip,
125
                                   int chunk_id, int size, uint8_t *data)
126
{
127
    uint8_t         *eod = (data + size);
128
    uint32_t         flag, mask;
129
    cvid_codebook_t *codebook;
130
    unsigned int     x, y;
131
    uint32_t         iy[4];
132
    uint32_t         iu[2];
133
    uint32_t         iv[2];
134

    
135
    flag = 0;
136
    mask = 0;
137

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

    
140
        iy[0] = strip->x1 + (y * s->frame.linesize[0]);
141
        iy[1] = iy[0] + s->frame.linesize[0];
142
        iy[2] = iy[1] + s->frame.linesize[0];
143
        iy[3] = iy[2] + s->frame.linesize[0];
144
        iu[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[1]);
145
        iu[1] = iu[0] + s->frame.linesize[1];
146
        iv[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[2]);
147
        iv[1] = iv[0] + s->frame.linesize[2];
148

    
149
        for (x=strip->x1; x < strip->x2; x+=4) {
150
            if ((chunk_id & 0x0100) && !(mask >>= 1)) {
151
                if ((data + 4) > eod)
152
                    return -1;
153

    
154
                flag  = AV_RB32 (data);
155
                data += 4;
156
                mask  = 0x80000000;
157
            }
158

    
159
            if (!(chunk_id & 0x0100) || (flag & mask)) {
160
                if (!(chunk_id & 0x0200) && !(mask >>= 1)) {
161
                    if ((data + 4) > eod)
162
                        return -1;
163

    
164
                    flag  = AV_RB32 (data);
165
                    data += 4;
166
                    mask  = 0x80000000;
167
                }
168

    
169
                if ((chunk_id & 0x0200) || (~flag & mask)) {
170
                    if (data >= eod)
171
                        return -1;
172

    
173
                    codebook = &strip->v1_codebook[*data++];
174
                    s->frame.data[0][iy[0] + 0] = codebook->y0;
175
                    s->frame.data[0][iy[0] + 1] = codebook->y0;
176
                    s->frame.data[0][iy[1] + 0] = codebook->y0;
177
                    s->frame.data[0][iy[1] + 1] = codebook->y0;
178
                    if (!s->palette_video) {
179
                        s->frame.data[1][iu[0]] = codebook->u;
180
                        s->frame.data[2][iv[0]] = codebook->v;
181
                    }
182

    
183
                    s->frame.data[0][iy[0] + 2] = codebook->y1;
184
                    s->frame.data[0][iy[0] + 3] = codebook->y1;
185
                    s->frame.data[0][iy[1] + 2] = codebook->y1;
186
                    s->frame.data[0][iy[1] + 3] = codebook->y1;
187
                    if (!s->palette_video) {
188
                        s->frame.data[1][iu[0] + 1] = codebook->u;
189
                        s->frame.data[2][iv[0] + 1] = codebook->v;
190
                    }
191

    
192
                    s->frame.data[0][iy[2] + 0] = codebook->y2;
193
                    s->frame.data[0][iy[2] + 1] = codebook->y2;
194
                    s->frame.data[0][iy[3] + 0] = codebook->y2;
195
                    s->frame.data[0][iy[3] + 1] = codebook->y2;
196
                    if (!s->palette_video) {
197
                        s->frame.data[1][iu[1]] = codebook->u;
198
                        s->frame.data[2][iv[1]] = codebook->v;
199
                    }
200

    
201
                    s->frame.data[0][iy[2] + 2] = codebook->y3;
202
                    s->frame.data[0][iy[2] + 3] = codebook->y3;
203
                    s->frame.data[0][iy[3] + 2] = codebook->y3;
204
                    s->frame.data[0][iy[3] + 3] = codebook->y3;
205
                    if (!s->palette_video) {
206
                        s->frame.data[1][iu[1] + 1] = codebook->u;
207
                        s->frame.data[2][iv[1] + 1] = codebook->v;
208
                    }
209

    
210
                } else if (flag & mask) {
211
                    if ((data + 4) > eod)
212
                        return -1;
213

    
214
                    codebook = &strip->v4_codebook[*data++];
215
                    s->frame.data[0][iy[0] + 0] = codebook->y0;
216
                    s->frame.data[0][iy[0] + 1] = codebook->y1;
217
                    s->frame.data[0][iy[1] + 0] = codebook->y2;
218
                    s->frame.data[0][iy[1] + 1] = codebook->y3;
219
                    if (!s->palette_video) {
220
                        s->frame.data[1][iu[0]] = codebook->u;
221
                        s->frame.data[2][iv[0]] = codebook->v;
222
                    }
223

    
224
                    codebook = &strip->v4_codebook[*data++];
225
                    s->frame.data[0][iy[0] + 2] = codebook->y0;
226
                    s->frame.data[0][iy[0] + 3] = codebook->y1;
227
                    s->frame.data[0][iy[1] + 2] = codebook->y2;
228
                    s->frame.data[0][iy[1] + 3] = codebook->y3;
229
                    if (!s->palette_video) {
230
                        s->frame.data[1][iu[0] + 1] = codebook->u;
231
                        s->frame.data[2][iv[0] + 1] = codebook->v;
232
                    }
233

    
234
                    codebook = &strip->v4_codebook[*data++];
235
                    s->frame.data[0][iy[2] + 0] = codebook->y0;
236
                    s->frame.data[0][iy[2] + 1] = codebook->y1;
237
                    s->frame.data[0][iy[3] + 0] = codebook->y2;
238
                    s->frame.data[0][iy[3] + 1] = codebook->y3;
239
                    if (!s->palette_video) {
240
                        s->frame.data[1][iu[1]] = codebook->u;
241
                        s->frame.data[2][iv[1]] = codebook->v;
242
                    }
243

    
244
                    codebook = &strip->v4_codebook[*data++];
245
                    s->frame.data[0][iy[2] + 2] = codebook->y0;
246
                    s->frame.data[0][iy[2] + 3] = codebook->y1;
247
                    s->frame.data[0][iy[3] + 2] = codebook->y2;
248
                    s->frame.data[0][iy[3] + 3] = codebook->y3;
249
                    if (!s->palette_video) {
250
                        s->frame.data[1][iu[1] + 1] = codebook->u;
251
                        s->frame.data[2][iv[1] + 1] = codebook->v;
252
                    }
253

    
254
                }
255
            }
256

    
257
            iy[0] += 4;  iy[1] += 4;
258
            iy[2] += 4;  iy[3] += 4;
259
            iu[0] += 2;  iu[1] += 2;
260
            iv[0] += 2;  iv[1] += 2;
261
        }
262
    }
263

    
264
    return 0;
265
}
266

    
267
static int cinepak_decode_strip (CinepakContext *s,
268
                                 cvid_strip_t *strip, uint8_t *data, int size)
269
{
270
    uint8_t *eod = (data + size);
271
    int      chunk_id, chunk_size;
272

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

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

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

    
288
        switch (chunk_id) {
289

    
290
        case 0x2000:
291
        case 0x2100:
292
        case 0x2400:
293
        case 0x2500:
294
            cinepak_decode_codebook (strip->v4_codebook, chunk_id,
295
                chunk_size, data);
296
            break;
297

    
298
        case 0x2200:
299
        case 0x2300:
300
        case 0x2600:
301
        case 0x2700:
302
            cinepak_decode_codebook (strip->v1_codebook, chunk_id,
303
                chunk_size, data);
304
            break;
305

    
306
        case 0x3000:
307
        case 0x3100:
308
        case 0x3200:
309
            return cinepak_decode_vectors (s, strip, chunk_id,
310
                chunk_size, data);
311
        }
312

    
313
        data += chunk_size;
314
    }
315

    
316
    return -1;
317
}
318

    
319
static int cinepak_decode (CinepakContext *s)
320
{
321
    uint8_t      *eod = (s->data + s->size);
322
    int           i, result, strip_size, frame_flags, num_strips;
323
    int           y0 = 0;
324
    int           encoded_buf_size;
325

    
326
    if (s->size < 10)
327
        return -1;
328

    
329
    frame_flags = s->data[0];
330
    num_strips  = AV_RB16 (&s->data[8]);
331
    encoded_buf_size = ((s->data[1] << 16) | AV_RB16 (&s->data[2]));
332

    
333
    /* if this is the first frame, check for deviant Sega FILM data */
334
    if (s->sega_film_skip_bytes == -1) {
335
        if (encoded_buf_size != s->size) {
336
            /* If the encoded frame size differs from the frame size as indicated
337
             * by the container file, this data likely comes from a Sega FILM/CPK file.
338
             * If the frame header is followed by the bytes FE 00 00 06 00 00 then
339
             * this is probably one of the two known files that have 6 extra bytes
340
             * after the frame header. Else, assume 2 extra bytes. */
341
            if ((s->data[10] == 0xFE) &&
342
                (s->data[11] == 0x00) &&
343
                (s->data[12] == 0x00) &&
344
                (s->data[13] == 0x06) &&
345
                (s->data[14] == 0x00) &&
346
                (s->data[15] == 0x00))
347
                s->sega_film_skip_bytes = 6;
348
            else
349
                s->sega_film_skip_bytes = 2;
350
        } else
351
            s->sega_film_skip_bytes = 0;
352
    }
353

    
354
    s->data += 10 + s->sega_film_skip_bytes;
355

    
356
    if (num_strips > MAX_STRIPS)
357
        num_strips = MAX_STRIPS;
358

    
359
    for (i=0; i < num_strips; i++) {
360
        if ((s->data + 12) > eod)
361
            return -1;
362

    
363
        s->strips[i].id = AV_RB16 (s->data);
364
        s->strips[i].y1 = y0;
365
        s->strips[i].x1 = 0;
366
        s->strips[i].y2 = y0 + AV_RB16 (&s->data[8]);
367
        s->strips[i].x2 = s->avctx->width;
368

    
369
        strip_size = AV_RB16 (&s->data[2]) - 12;
370
        s->data   += 12;
371
        strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;
372

    
373
        if ((i > 0) && !(frame_flags & 0x01)) {
374
            memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook,
375
                sizeof(s->strips[i].v4_codebook));
376
            memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook,
377
                sizeof(s->strips[i].v1_codebook));
378
        }
379

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

    
382
        if (result != 0)
383
            return result;
384

    
385
        s->data += strip_size;
386
        y0    = s->strips[i].y2;
387
    }
388
    return 0;
389
}
390

    
391
static int cinepak_decode_init(AVCodecContext *avctx)
392
{
393
    CinepakContext *s = avctx->priv_data;
394

    
395
    s->avctx = avctx;
396
    s->width = (avctx->width + 3) & ~3;
397
    s->height = (avctx->height + 3) & ~3;
398
    s->sega_film_skip_bytes = -1;  /* uninitialized state */
399

    
400
    // check for paletted data
401
    if ((avctx->palctrl == NULL) || (avctx->bits_per_sample == 40)) {
402
        s->palette_video = 0;
403
        avctx->pix_fmt = PIX_FMT_YUV420P;
404
    } else {
405
        s->palette_video = 1;
406
        avctx->pix_fmt = PIX_FMT_PAL8;
407
    }
408

    
409
    dsputil_init(&s->dsp, avctx);
410

    
411
    s->frame.data[0] = NULL;
412

    
413
    return 0;
414
}
415

    
416
static int cinepak_decode_frame(AVCodecContext *avctx,
417
                                void *data, int *data_size,
418
                                uint8_t *buf, int buf_size)
419
{
420
    CinepakContext *s = avctx->priv_data;
421

    
422
    s->data = buf;
423
    s->size = buf_size;
424

    
425
    s->frame.reference = 1;
426
    s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
427
                            FF_BUFFER_HINTS_REUSABLE;
428
    if (avctx->reget_buffer(avctx, &s->frame)) {
429
        av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
430
        return -1;
431
    }
432

    
433
    cinepak_decode(s);
434

    
435
    if (s->palette_video) {
436
        memcpy (s->frame.data[1], avctx->palctrl->palette, AVPALETTE_SIZE);
437
        if (avctx->palctrl->palette_changed) {
438
            s->frame.palette_has_changed = 1;
439
            avctx->palctrl->palette_changed = 0;
440
        } else
441
            s->frame.palette_has_changed = 0;
442
    }
443

    
444
    *data_size = sizeof(AVFrame);
445
    *(AVFrame*)data = s->frame;
446

    
447
    /* report that the buffer was completely consumed */
448
    return buf_size;
449
}
450

    
451
static int cinepak_decode_end(AVCodecContext *avctx)
452
{
453
    CinepakContext *s = avctx->priv_data;
454

    
455
    if (s->frame.data[0])
456
        avctx->release_buffer(avctx, &s->frame);
457

    
458
    return 0;
459
}
460

    
461
AVCodec cinepak_decoder = {
462
    "cinepak",
463
    CODEC_TYPE_VIDEO,
464
    CODEC_ID_CINEPAK,
465
    sizeof(CinepakContext),
466
    cinepak_decode_init,
467
    NULL,
468
    cinepak_decode_end,
469
    cinepak_decode_frame,
470
    CODEC_CAP_DR1,
471
};