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