GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/qpeg.c Lines: 168 195 86.2 %
Date: 2020-10-23 17:01:47 Branches: 96 126 76.2 %

Line Branch Exec Source
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
31
typedef struct QpegContext{
32
    AVCodecContext *avctx;
33
    AVFrame *ref;
34
    uint32_t pal[256];
35
    GetByteContext buffer;
36
} QpegContext;
37
38
1
static 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
1
    int filled = 0;
46
    int rows_to_go;
47
48
1
    rows_to_go = height;
49
1
    height--;
50
1
    dst = dst + height * stride;
51
52

10407
    while ((bytestream2_get_bytes_left(&qctx->buffer) > 0) && (rows_to_go > 0)) {
53
10406
        code = bytestream2_get_byte(&qctx->buffer);
54
10406
        run = copy = 0;
55
10406
        if(code == 0xFC) /* end-of-picture code */
56
            break;
57
10406
        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
10406
        } else if (code >= 0xF0) { /* long run */
62
21
            c0 = bytestream2_get_byte(&qctx->buffer);
63
21
            run = ((code & 0xF) << 8) + c0 + 2;
64
10385
        } else if (code >= 0xE0) { /* short run */
65
5687
            run = (code & 0x1F) + 2;
66
4698
        } 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
4698
        } 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
4698
            copy = code + 1;
75
        }
76
77
        /* perform actual run or copy */
78
10406
        if(run) {
79
            int p;
80
81
5708
            p = bytestream2_get_byte(&qctx->buffer);
82
11416
            for(i = 0; i < run; i++) {
83
5708
                int step = FFMIN(run - i, width - filled);
84
5708
                memset(dst+filled, p, step);
85
5708
                filled += step;
86
5708
                i      += step - 1;
87
5708
                if (filled >= width) {
88
30
                    filled = 0;
89
30
                    dst -= stride;
90
30
                    rows_to_go--;
91

30
                    while (run - i > width && rows_to_go > 0) {
92
                        memset(dst, p, width);
93
                        dst -= stride;
94
                        rows_to_go--;
95
                        i += width;
96
                    }
97
30
                    if(rows_to_go <= 0)
98
                        break;
99
                }
100
            }
101
        } else {
102
4698
            if (bytestream2_get_bytes_left(&qctx->buffer) < copy)
103
                copy = bytestream2_get_bytes_left(&qctx->buffer);
104
9588
            while (copy > 0) {
105
4891
                int step = FFMIN(copy, width - filled);
106
4891
                bytestream2_get_bufferu(&qctx->buffer, dst + filled, step);
107
4891
                filled += step;
108
4891
                copy -= step;
109
4891
                if (filled >= width) {
110
210
                    filled = 0;
111
210
                    dst -= stride;
112
210
                    rows_to_go--;
113
210
                    if(rows_to_go <= 0)
114
1
                        break;
115
                }
116
            }
117
        }
118
    }
119
1
}
120
121
static const uint8_t qpeg_table_h[16] =
122
 { 0x00, 0x20, 0x20, 0x20, 0x18, 0x10, 0x10, 0x20, 0x10, 0x08, 0x18, 0x08, 0x08, 0x18, 0x10, 0x04};
123
static const uint8_t qpeg_table_w[16] =
124
 { 0x00, 0x20, 0x18, 0x08, 0x18, 0x10, 0x20, 0x10, 0x08, 0x10, 0x20, 0x20, 0x08, 0x10, 0x18, 0x04};
125
126
/* Decodes delta frames */
127
99
static void av_noinline qpeg_decode_inter(QpegContext *qctx, uint8_t *dst,
128
                              int stride, int width, int height,
129
                              int delta, const uint8_t *ctable,
130
                              uint8_t *refdata)
