Statistics
| Branch: | Revision:

ffmpeg / libavcodec / cinepak.c @ 72415b2a

History | View | Annotate | Download (15 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
 * @file libavcodec/cinepak.c
24
 * Cinepak video decoder
25
 * by Ewald Snel <ewald@rambo.its.tudelft.nl>
26
 * For more information on the Cinepak algorithm, visit:
27
 *   http://www.csse.monash.edu.au/~timf/
28
 * For more information on the quirky data inside Sega FILM/CPK files, visit:
29
 *   http://wiki.multimedia.cx/index.php?title=Sega_FILM
30
 */
31

    
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35

    
36
#include "libavutil/intreadwrite.h"
37
#include "avcodec.h"
38

    
39

    
40
typedef struct {
41
    uint8_t  y0, y1, y2, y3;
42
    uint8_t  u, v;
43
} cvid_codebook;
44

    
45
#define MAX_STRIPS      32
46

    
47
typedef struct {
48
    uint16_t          id;
49
    uint16_t          x1, y1;
50
    uint16_t          x2, y2;
51
    cvid_codebook     v4_codebook[256];
52
    cvid_codebook     v1_codebook[256];
53
} cvid_strip;
54

    
55
typedef struct CinepakContext {
56

    
57
    AVCodecContext *avctx;
58
    AVFrame frame;
59

    
60
    const unsigned char *data;
61
    int size;
62

    
63
    int width, height;
64

    
65
    int palette_video;
66
    cvid_strip strips[MAX_STRIPS];
67

    
68
    int sega_film_skip_bytes;
69

    
70
} CinepakContext;
71

    
72
static void cinepak_decode_codebook (cvid_codebook *codebook,
73
                                     int chunk_id, int size, const uint8_t *data)
74
{
75
    const uint8_t *eod = (data + size);
76
    uint32_t flag, mask;
77
    int      i, n;
78

    
79
    /* check if this chunk contains 4- or 6-element vectors */
80
    n    = (chunk_id & 0x04) ? 4 : 6;
81
    flag = 0;
82
    mask = 0;
83

    
84
    for (i=0; i < 256; i++) {
85
        if ((chunk_id & 0x01) && !(mask >>= 1)) {
86
            if ((data + 4) > eod)
87
                break;
88

    
89
            flag  = AV_RB32 (data);
90
            data += 4;
91
            mask  = 0x80000000;
92
        }
93

    
94
        if (!(chunk_id & 0x01) || (flag & mask)) {
95
            if ((data + n) > eod)
96
                break;
97

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

    
121
static int cinepak_decode_vectors (CinepakContext *s, cvid_strip *strip,
122
                                   int chunk_id, int size, const uint8_t *data)
123
{
124
    const uint8_t   *eod = (data + size);
125
    uint32_t         flag, mask;
126
    cvid_codebook   *codebook;
127
    unsigned int     x, y;
128
    uint32_t         iy[4];
129
    uint32_t         iu[2];
130
    uint32_t         iv[2];
131

    
132
    flag = 0;
133
    mask = 0;
134

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

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

    
146
        for (x=strip->x1; x < strip->x2; x+=4) {
147
            if ((chunk_id & 0x01) && !(mask >>= 1)) {
148
                if ((data + 4) > eod)
149
                    return -1;
150

    
151
                flag  = AV_RB32 (data);
152
                data += 4;
153
                mask  = 0x80000000;
154
            }
155

    
156
            if (!(chunk_id & 0x01) || (flag & mask)) {
157
                if (!(chunk_id & 0x02) && !(mask >>= 1)) {
158
                    if ((data + 4) > eod)
159
                        return -1;
160

    
161
                    flag  = AV_RB32 (data);
162
                    data += 4;
163
                    mask  = 0x80000000;
164
                }
165

    
166
                if ((chunk_id & 0x02) || (~flag & mask)) {
167
                    if (data >= eod)
168
                        return -1;
169

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

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

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

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

    
207
                } else if (flag & mask) {
208
                    if ((data + 4) > eod)
209
                        return -1;
210

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

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

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

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

    
251
                }
252
            }
253

    
254
            iy[0] += 4;  iy[1] += 4;
255
            iy[2] += 4;  iy[3] += 4;
256
            iu[0] += 2;  iu[1] += 2;
257
            iv[0] += 2;  iv[1] += 2;
258
        }
259
    }
260

    
261
    return 0;
262
}
263

    
264
static int cinepak_decode_strip (CinepakContext *s,
265
                                 cvid_strip *strip, const uint8_t *data, int size)
266
{
267
    const uint8_t *eod = (data + size);
268
    int      chunk_id, chunk_size;
269

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

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

    
282
        data      += 4;
283
        chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size;
284

    
285
        switch (chunk_id) {
286

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

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

    
303
        case 0x30:
304
        case 0x31:
305
        case 0x32:
306
            return cinepak_decode_vectors (s, strip, chunk_id,
307
                chunk_size, data);
308
        }
309

    
310
        data += chunk_size;
311
    }
312

    
313
    return -1;
314
}
315

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

    
323
    if (s->size < 10)
324
        return -1;
325

    
326
    frame_flags = s->data[0];
327
    num_strips  = AV_RB16 (&s->data[8]);
328
    encoded_buf_size = ((s->data[1] << 16) | AV_RB16 (&s->data[2]));
329

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

    
351
    s->data += 10 + s->sega_film_skip_bytes;
352

    
353
    if (num_strips > MAX_STRIPS)
354
        num_strips = MAX_STRIPS;
355

    
356
    for (i=0; i < num_strips; i++) {
357
        if ((s->data + 12) > eod)
358
            return -1;
359

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

    
366
        strip_size = AV_RB24 (&s->data[1]) - 12;
367
        s->data   += 12;
368
        strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;
369

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

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

    
379
        if (result != 0)
380
            return result;
381

    
382
        s->data += strip_size;
383
        y0    = s->strips[i].y2;
384
    }
385
    return 0;
386
}
387

    
388
static av_cold int cinepak_decode_init(AVCodecContext *avctx)
389
{
390
    CinepakContext *s = avctx->priv_data;
391

    
392
    s->avctx = avctx;
393
    s->width = (avctx->width + 3) & ~3;
394
    s->height = (avctx->height + 3) & ~3;
395
    s->sega_film_skip_bytes = -1;  /* uninitialized state */
396

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

    
406
    s->frame.data[0] = NULL;
407

    
408
    return 0;
409
}
410

    
411
static int cinepak_decode_frame(AVCodecContext *avctx,
412
                                void *data, int *data_size,
413
                                AVPacket *avpkt)
414
{
415
    const uint8_t *buf = avpkt->data;
416
    int buf_size = avpkt->size;
417
    CinepakContext *s = avctx->priv_data;
418

    
419
    s->data = buf;
420
    s->size = buf_size;
421

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

    
430
    cinepak_decode(s);
431

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

    
441
    *data_size = sizeof(AVFrame);
442
    *(AVFrame*)data = s->frame;
443

    
444
    /* report that the buffer was completely consumed */
445
    return buf_size;
446
}
447

    
448
static av_cold int cinepak_decode_end(AVCodecContext *avctx)
449
{
450
    CinepakContext *s = avctx->priv_data;
451

    
452
    if (s->frame.data[0])
453
        avctx->release_buffer(avctx, &s->frame);
454

    
455
    return 0;
456
}
457

    
458
AVCodec cinepak_decoder = {
459
    "cinepak",
460
    AVMEDIA_TYPE_VIDEO,
461
    CODEC_ID_CINEPAK,
462
    sizeof(CinepakContext),
463
    cinepak_decode_init,
464
    NULL,
465
    cinepak_decode_end,
466
    cinepak_decode_frame,
467
    CODEC_CAP_DR1,
468
    .long_name = NULL_IF_CONFIG_SMALL("Cinepak"),
469
};