FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/qpeg.c
Date: 2025-10-10 03:51:19
Exec Total Coverage
Lines: 164 190 86.3%
Functions: 6 6 100.0%
Branches: 94 122 77.0%

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