131
{
132
    int i, j;
133
    int code;
134
99
    int filled = 0;
135
    int orig_height;
136
137
99
    if (refdata) {
138
        /* copy prev frame */
139
23859
        for (i = 0; i < height; i++)
140
23760
            memcpy(dst + (i * stride), refdata + (i * stride), width);
141
    } else {
142
        refdata = dst;
143
    }
144
145
99
    orig_height = height;
146
99
    height--;
147
99
    dst = dst + height * stride;
148
149

731786
    while ((bytestream2_get_bytes_left(&qctx->buffer) > 0) && (height >= 0)) {
150
731779
        code = bytestream2_get_byte(&qctx->buffer);
151
152
731779
        if(delta) {
153
            /* motion compensation */
154

751186
            while(bytestream2_get_bytes_left(&qctx->buffer) > 0 && (code & 0xF0) == 0xF0) {
155
19407
                if(delta == 1) {
156
                    int me_idx;
157
                    int me_w, me_h, me_x, me_y;
158
                    uint8_t *me_plane;
159
                    int corr, val;
160
161
                    /* get block size by index */
162
19407
                    me_idx = code & 0xF;
163
19407
                    me_w = qpeg_table_w[me_idx];
164
19407
                    me_h = qpeg_table_h[me_idx];
165
166
                    /* extract motion vector */
167
19407
                    corr = bytestream2_get_byte(&qctx->buffer);
168
169
19407
                    val = corr >> 4;
170
19407
                    if(val > 7)
171
8637
                        val -= 16;
172
19407
                    me_x = val;
173
174
19407
                    val = corr & 0xF;
175
19407
                    if(val > 7)
176
9623
                        val -= 16;
177
19407
                    me_y = val;
178
179
                    /* check motion vector */
180

19407
                    if ((me_x + filled < 0) || (me_x + me_w + filled > width) ||
181

19407
                       (height - me_y - me_h < 0) || (height - me_y >= orig_height) ||
182

19407
                       (filled + me_w > width) || (height - me_h < 0))
183
                        av_log(qctx->avctx, AV_LOG_ERROR, "Bogus motion vector (%i,%i), block size %ix%i at %i,%i\n",
184
                               me_x, me_y, me_w, me_h, filled, height);
185
                    else {
186
                        /* do motion compensation */
187
19407
                        me_plane = refdata + (filled + me_x) + (height - me_y) * stride;
188
177719
                        for(j = 0; j < me_h; j++) {
189
1454952
                            for(i = 0; i < me_w; i++)
190
1296640
                                dst[filled + i - (j * stride)] = me_plane[i - (j * stride)];
191
                        }
192
                    }
193
                }
194
19407
                code = bytestream2_get_byte(&qctx->buffer);
195
            }
196
        }
197
198
731779
        if(code == 0xE0) /* end-of-picture code */
199
92
            break;
200
731687
        if(code > 0xE0) { /* run code: 0xE1..0xFF */
201
            int p;
202
203
96361
            code &= 0x1F;
204
96361
            p = bytestream2_get_byte(&qctx->buffer);
205
359528
            for(i = 0; i <= code; i++) {
206
263167
                dst[filled++] = p;
207
263167
                if(filled >= width) {
208
72
                    filled = 0;
209
72
                    dst -= stride;
210
72
                    height--;
211
72
                    if (height < 0)
212
                        break;
213
                }
214
            }
215
635326
        } else if(code >= 0xC0) { /* copy code: 0xC0..0xDF */
216
214471
            code &= 0x1F;
217
218
214471
            if(code + 1 > bytestream2_get_bytes_left(&qctx->buffer))
219
                break;
220
221
957914
            for(i = 0; i <= code; i++) {
222
743443
                dst[filled++] = bytestream2_get_byte(&qctx->buffer);
223
743443
                if(filled >= width) {
224
16
                    filled = 0;
225
16
                    dst -= stride;
226
16
                    height--;
227
16
                    if (height < 0)
228
                        break;
229
                }
230
            }
231
420855
        } else if(code >= 0x80) { /* skip code: 0x80..0xBF */
232
            int skip;
233
234
249130
            code &= 0x3F;
235
            /* codes 0x80 and 0x81 are actually escape codes,
236
               skip value minus constant is in the next byte */
237
249130
            if(!code)
238
29191
                skip = bytestream2_get_byte(&qctx->buffer) +  64;
239
219939
            else if(code == 1)
240
273
                skip = bytestream2_get_byte(&qctx->buffer) + 320;
241
            else
242
219666
                skip = code;
243
249130
            filled += skip;
244
272528
            while( filled >= width) {
245
23398
                filled -= width;
246
23398
                dst -= stride;
247
23398
                height--;
248
23398
                if(height < 0)
249
                    break;
250
            }
251
        } else {
252
            /* zero code treated as one-pixel skip */
253
171725
            if(code) {
254
124870
                dst[filled++] = ctable[code & 0x7F];
255
            }
256
            else
257
46855
                filled++;
258
171725
            if(filled >= width) {
259
182
                filled = 0;
260
182
                dst -= stride;
261
182
                height--;
262
            }
263
        }
264
    }
265
99
}
266
267
100
static int decode_frame(AVCodecContext *avctx,
268
                        void *data, int *got_frame,
269
                        AVPacket *avpkt)
