1/*
2 * QPEG codec
3 * Copyright (c) 2004 Konstantin Shishkov
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
24 * QPEG codec.
25 */
26
27#include "avcodec.h"
28#include "bytestream.h"
29#include "internal.h"
30
31typedef struct QpegContext{
32    AVCodecContext *avctx;
33    AVFrame *pic, *ref;
34    uint32_t pal[256];
35    GetByteContext buffer;
36} QpegContext;
37
38static void qpeg_decode_intra(QpegContext *qctx, uint8_t *dst,
39                              int stride, int width, int height)
40{
41    int i;
42    int code;
43    int c0, c1;
44    int run, copy;
45    int filled = 0;
46    int rows_to_go;
47
48    rows_to_go = height;
49    height--;
50    dst = dst + height * stride;
51
52    while ((bytestream2_get_bytes_left(&qctx->buffer) > 0) && (rows_to_go > 0)) {
53        code = bytestream2_get_byte(&qctx->buffer);
54        run = copy = 0;
55        if(code == 0xFC) /* end-of-picture code */
56            break;
57        if(code >= 0xF8) { /* very long run */
58            c0 = bytestream2_get_byte(&qctx->buffer);
59            c1 = bytestream2_get_byte(&qctx->buffer);
60            run = ((code & 0x7) << 16) + (c0 << 8) + c1 + 2;
61        } else if (code >= 0xF0) { /* long run */
62            c0 = bytestream2_get_byte(&qctx->buffer);
63            run = ((code & 0xF) << 8) + c0 + 2;
64        } else if (code >= 0xE0) { /* short run */
65            run = (code & 0x1F) + 2;
66        } else if (code >= 0xC0) { /* very long copy */
67            c0 = bytestream2_get_byte(&qctx->buffer);
68            c1 = bytestream2_get_byte(&qctx->buffer);
69            copy = ((code & 0x3F) << 16) + (c0 << 8) + c1 + 1;
70        } else if (code >= 0x80) { /* long copy */
71            c0 = bytestream2_get_byte(&qctx->buffer);
72            copy = ((code & 0x7F) << 8) + c0 + 1;
73        } else { /* short copy */
74            copy = code + 1;
75        }
76
77        /* perform actual run or copy */
78        if(run) {
79            int p;
80
81            p = bytestream2_get_byte(&qctx->buffer);
82            for(i = 0; i < run; i++) {
83                dst[filled++] = p;
84                if (filled >= width) {
85                    filled = 0;
86                    dst -= stride;
87                    rows_to_go--;
88                    if(rows_to_go <= 0)
89                        break;
90                }
91            }
92        } else {
93            for(i = 0; i < copy; i++) {
94                dst[filled++] = bytestream2_get_byte(&qctx->buffer);
95                if (filled >= width) {
96                    filled = 0;
97                    dst -= stride;
98                    rows_to_go--;
99                    if(rows_to_go <= 0)
100                        break;
101                }
102            }
103        }
104    }
105}
106
107static const int qpeg_table_h[16] =
108 { 0x00, 0x20, 0x20, 0x20, 0x18, 0x10, 0x10, 0x20, 0x10, 0x08, 0x18, 0x08, 0x08, 0x18, 0x10, 0x04};
109static const int qpeg_table_w[16] =
110 { 0x00, 0x20, 0x18, 0x08, 0x18, 0x10, 0x20, 0x10, 0x08, 0x10, 0x20, 0x20, 0x08, 0x10, 0x18, 0x04};
111
112/* Decodes delta frames */
113static void av_noinline qpeg_decode_inter(QpegContext *qctx, uint8_t *dst,
114                              int stride, int width, int height,
115                              int delta, const uint8_t *ctable,
116                              uint8_t *refdata)
117{
118    int i, j;
119    int code;
120    int filled = 0;
121    int orig_height;
122
123    if(!refdata)
124        refdata= dst;
125
126    /* copy prev frame */
127    for(i = 0; i < height; i++)
128        memcpy(dst + (i * stride), refdata + (i * stride), width);
129
130    orig_height = height;
131    height--;
132    dst = dst + height * stride;
133
134    while ((bytestream2_get_bytes_left(&qctx->buffer) > 0) && (height >= 0)) {
135        code = bytestream2_get_byte(&qctx->buffer);
136
137        if(delta) {
138            /* motion compensation */
139            while(bytestream2_get_bytes_left(&qctx->buffer) > 0 && (code & 0xF0) == 0xF0) {
140                if(delta == 1) {
141                    int me_idx;
142                    int me_w, me_h, me_x, me_y;
143                    uint8_t *me_plane;
144                    int corr, val;
145
146                    /* get block size by index */
147                    me_idx = code & 0xF;
148                    me_w = qpeg_table_w[me_idx];
149                    me_h = qpeg_table_h[me_idx];
150
151                    /* extract motion vector */
152                    corr = bytestream2_get_byte(&qctx->buffer);
153
154                    val = corr >> 4;
155                    if(val > 7)
156                        val -= 16;
157                    me_x = val;
158
159                    val = corr & 0xF;
160                    if(val > 7)
161                        val -= 16;
162                    me_y = val;
163
164                    /* check motion vector */
165                    if ((me_x + filled < 0) || (me_x + me_w + filled > width) ||
166                       (height - me_y - me_h < 0) || (height - me_y > orig_height) ||
167                       (filled + me_w > width) || (height - me_h < 0))
168                        av_log(NULL, AV_LOG_ERROR, "Bogus motion vector (%i,%i), block size %ix%i at %i,%i\n",
169                               me_x, me_y, me_w, me_h, filled, height);
170                    else {
171                        /* do motion compensation */
172                        me_plane = refdata + (filled + me_x) + (height - me_y) * stride;
173                        for(j = 0; j < me_h; j++) {
174                            for(i = 0; i < me_w; i++)
175                                dst[filled + i - (j * stride)] = me_plane[i - (j * stride)];
176                        }
177                    }
178                }
179                code = bytestream2_get_byte(&qctx->buffer);
180            }
181        }
182
183        if(code == 0xE0) /* end-of-picture code */
184            break;
185        if(code > 0xE0) { /* run code: 0xE1..0xFF */
186            int p;
187
188            code &= 0x1F;
189            p = bytestream2_get_byte(&qctx->buffer);
190            for(i = 0; i <= code; i++) {
191                dst[filled++] = p;
192                if(filled >= width) {
193                    filled = 0;
194                    dst -= stride;
195                    height--;
196                    if (height < 0)
197                        break;
198                }
199            }
200        } else if(code >= 0xC0) { /* copy code: 0xC0..0xDF */
201            code &= 0x1F;
202
203            if(code + 1 > bytestream2_get_bytes_left(&qctx->buffer))
204                break;
205
206            for(i = 0; i <= code; i++) {
207                dst[filled++] = bytestream2_get_byte(&qctx->buffer);
208                if(filled >= width) {
209                    filled = 0;
210                    dst -= stride;
211                    height--;
212                    if (height < 0)
213                        break;
214                }
215            }
216        } else if(code >= 0x80) { /* skip code: 0x80..0xBF */
217            int skip;
218
219            code &= 0x3F;
220            /* codes 0x80 and 0x81 are actually escape codes,
221               skip value minus constant is in the next byte */
222            if(!code)
223                skip = bytestream2_get_byte(&qctx->buffer) +  64;
224            else if(code == 1)
225                skip = bytestream2_get_byte(&qctx->buffer) + 320;
226            else
227                skip = code;
228            filled += skip;
229            while( filled >= width) {
230                filled -= width;
231                dst -= stride;
232                height--;
233                if(height < 0)
234                    break;
235            }
236        } else {
237            /* zero code treated as one-pixel skip */
238            if(code) {
239                dst[filled++] = ctable[code & 0x7F];
240            }
241            else
242                filled++;
243            if(filled >= width) {
244                filled = 0;
245                dst -= stride;
246                height--;
247            }
248        }
249    }
250}
251
252static int decode_frame(AVCodecContext *avctx,
253                        void *data, int *got_frame,
254                        AVPacket *avpkt)
255{
256    uint8_t ctable[128];
257    QpegContext * const a = avctx->priv_data;
258    AVFrame * const p = a->pic;
259    AVFrame * const ref = a->ref;
260    uint8_t* outdata;
261    int delta, ret;
262    const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL);
263
264    if (avpkt->size < 0x86) {
265        av_log(avctx, AV_LOG_ERROR, "Packet is too small\n");
266        return AVERROR_INVALIDDATA;
267    }
268
269    bytestream2_init(&a->buffer, avpkt->data, avpkt->size);
270
271    av_frame_unref(ref);
272    av_frame_move_ref(ref, p);
273
274    if ((ret = ff_get_buffer(avctx, p, AV_GET_BUFFER_FLAG_REF)) < 0)
275        return ret;
276    outdata = p->data[0];
277    bytestream2_skip(&a->buffer, 4);
278    bytestream2_get_buffer(&a->buffer, ctable, 128);
279    bytestream2_skip(&a->buffer, 1);
280
281    delta = bytestream2_get_byte(&a->buffer);
282    if(delta == 0x10) {
283        qpeg_decode_intra(a, outdata, p->linesize[0], avctx->width, avctx->height);
284    } else {
285        qpeg_decode_inter(a, outdata, p->linesize[0], avctx->width, avctx->height, delta, ctable, ref->data[0]);
286    }
287
288    /* make the palette available on the way out */
289    if (pal) {
290        p->palette_has_changed = 1;
291        memcpy(a->pal, pal, AVPALETTE_SIZE);
292    }
293    memcpy(p->data[1], a->pal, AVPALETTE_SIZE);
294
295    if ((ret = av_frame_ref(data, p)) < 0)
296        return ret;
297
298    *got_frame      = 1;
299
300    return avpkt->size;
301}
302
303static void decode_flush(AVCodecContext *avctx){
304    QpegContext * const a = avctx->priv_data;
305    int i, pal_size;
306    const uint8_t *pal_src;
307
308    pal_size = FFMIN(1024U, avctx->extradata_size);
309    pal_src = avctx->extradata + avctx->extradata_size - pal_size;
310
311    for (i=0; i<pal_size/4; i++)
312        a->pal[i] = 0xFFU<<24 | AV_RL32(pal_src+4*i);
313}
314
315static av_cold int decode_end(AVCodecContext *avctx)
316{
317    QpegContext * const a = avctx->priv_data;
318
319    av_frame_free(&a->pic);
320    av_frame_free(&a->ref);
321
322    return 0;
323}
324
325static av_cold int decode_init(AVCodecContext *avctx){
326    QpegContext * const a = avctx->priv_data;
327
328    a->avctx = avctx;
329    avctx->pix_fmt= AV_PIX_FMT_PAL8;
330
331    decode_flush(avctx);
332
333    a->pic = av_frame_alloc();
334    a->ref = av_frame_alloc();
335    if (!a->pic || !a->ref) {
336        decode_end(avctx);
337        return AVERROR(ENOMEM);
338    }
339
340    return 0;
341}
342
343AVCodec ff_qpeg_decoder = {
344    .name           = "qpeg",
345    .long_name      = NULL_IF_CONFIG_SMALL("Q-team QPEG"),
346    .type           = AVMEDIA_TYPE_VIDEO,
347    .id             = AV_CODEC_ID_QPEG,
348    .priv_data_size = sizeof(QpegContext),
349    .init           = decode_init,
350    .close          = decode_end,
351    .decode         = decode_frame,
352    .flush          = decode_flush,
353    .capabilities   = CODEC_CAP_DR1,
354};
355