FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/apngdec.c
Date: 2026-10-30 17:37:53
Exec Total Coverage
Lines: 180 232 77.6%
Functions: 7 7 100.0%
Branches: 113 186 60.8%

Line Branch Exec Source
1 /*
2 * APNG demuxer
3 * Copyright (c) 2014 Benoit Fouet
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 * APNG demuxer.
25 * @see https://wiki.mozilla.org/APNG_Specification
26 * @see http://www.w3.org/TR/PNG
27 */
28
29 #include "avformat.h"
30 #include "avio_internal.h"
31 #include "demux.h"
32 #include "internal.h"
33 #include "libavutil/imgutils.h"
34 #include "libavutil/intreadwrite.h"
35 #include "libavutil/mem.h"
36 #include "libavutil/opt.h"
37 #include "libavcodec/apng.h"
38 #include "libavcodec/png.h"
39 #include "libavcodec/bytestream.h"
40
41 #define DEFAULT_APNG_FPS 15
42
43 typedef struct APNGDemuxContext {
44 const AVClass *class;
45
46 int max_fps;
47 int default_fps;
48
49 int pkt_duration;
50
51 int is_key_frame;
52
53 /* fcTL and fdAT chunks share a single sequence number, which must start
54 * at 0 and increase by one for every chunk, with no gap or duplicate */
55 uint32_t expected_seq;
56
57 /*
58 * loop options
59 */
60 int ignore_loop;
61 uint32_t num_frames;
62 uint32_t num_play;
63 uint32_t cur_loop;
64 } APNGDemuxContext;
65
66 /*
67 * To be a valid APNG file, we mandate, in this order:
68 * PNGSIG
69 * IHDR
70 * ...
71 * acTL
72 * ...
73 * IDAT
74 */
75 8356 static int apng_probe(const AVProbeData *p)
76 {
77 GetByteContext gb;
78 8356 int state = 0;
79 uint32_t len, tag;
80
81 8356 bytestream2_init(&gb, p->buf, p->buf_size);
82
83
2/2
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 8302 times.
8356 if (bytestream2_get_be64(&gb) != PNGSIG)
84 8302 return 0;
85
86 for (;;) {
87 237 len = bytestream2_get_be32(&gb);
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 237 times.
237 if (len > 0x7fffffff)
89 ✗ return 0;
90
91 237 tag = bytestream2_get_le32(&gb);
92 /* we don't check IDAT size, as this is the last tag
93 * we check, and it may be larger than the probe buffer */
94
2/2
✓ Branch 0 taken 187 times.
✓ Branch 1 taken 50 times.
237 if (tag != MKTAG('I', 'D', 'A', 'T') &&
95
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 183 times.
187 len + 4 > bytestream2_get_bytes_left(&gb))
96 4 return 0;
97
98
4/4
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 118 times.
233 switch (tag) {
99 54 case MKTAG('I', 'H', 'D', 'R'):
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (len != 13)
101 ✗ return 0;
102
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 54 times.
54 if (av_image_check_size(bytestream2_get_be32(&gb), bytestream2_get_be32(&gb), 0, NULL))
103 ✗ return 0;
104 54 bytestream2_skip(&gb, 9);
105 54 state++;
106 54 break;
107 11 case MKTAG('a', 'c', 'T', 'L'):
108
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 if (state != 1 ||
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 len != 8 ||
110 11 bytestream2_get_be32(&gb) == 0) /* 0 is not a valid value for number of frames */
111 ✗ return 0;
112 11 bytestream2_skip(&gb, 8);
113 11 state++;
114 11 break;
115 50 case MKTAG('I', 'D', 'A', 'T'):
116
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 11 times.
50 if (state != 2)
117 39 return 0;
118 11 goto end;
119 118 default:
120 /* skip other tags */
121 118 bytestream2_skip(&gb, len + 4);
122 118 break;
123 }
124 }
125
126 11 end:
127 11 return AVPROBE_SCORE_MAX;
128 }
129
130 23 static int append_extradata(AVCodecParameters *par, AVIOContext *pb, int len)
131 {
132 23 int previous_size = par->extradata_size;
133 int new_size, ret;
134 uint8_t *new_extradata;
135
136
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (len > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE - previous_size)
137 ✗ return AVERROR_INVALIDDATA;
138
139 23 new_size = previous_size + len;
140 23 new_extradata = av_realloc(par->extradata, new_size + AV_INPUT_BUFFER_PADDING_SIZE);
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (!new_extradata)
142 ✗ return AVERROR(ENOMEM);
143 23 memset(new_extradata + new_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
144 23 par->extradata = new_extradata;
145 23 par->extradata_size = new_size;
146
147
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
23 if ((ret = ffio_read_size(pb, par->extradata + previous_size, len)) < 0)
148 ✗ return ret;
149
150 23 return previous_size;
151 }
152
153 12 static int apng_read_header(AVFormatContext *s)
154 {
155 12 APNGDemuxContext *ctx = s->priv_data;
156 12 AVIOContext *pb = s->pb;
157 uint32_t len, tag;
158 AVStream *st;
159 12 int acTL_found = 0;
160 int64_t ret;
161
162 /* verify PNGSIG */
163
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (avio_rb64(pb) != PNGSIG)
164 ✗ return AVERROR_INVALIDDATA;
165
166 /* parse IHDR (must be first chunk) */
167 12 len = avio_rb32(pb);
168 12 tag = avio_rl32(pb);
169
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (len != 13 || tag != MKTAG('I', 'H', 'D', 'R'))
170 ✗ return AVERROR_INVALIDDATA;
171
172 12 st = avformat_new_stream(s, NULL);
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (!st)
174 ✗ return AVERROR(ENOMEM);
175
176 /* set the timebase to something large enough (1/100,000 of second)
177 * to hopefully cope with all sane frame durations */
178 12 avpriv_set_pts_info(st, 64, 1, 100000);
179 12 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
180 12 st->codecpar->codec_id = AV_CODEC_ID_APNG;
181 12 st->codecpar->width = avio_rb32(pb);
182 12 st->codecpar->height = avio_rb32(pb);
183
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if ((ret = av_image_check_size(st->codecpar->width, st->codecpar->height, 0, s)) < 0)
184 ✗ return ret;
185
186 /* extradata will contain every chunk up to the first fcTL (excluded) */
187 12 ret = ff_alloc_extradata(st->codecpar, len + 12);
188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (ret < 0)
189 ✗ return ret;
190 12 AV_WB32(st->codecpar->extradata, len);
191 12 AV_WL32(st->codecpar->extradata+4, tag);
192 12 AV_WB32(st->codecpar->extradata+8, st->codecpar->width);
193 12 AV_WB32(st->codecpar->extradata+12, st->codecpar->height);
194
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if ((ret = ffio_read_size(pb, st->codecpar->extradata + 16, 9)) < 0)
195 ✗ return ret;
196
197 while (1) {
198
4/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
35 if (acTL_found && ctx->num_play != 1) {
199 10 int64_t size = avio_size(pb);
200 10 int64_t offset = avio_tell(pb);
201
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 if (size < 0 || offset < 0 ||
202
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 (ret = ffio_ensure_seekback(pb, size - offset)) < 0) {
203 ✗ av_log(s, AV_LOG_WARNING, "Could not ensure seekback, will not loop\n");
204 ✗ ctx->num_play = 1;
205 }
206 }
207
4/4
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 10 times.
35 if ((ctx->num_play == 1 || !acTL_found) &&
208
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
25 ((ret = ffio_ensure_seekback(pb, 4 /* len */ + 4 /* tag */)) < 0))
209 ✗ return ret;
210
211 35 len = avio_rb32(pb);
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (len > INT_MAX - 12)
213 ✗ return AVERROR_INVALIDDATA;
214
215 35 tag = avio_rl32(pb);
216
3/3
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 11 times.
35 switch (tag) {
217 12 case MKTAG('a', 'c', 'T', 'L'):
218
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 ||
219
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 (ret = append_extradata(st->codecpar, pb, len + 12)) < 0)
220 ✗ return ret;
221 12 acTL_found = 1;
222 12 ctx->num_frames = AV_RB32(st->codecpar->extradata + ret + 8);
223 12 ctx->num_play = AV_RB32(st->codecpar->extradata + ret + 12);
224 12 av_log(s, AV_LOG_DEBUG, "num_frames: %"PRIu32", num_play: %"PRIu32"\n",
225 ctx->num_frames, ctx->num_play);
226 12 break;
227 12 case MKTAG('f', 'c', 'T', 'L'):
228
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (!acTL_found || len != APNG_FCTL_CHUNK_SIZE) {
229 ✗ return AVERROR_INVALIDDATA;
230 }
231
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0)
232 ✗ return ret;
233 12 return 0;
234 11 default:
235
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 ||
236
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 (ret = append_extradata(st->codecpar, pb, len + 12)) < 0)
237 ✗ return ret;
238 }
239 }
240 }
241
242 /* Out-of-order chunks are only reported: the sequence is resynchronized on the
243 * value actually read, so that every following chunk is not reported as well. */
244 3372 static void check_chunk_sequence(AVFormatContext *s, APNGDemuxContext *ctx,
245 const char *name, uint32_t sequence_number)
246 {
247 /* A seek can resume at any frame, so resynchronize before checking. */
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3372 times.
3372 if (s->io_repositioned) {
249 ✗ ctx->expected_seq = sequence_number;
250 ✗ s->io_repositioned = 0;
251 }
252
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3372 times.
3372 if (sequence_number != ctx->expected_seq)
254 ✗ av_log(s, AV_LOG_WARNING, "Invalid %s sequence number %"PRIu32
255 ", expected %"PRIu32"\n", name, sequence_number, ctx->expected_seq);
256
257 3372 ctx->expected_seq = sequence_number + 1;
258 3372 }
259
260 /* check the sequence number of an fdAT chunk: it is the first field of the
261 * chunk, which the caller has just appended at the end of the packet */
262 3036 static void check_fdat_chunk(AVFormatContext *s, APNGDemuxContext *ctx,
263 AVPacket *pkt, uint32_t len)
264 {
265 3036 check_chunk_sequence(s, ctx, "fdAT", AV_RB32(pkt->data + pkt->size - len - 4));
266 3036 }
267
268 336 static int decode_fctl_chunk(AVFormatContext *s, APNGDemuxContext *ctx)
269 {
270 uint32_t sequence_number, width, height, x_offset, y_offset;
271 uint16_t delay_num, delay_den;
272 uint8_t dispose_op, blend_op;
273
274 336 sequence_number = avio_rb32(s->pb);
275 336 width = avio_rb32(s->pb);
276 336 height = avio_rb32(s->pb);
277 336 x_offset = avio_rb32(s->pb);
278 336 y_offset = avio_rb32(s->pb);
279 336 delay_num = avio_rb16(s->pb);
280 336 delay_den = avio_rb16(s->pb);
281 336 dispose_op = avio_r8(s->pb);
282 336 blend_op = avio_r8(s->pb);
283 336 avio_skip(s->pb, 4); /* crc */
284
285 336 check_chunk_sequence(s, ctx, "fcTL", sequence_number);
286
287 /* default is hundredths of seconds */
288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
336 if (!delay_den)
289 ✗ delay_den = 100;
290
2/6
✓ Branch 0 taken 336 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 336 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
336 if (!delay_num || (ctx->max_fps && delay_den / delay_num > ctx->max_fps)) {
291 ✗ delay_num = 1;
292 ✗ delay_den = ctx->default_fps;
293 }
294 336 ctx->pkt_duration = av_rescale_q(delay_num,
295 336 (AVRational){ 1, delay_den },
296 336 s->streams[0]->time_base);
297
298 336 av_log(s, AV_LOG_DEBUG, "%s: "
299 "sequence_number: %"PRId32", "
300 "width: %"PRIu32", "
301 "height: %"PRIu32", "
302 "x_offset: %"PRIu32", "
303 "y_offset: %"PRIu32", "
304 "delay_num: %"PRIu16", "
305 "delay_den: %"PRIu16", "
306 "dispose_op: %d, "
307 "blend_op: %d\n",
308 __func__,
309 sequence_number,
310 width,
311 height,
312 x_offset,
313 y_offset,
314 delay_num,
315 delay_den,
316 dispose_op,
317 blend_op);
318
319
2/2
✓ Branch 0 taken 221 times.
✓ Branch 1 taken 115 times.
336 if (width != s->streams[0]->codecpar->width ||
320
2/4
✓ Branch 0 taken 221 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 221 times.
✗ Branch 3 not taken.
221 height != s->streams[0]->codecpar->height ||
321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
221 x_offset != 0 ||
322 y_offset != 0) {
323
1/2
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
115 if (sequence_number == 0 ||
324
1/2
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
115 x_offset >= s->streams[0]->codecpar->width ||
325
1/2
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
115 width > s->streams[0]->codecpar->width - x_offset ||
326
1/2
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
115 y_offset >= s->streams[0]->codecpar->height ||
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 115 times.
115 height > s->streams[0]->codecpar->height - y_offset)
328 ✗ return AVERROR_INVALIDDATA;
329 115 ctx->is_key_frame = 0;
330 } else {
331
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
221 if (sequence_number == 0 && dispose_op == APNG_DISPOSE_OP_PREVIOUS)
332 ✗ dispose_op = APNG_DISPOSE_OP_BACKGROUND;
333
4/4
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 219 times.
✓ Branch 3 taken 1 times.
221 ctx->is_key_frame = dispose_op == APNG_DISPOSE_OP_BACKGROUND ||
334 blend_op == APNG_BLEND_OP_SOURCE;
335 }
336
337 336 return 0;
338 }
339
340 359 static int apng_read_packet(AVFormatContext *s, AVPacket *pkt)
341 {
342 359 APNGDemuxContext *ctx = s->priv_data;
343 int64_t ret;
344 int64_t size;
345 359 AVIOContext *pb = s->pb;
346 uint32_t len, tag;
347
348 /*
349 * fcTL chunk length, in bytes:
350 * 4 (length)
351 * 4 (tag)
352 * 26 (actual chunk)
353 * 4 (crc) bytes
354 * and needed next:
355 * 4 (length)
356 * 4 (tag (must be fdAT or IDAT))
357 */
358 /* if num_play is not 1, then the seekback is already guaranteed */
359
3/4
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 110 times.
359 if (ctx->num_play == 1 && (ret = ffio_ensure_seekback(pb, 46)) < 0)
360 ✗ return ret;
361
362 359 len = avio_rb32(pb);
363 359 tag = avio_rl32(pb);
364
365
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 359 times.
359 if (avio_feof(pb))
366 ✗ return AVERROR_EOF;
367
368
2/3
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
359 switch (tag) {
369 336 case MKTAG('f', 'c', 'T', 'L'):
370
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
336 if (len != APNG_FCTL_CHUNK_SIZE)
371 359 return AVERROR_INVALIDDATA;
372
373
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 336 times.
336 if ((ret = decode_fctl_chunk(s, ctx)) < 0)
374 ✗ return ret;
375
376 /* fcTL may be followed by other chunks before fdAT or IDAT */
377 336 len = avio_rb32(pb);
378 336 tag = avio_rl32(pb);
379
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
336 if (len > 0x7fffffff)
380 ✗ return AVERROR_INVALIDDATA;
381
382 /* check for empty frame */
383
2/4
✓ Branch 0 taken 336 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 336 times.
336 if (tag == MKTAG('f', 'c', 'T', 'L') ||
384 tag == MKTAG('I', 'E', 'N', 'D'))
385 ✗ return AVERROR_INVALIDDATA;
386
387 336 size = 38 /* fcTL */ + 8 /* len, tag */ + len + 4 /* crc */;
388
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
336 if (size > INT_MAX)
389 ✗ return AVERROR(EINVAL);
390
391
1/2
✓ Branch 1 taken 336 times.
✗ Branch 2 not taken.
336 if ((ret = avio_seek(pb, -46, SEEK_CUR)) < 0 ||
392
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 336 times.
336 (ret = av_append_packet(pb, pkt, size)) < 0)
393 ✗ return ret;
394
395
2/2
✓ Branch 0 taken 324 times.
✓ Branch 1 taken 12 times.
336 if (tag == MKTAG('f', 'd', 'A', 'T')) {
396
2/4
✓ Branch 0 taken 324 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 324 times.
324 if (len < 4 || ret != size)
397 ✗ return AVERROR_INVALIDDATA;
398 324 check_fdat_chunk(s, ctx, pkt, len);
399 }
400
401
3/4
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 240 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 96 times.
336 if (ctx->num_play == 1 && (ret = ffio_ensure_seekback(pb, 8)) < 0)
402 ✗ return ret;
403
404 336 len = avio_rb32(pb);
405 336 tag = avio_rl32(pb);
406
2/2
✓ Branch 0 taken 2840 times.
✓ Branch 1 taken 324 times.
3500 while (tag &&
407
3/4
✓ Branch 0 taken 3164 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2828 times.
✓ Branch 3 taken 12 times.
6004 tag != MKTAG('f', 'c', 'T', 'L') &&
408 tag != MKTAG('I', 'E', 'N', 'D')) {
409
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2828 times.
2828 if (len > 0x7fffffff)
410 ✗ return AVERROR_INVALIDDATA;
411
1/2
✓ Branch 1 taken 2828 times.
✗ Branch 2 not taken.
2828 if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 ||
412
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2828 times.
2828 (ret = av_append_packet(pb, pkt, len + 12)) < 0)
413 ✗ return ret;
414
415
2/2
✓ Branch 0 taken 2712 times.
✓ Branch 1 taken 116 times.
2828 if (tag == MKTAG('f', 'd', 'A', 'T')) {
416
2/4
✓ Branch 0 taken 2712 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2712 times.
2712 if (len < 4 || ret != len + 12)
417 ✗ return AVERROR_INVALIDDATA;
418 2712 check_fdat_chunk(s, ctx, pkt, len);
419 }
420
421
3/4
✓ Branch 0 taken 2826 times.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2826 times.
2828 if (ctx->num_play == 1 && (ret = ffio_ensure_seekback(pb, 8)) < 0)
422 ✗ return ret;
423 2828 len = avio_rb32(pb);
424 2828 tag = avio_rl32(pb);
425 }
426
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 336 times.
336 if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0)
427 ✗ return ret;
428
429
2/2
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 116 times.
336 if (ctx->is_key_frame)
430 220 pkt->flags |= AV_PKT_FLAG_KEY;
431 336 pkt->pts = pkt->dts = AV_NOPTS_VALUE;
432 336 pkt->duration = ctx->pkt_duration;
433 336 return ret;
434 23 case MKTAG('I', 'E', 'N', 'D'):
435 23 ctx->cur_loop++;
436
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
23 if (ctx->ignore_loop || ctx->num_play >= 1 && ctx->cur_loop == ctx->num_play) {
437 23 avio_seek(pb, -8, SEEK_CUR);
438 23 return AVERROR_EOF;
439 }
440 ✗ ctx->expected_seq = 0;
441 ✗ if ((ret = avio_seek(pb, s->streams[0]->codecpar->extradata_size + 8, SEEK_SET)) < 0)
442 ✗ return ret;
443 ✗ return 0;
444 ✗ default:
445 ✗ avpriv_request_sample(s, "In-stream tag=%s (0x%08"PRIX32") len=%"PRIu32,
446 ✗ av_fourcc2str(tag), tag, len);
447 ✗ avio_skip(pb, len + 4);
448 }
449
450 /* Handle the unsupported yet cases */
451 ✗ return AVERROR_PATCHWELCOME;
452 }
453
454 static const AVOption options[] = {
455 { "ignore_loop", "ignore loop setting" , offsetof(APNGDemuxContext, ignore_loop),
456 AV_OPT_TYPE_BOOL, { .i64 = 1 } , 0, 1 , AV_OPT_FLAG_DECODING_PARAM },
457 { "max_fps" , "maximum framerate (0 is no limit)" , offsetof(APNGDemuxContext, max_fps),
458 AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
459 { "default_fps", "default framerate (0 is as fast as possible)", offsetof(APNGDemuxContext, default_fps),
460 AV_OPT_TYPE_INT, { .i64 = DEFAULT_APNG_FPS }, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
461 { NULL },
462 };
463
464 static const AVClass demuxer_class = {
465 .class_name = "APNG demuxer",
466 .item_name = av_default_item_name,
467 .option = options,
468 .version = LIBAVUTIL_VERSION_INT,
469 .category = AV_CLASS_CATEGORY_DEMUXER,
470 };
471
472 const FFInputFormat ff_apng_demuxer = {
473 .p.name = "apng",
474 .p.long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"),
475 .p.flags = AVFMT_GENERIC_INDEX,
476 .p.priv_class = &demuxer_class,
477 .priv_data_size = sizeof(APNGDemuxContext),
478 .read_probe = apng_probe,
479 .read_header = apng_read_header,
480 .read_packet = apng_read_packet,
481 };
482