| 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 |