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#include <unistd.h>
36
37#include "libavutil/intreadwrite.h"
38#include "avcodec.h"
39
40
41typedef struct {
42    uint8_t  y0, y1, y2, y3;
43    uint8_t  u, v;
44} cvid_codebook;
45
46#define MAX_STRIPS      32
47
48typedef struct {
49    uint16_t          id;
50    uint16_t          x1, y1;
51    uint16_t          x2, y2;
52    cvid_codebook     v4_codebook[256];
53    cvid_codebook     v1_codebook[256];
54} cvid_strip;
55
56typedef struct CinepakContext {
57
58    AVCodecContext *avctx;
59    AVFrame frame;
60
61    const unsigned char *data;
62    int size;
63
64    int width, height;
65
66    int palette_video;
67    cvid_strip strips[MAX_STRIPS];
68
69    int sega_film_skip_bytes;
70
71} CinepakContext;
72
73static void cinepak_decode_codebook (cvid_codebook *codebook,
74                                     int chunk_id, int size, const uint8_t *data)
75{
76    const uint8_t *eod = (data + size);
77    uint32_t flag, mask;
78    int      i, n;
79
80    /* check if this chunk contains 4- or 6-element vectors */
81    n    = (chunk_id & 0x04) ? 4 : 6;
82    flag = 0;
83    mask = 0;
84
85    for (i=0; i < 256; i++) {
86        if ((chunk_id & 0x01) && !(mask >>= 1)) {
87            if ((data + 4) > eod)
88                break;
89
90            flag  = AV_RB32 (data);
91            data += 4;
92            mask  = 0x80000000;
93        }
94
95        if (!(chunk_id & 0x01) || (flag & mask)) {
96            if ((data + n) > eod)
97                break;
98
99            if (n == 6) {
100                codebook[i].y0 = *data++;
101                codebook[i].y1 = *data++;
102                codebook[i].y2 = *data++;
103                codebook[i].y3 = *data++;
104                codebook[i].u  = 128 + *data++;
105                codebook[i].v  = 128 + *data++;
106            } else {
107                /* this codebook type indicates either greyscale or
108                 * palettized video; if palettized, U & V components will
109                 * not be used so it is safe to set them to 128 for the
110                 * benefit of greyscale rendering in YUV420P */
111                codebook[i].y0 = *data++;
112                codebook[i].y1 = *data++;
113                codebook[i].y2 = *data++;
114                codebook[i].y3 = *data++;
115                codebook[i].u  = 128;
116                codebook[i].v  = 128;
117            }
118        }
119    }
120}
121
122static int cinepak_decode_vectors (CinepakContext *s, cvid_strip *strip,
123                                   int chunk_id, int size, const uint8_t *data)
124{
125    const uint8_t   *eod = (data + size);
126    uint32_t         flag, mask;
127    cvid_codebook   *codebook;
128    unsigned int     x, y;
129    uint32_t         iy[4];
130    uint32_t         iu[2];
131    uint32_t         iv[2];
132
133    flag = 0;
134    mask = 0;
135
136    for (y=strip->y1; y < strip->y2; y+=4) {
137
138        iy[0] = strip->x1 + (y * s->frame.linesize[0]);
139        iy[1] = iy[0] + s->frame.linesize[0];
140        iy[2] = iy[1] + s->frame.linesize[0];
141        iy[3] = iy[2] + s->frame.linesize[0];
142        iu[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[1]);
143        iu[1] = iu[0] + s->frame.linesize[1];
144        iv[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[2]);
145        iv[1] = iv[0] + s->frame.linesize[2];
146
147        for (x=strip->x1; x < strip->x2; x+=4) {
148            if ((chunk_id & 0x01) && !(mask >>= 1)) {
149                if ((data + 4) > eod)
150                    return -1;
151
152                flag  = AV_RB32 (data);
153                data += 4;
154                mask  = 0x80000000;
155            }
156
157            if (!(chunk_id & 0x01) || (flag & mask)) {
158                if (!(chunk_id & 0x02) && !(mask >>= 1)) {
159                    if ((data + 4) > eod)
160                        return -1;
161
162                    flag  = AV_RB32 (data);
163                    data += 4;
164                    mask  = 0x80000000;
165                }
166
167                if ((chunk_id & 0x02) || (~flag & mask)) {
168                    if (data >= eod)
169                        return -1;
170
171                    codebook = &strip->v1_codebook[*data++];
172                    s->frame.data[0][iy[0] + 0] = codebook->y0;
173                    s->frame.data[0][iy[0] + 1] = codebook->y0;
174                    s->frame.data[0][iy[1] + 0] = codebook->y0;
175                    s->frame.data[0][iy[1] + 1] = codebook->y0;
176                    if (!s->palette_video) {
177                        s->frame.data[1][iu[0]] = codebook->u;
178                        s->frame.data[2][iv[0]] = codebook->v;
179                    }
180
181                    s->frame.data[0][iy[0] + 2] = codebook->y1;
182                    s->frame.data[0][iy[0] + 3] = codebook->y1;
183                    s->frame.data[0][iy[1] + 2] = codebook->y1;
184                    s->frame.data[0][iy[1] + 3] = codebook->y1;
185                    if (!s->palette_video) {
186                        s->frame.data[1][iu[0] + 1] = codebook->u;
187                        s->frame.data[2][iv[0] + 1] = codebook->v;
188                    }
189
190                    s->frame.data[0][iy[2] + 0] = codebook->y2;
191                    s->frame.data[0][iy[2] + 1] = codebook->y2;
192                    s->frame.data[0][iy[3] + 0] = codebook->y2;
193                    s->frame.data[0][iy[3] + 1] = codebook->y2;
194                    if (!s->palette_video) {
195                        s->frame.data[1][iu[1]] = codebook->u;
196                        s->frame.data[2][iv[1]] = codebook->v;
197                    }
198
199                    s->frame.data[0][iy[2] + 2] = codebook->y3;
200                    s->frame.data[0][iy[2] + 3] = codebook->y3;
201                    s->frame.data[0][iy[3] + 2] = codebook->y3;
202                    s->frame.data[0][iy[3] + 3] = codebook->y3;
203                    if (!s->palette_video) {
204                        s->frame.data[1][iu[1] + 1] = codebook->u;
205                        s->frame.data[2][iv[1] + 1] = codebook->v;
206                    }
207
208                } else if (flag & mask) {
209                    if ((data + 4) > eod)
210                        return -1;
211
212                    codebook = &strip->v4_codebook[*data++];
213                    s->frame.data[0][iy[0] + 0] = codebook->y0;
214                    s->frame.data[0][iy[0] + 1] = codebook->y1;
215                    s->frame.data[0][iy[1] + 0] = codebook->y2;
216                    s->frame.data[0][iy[1] + 1] = codebook->y3;
217                    if (!s->palette_video) {
218                        s->frame.data[1][iu[0]] = codebook->u;
219                        s->frame.data[2][iv[0]] = codebook->v;
220                    }
221
222                    codebook = &strip->v4_codebook[*data++];
223                    s->frame.data[0][iy[0] + 2] = codebook->y0;
224                    s->frame.data[0][iy[0] + 3] = codebook->y1;
225                    s->frame.data[0][iy[1] + 2] = codebook->y2;
226                    s->frame.data[0][iy[1] + 3] = codebook->y3;
227                    if (!s->palette_video) {
228                        s->frame.data[1][iu[0] + 1] = codebook->u;
229                        s->frame.data[2][iv[0] + 1] = codebook->v;
230                    }
231
232                    codebook = &strip->v4_codebook[*data++];
233                    s->frame.data[0][iy[2] + 0] = codebook->y0;
234                    s->frame.data[0][iy[2] + 1] = codebook->y1;
235                    s->frame.data[0][iy[3] + 0] = codebook->y2;
236                    s->frame.data[0][iy[3] + 1] = codebook->y3;
237                    if (!s->palette_video) {
238                        s->frame.data[1][iu[1]] = codebook->u;
239                        s->frame.data[2][iv[1]] = codebook->v;
240                    }
241
242                    codebook = &strip->v4_codebook[*data++];
243                    s->frame.data[0][iy[2] + 2] = codebook->y0;
244                    s->frame.data[0][iy[2] + 3] = codebook->y1;
245                    s->frame.data[0][iy[3] + 2] = codebook->y2;
246                    s->frame.data[0][iy[3] + 3] = codebook->y3;
247                    if (!s->palette_video) {
248                        s->frame.data[1][iu[1] + 1] = codebook->u;
249                        s->frame.data[2][iv[1] + 1] = codebook->v;
250                    }
251
252                }
253            }
254
255            iy[0] += 4;  iy[1] += 4;
256            iy[2] += 4;  iy[3] += 4;
257            iu[0] += 2;  iu[1] += 2;
258            iv[0] += 2;  iv[1] += 2;
259        }
260    }
261
262    return 0;
263}
264
265static int cinepak_decode_strip (CinepakContext *s,
266                                 cvid_strip *strip, const uint8_t *data, int size)
267{
268    const uint8_t *eod = (data + size);
269    int      chunk_id, chunk_size;
270
271    /* coordinate sanity checks */
272    if (strip->x1 >= s->width  || strip->x2 > s->width  ||
273        strip->y1 >= s->height || strip->y2 > s->height ||
274        strip->x1 >= strip->x2 || strip->y1 >= strip->y2)
275        return -1;
276
277    while ((data + 4) <= eod) {
278        chunk_id   = data[0];
279        chunk_size = AV_RB24 (&data[1]) - 4;
280        if(chunk_size < 0)
281            return -1;
282
283        data      += 4;
284        chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size;
285
286        switch (chunk_id) {
287
288        case 0x20:
289        case 0x21:
290        case 0x24:
291        case 0x25:
292            cinepak_decode_codebook (strip->v4_codebook, chunk_id,
293                chunk_size, data);
294            break;
295
296        case 0x22:
297        case 0x23:
298        case 0x26:
299        case 0x27:
300            cinepak_decode_codebook (strip->v1_codebook, chunk_id,
301                chunk_size, data);
302            break;
303
304        case 0x30:
305        case 0x31:
306        case 0x32:
307            return cinepak_decode_vectors (s, strip, chunk_id,
308                chunk_size, data);
309        }
310
311        data += chunk_size;
312    }
313
314    return -1;
315}
316
317static int cinepak_decode (CinepakContext *s)
318{
319    const uint8_t  *eod = (s->data + s->size);
320    int           i, result, strip_size, frame_flags, num_strips;
321    int           y0 = 0;
322    int           encoded_buf_size;
323
324    if (s->size < 10)
325        return -1;
326
327    frame_flags = s->data[0];
328    num_strips  = AV_RB16 (&s->data[8]);
329    encoded_buf_size = ((s->data[1] << 16) | AV_RB16 (&s->data[2]));
330
331    /* if this is the first frame, check for deviant Sega FILM data */
332    if (s->sega_film_skip_bytes == -1) {
333        if (encoded_buf_size != s->size) {
334            /* If the encoded frame size differs from the frame size as indicated
335             * by the container file, this data likely comes from a Sega FILM/CPK file.
336             * If the frame header is followed by the bytes FE 00 00 06 00 00 then
337             * this is probably one of the two known files that have 6 extra bytes
338             * after the frame header. Else, assume 2 extra bytes. */
339            if ((s->data[10] == 0xFE) &&
340                (s->data[11] == 0x00) &&
341                (s->data[12] == 0x00) &&
342                (s->data[13] == 0x06) &&
343                (s->data[14] == 0x00) &&
344                (s->data[15] == 0x00))
345                s->sega_film_skip_bytes = 6;
346            else
347                s->sega_film_skip_bytes = 2;
348        } else
349            s->sega_film_skip_bytes = 0;
350    }
351
352    s->data += 10 + s->sega_film_skip_bytes;
353
354    if (num_strips > MAX_STRIPS)
355        num_strips = MAX_STRIPS;
356
357    for (i=0; i < num_strips; i++) {
358        if ((s->data + 12) > eod)
359            return -1;
360
361        s->strips[i].id = s->data[0];
362        s->strips[i].y1 = y0;
363        s->strips[i].x1 = 0;
364        s->strips[i].y2 = y0 + AV_RB16 (&s->data[8]);
365        s->strips[i].x2 = s->avctx->width;
366
367        strip_size = AV_RB24 (&s->data[1]) - 12;
368        s->data   += 12;
369        strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size;
370
371        if ((i > 0) && !(frame_flags & 0x01)) {
372            memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook,
373                sizeof(s->strips[i].v4_codebook));
374            memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook,
375                sizeof(s->strips[i].v1_codebook));
376        }
377
378        result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size);
379
380        if (result != 0)
381            return result;
382
383        s->data += strip_size;
384        y0    = s->strips[i].y2;
385    }
386    return 0;
387}
388
389static av_cold int cinepak_decode_init(AVCodecContext *avctx)
390{
391    CinepakContext *s = avctx->priv_data;
392
393    s->avctx = avctx;
394    s->width = (avctx->width + 3) & ~3;
395    s->height = (avctx->height + 3) & ~3;
396    s->sega_film_skip_bytes = -1;  /* uninitialized state */
397
398    // check for paletted data
399    if ((avctx->palctrl == NULL) || (avctx->bits_per_coded_sample == 40)) {
400        s->palette_video = 0;
401        avctx->pix_fmt = PIX_FMT_YUV420P;
402    } else {
403        s->palette_video = 1;
404        avctx->pix_fmt = PIX_FMT_PAL8;
405    }
406
407    s->frame.data[0] = NULL;
408
409    return 0;
410}
411
412static int cinepak_decode_frame(AVCodecContext *avctx,
413                                void *data, int *data_size,
414                                const uint8_t *buf, int buf_size)
415{
416    CinepakContext *s = avctx->priv_data;
417
418    s->data = buf;
419    s->size = buf_size;
420
421    s->frame.reference = 1;
422    s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
423                            FF_BUFFER_HINTS_REUSABLE;
424    if (avctx->reget_buffer(avctx, &s->frame)) {
425        av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
426        return -1;
427    }
428
429    cinepak_decode(s);
430
431    if (s->palette_video) {
432        memcpy (s->frame.data[1], avctx->palctrl->palette, AVPALETTE_SIZE);
433        if (avctx->palctrl->palette_changed) {
434            s->frame.palette_has_changed = 1;
435            avctx->palctrl->palette_changed = 0;
436        } else
437            s->frame.palette_has_changed = 0;
438    }
439
440    *data_size = sizeof(AVFrame);
441    *(AVFrame*)data = s->frame;
442
443    /* report that the buffer was completely consumed */
444    return buf_size;
445}
446
447static av_cold int cinepak_decode_end(AVCodecContext *avctx)
448{
449    CinepakContext *s = avctx->priv_data;
450
451    if (s->frame.data[0])
452        avctx->release_buffer(avctx, &s->frame);
453
454    return 0;
455}
456
457AVCodec cinepak_decoder = {
458    "cinepak",
459    CODEC_TYPE_VIDEO,
460    CODEC_ID_CINEPAK,
461    sizeof(CinepakContext),
462    cinepak_decode_init,
463    NULL,
464    cinepak_decode_end,
465    cinepak_decode_frame,
466    CODEC_CAP_DR1,
467    .long_name = NULL_IF_CONFIG_SMALL("Cinepak"),
468};
469