270
{
271
    uint8_t ctable[128];
272
100
    QpegContext * const a = avctx->priv_data;
273
100
    AVFrame * const p = data;
274
100
    AVFrame * const ref = a->ref;
275
    uint8_t* outdata;
276
    int delta, intra, ret;
277
    int pal_size;
278
100
    const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, &pal_size);
279
280
100
    if (avpkt->size < 0x86) {
281
        av_log(avctx, AV_LOG_ERROR, "Packet is too small\n");
282
        return AVERROR_INVALIDDATA;
283
    }
284
285
100
    bytestream2_init(&a->buffer, avpkt->data, avpkt->size);
286
287
100
    if ((ret = ff_get_buffer(avctx, p, AV_GET_BUFFER_FLAG_REF)) < 0)
288
        return ret;
289
100
    outdata = p->data[0];
290
100
    bytestream2_skip(&a->buffer, 4);
291
100
    bytestream2_get_buffer(&a->buffer, ctable, 128);
292
100
    bytestream2_skip(&a->buffer, 1);
293
294
100
    delta = bytestream2_get_byte(&a->buffer);
295
100
    intra = delta == 0x10;
296
100
    if (intra) {
297
1
        qpeg_decode_intra(a, outdata, p->linesize[0], avctx->width, avctx->height);
298
    } else {
299
99
        qpeg_decode_inter(a, outdata, p->linesize[0], avctx->width, avctx->height, delta, ctable, ref->data[0]);
300
    }
301
302
    /* make the palette available on the way out */
303

100
    if (pal && pal_size == AVPALETTE_SIZE) {
304
1
        p->palette_has_changed = 1;
305
1
        memcpy(a->pal, pal, AVPALETTE_SIZE);
306
99
    } else if (pal) {
307
        av_log(avctx, AV_LOG_ERROR, "Palette size %d is wrong\n", pal_size);
308
    }
309
100
    memcpy(p->data[1], a->pal, AVPALETTE_SIZE);
310
311
100
    av_frame_unref(ref);
312
100
    if ((ret = av_frame_ref(ref, p)) < 0)
313
        return ret;
314
315
100
    p->key_frame = intra;
316
100
    p->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
317
318
100
    *got_frame      = 1;
319
320
100
    return avpkt->size;
321
}
322
323
2
static void decode_flush(AVCodecContext *avctx){
324
2
    QpegContext * const a = avctx->priv_data;
325
    int i, pal_size;
326
    const uint8_t *pal_src;
327
328
2
    av_frame_unref(a->ref);
329
330
2
    pal_size = FFMIN(1024U, avctx->extradata_size);
331
2
    pal_src = avctx->extradata + avctx->extradata_size - pal_size;
332
333
514
    for (i=0; i<pal_size/4; i++)
334
512
        a->pal[i] = 0xFFU<<24 | AV_RL32(pal_src+4*i);
335
2
}
336
337
2
static av_cold int decode_end(AVCodecContext *avctx)
338
{
339
2
    QpegContext * const a = avctx->priv_data;
340
341
2
    av_frame_free(&a->ref);
342
343
2
    return 0;
344
}
345
346
2
static av_cold int decode_init(AVCodecContext *avctx){
347
2
    QpegContext * const a = avctx->priv_data;
348
349
2
    a->avctx = avctx;
350
2
    avctx->pix_fmt= AV_PIX_FMT_PAL8;
351
352
2
    a->ref = av_frame_alloc();
353
2
    if (!a->ref)
354
        return AVERROR(ENOMEM);
355
356
2
    decode_flush(avctx);
357
358
2
    return 0;
359
}
360
361
AVCodec ff_qpeg_decoder = {
362
    .name           = "qpeg",
363
    .long_name      = NULL_IF_CONFIG_SMALL("Q-team QPEG"),
364
    .type           = AVMEDIA_TYPE_VIDEO,
365
    .id             = AV_CODEC_ID_QPEG,
366
    .priv_data_size = sizeof(QpegContext),
367
    .init           = decode_init,
368
    .close          = decode_end,
369
    .decode         = decode_frame,
370
    .flush          = decode_flush,
371
    .capabilities   = AV_CODEC_CAP_DR1,
372
    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
373
                      FF_CODEC_CAP_INIT_CLEANUP,
374
};