FFmpeg
Loading...
Searching...
No Matches
webp_anim_dec.c
Go to the documentation of this file.
1/*
2 * Animated WebP demuxer
3 * Copyright (c) 2020 Pexeso Inc.
4 * Copyright (c) 2026 Ramiro Polla
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23/**
24 * @file
25 * Animated WebP demuxer.
26 */
27
28#include "avio_internal.h"
29#include "demux.h"
30#include "avformat.h"
31#include "internal.h"
33#include "libavutil/mem.h"
34#include "libavutil/opt.h"
35
36#define VP8X_FLAG_ANIMATION 0x02
37#define VP8X_FLAG_XMP_METADATA 0x04
38#define VP8X_FLAG_EXIF_METADATA 0x08
39#define VP8X_FLAG_ALPHA 0x10
40#define VP8X_FLAG_ICC 0x20
41
42typedef struct WebPAnimDemuxContext {
43 const AVClass *class;
44
45 /**
46 * Minimum allowed delay between frames in milliseconds.
47 * Values below this threshold are considered to be invalid
48 * and set to value of default_delay.
49 */
53
54 /*
55 * loop options
56 */
57 int ignore_loop; ///< ignore loop setting
58 int loop_count; ///< number of times to loop the animation
59 int cur_loop; ///< current loop counter
60
61 /*
62 * variables for the key frame detection
63 */
64 int cur_frame; ///< number of frames of the current animation file
66
71
73
76
77/**
78 * Major web browsers display WebPs at ~10-15fps when rate is not
79 * explicitly set or have too low values. We assume default rate to be 10.
80 * Default delay = 1000 microseconds / 10fps = 100 milliseconds per frame.
81 */
82#define WEBP_DEFAULT_DELAY 100
83/**
84 * By default delay values less than this threshold considered to be invalid.
85 */
86#define WEBP_MIN_DELAY 10
87
88static int webp_anim_probe(const AVProbeData *p)
89{
90 const uint8_t *b = p->buf;
91
92 if (AV_RL32(b) == MKTAG('R', 'I', 'F', 'F') &&
93 AV_RL32(b + 8) == MKTAG('W', 'E', 'B', 'P') &&
94 AV_RL32(b + 12) == MKTAG('V', 'P', '8', 'X') &&
95 AV_RL32(b + 16) == 10 &&
96 (b[20] & VP8X_FLAG_ANIMATION))
97 return AVPROBE_SCORE_MAX;
98
99 return 0;
100}
101
103{
104 WebPAnimDemuxContext *ctx = s->priv_data;
105 AVIOContext *pb = s->pb;
106 int ret;
107
108 /* Check for signature. */
109 if (avio_rl32(pb) != MKTAG('R', 'I', 'F', 'F'))
110 return AVERROR_INVALIDDATA;
111 avio_skip(pb, 4); /* file size */
112 if (avio_rl32(pb) != MKTAG('W', 'E', 'B', 'P'))
113 return AVERROR_INVALIDDATA;
114
115 /* VP8X must be first chunk */
116 if (avio_rl32(pb) != MKTAG('V', 'P', '8', 'X') ||
117 avio_rl32(pb) != 10 /* chunk size */)
118 return AVERROR_INVALIDDATA;
119 ctx->vp8x_flags = avio_r8(pb);
120 if (!(ctx->vp8x_flags & VP8X_FLAG_ANIMATION))
121 return AVERROR_INVALIDDATA;
122 avio_skip(pb, 3);
123
125 if (!st)
126 return AVERROR(ENOMEM);
127
128 avpriv_set_pts_info(st, 64, 1, 1000);
132 st->codecpar->width = avio_rl24(pb) + 1;
133 st->codecpar->height = avio_rl24(pb) + 1;
134 st->start_time = 0;
135
136 int explode = (s->error_recognition & AV_EF_EXPLODE);
137 int loglevel = explode ? AV_LOG_ERROR : AV_LOG_WARNING;
138 while (1) {
140 uint32_t fourcc = avio_rl32(pb);
141 uint32_t size = avio_rl32(pb);
142
143 av_log(s, AV_LOG_DEBUG, "Chunk %s of size %u at offset %" PRId64 "\n",
145
146 if (size == UINT32_MAX)
147 return AVERROR_INVALIDDATA;
148 size += size & 1;
149
150 if (avio_feof(pb))
151 break;
152
153 switch (fourcc) {
154 case MKTAG('I', 'C', 'C', 'P'):
155 if (ctx->has_iccp) {
156 av_log(s, loglevel, "Extra ICCP chunk found\n");
157 if (explode)
158 return AVERROR_INVALIDDATA;
159 avio_skip(pb, size);
160 } else {
161 if (!(ctx->vp8x_flags & VP8X_FLAG_ICC)) {
162 av_log(s, loglevel,
163 "ICCP chunk present, but ICC Profile bit not set in the VP8X header\n");
164 if (explode)
165 return AVERROR_INVALIDDATA;
166 }
167
171 if (!sd)
172 return AVERROR(ENOMEM);
173 ret = ffio_read_size(pb, sd->data, size);
174 if (ret < 0) {
176 return ret;
177 }
178 ctx->has_iccp = 1;
179 }
180 break;
181 case MKTAG('A', 'N', 'I', 'M'):
182 if (ctx->has_anim) {
183 av_log(s, loglevel, "Extra ANIM chunk found\n");
184 if (explode)
185 return AVERROR_INVALIDDATA;
186 avio_skip(pb, size);
187 } else {
188 if (size != 6)
189 return AVERROR_INVALIDDATA;
190 uint32_t bg_color = avio_rb32(pb);
191 ctx->loop_count = avio_rl16(pb);
192 if (ctx->usebgcolor) {
194 if (!st->codecpar->extradata)
195 return AVERROR(ENOMEM);
196 AV_WB32(st->codecpar->extradata, bg_color);
197 st->codecpar->extradata_size = 4;
198 }
200 "ANIM: background BGRA 0x%08x loop count %d\n",
201 bg_color, ctx->loop_count);
202 ctx->has_anim = 1;
203 }
204 break;
205 case MKTAG('A', 'N', 'M', 'F'):
206 if (!ctx->has_anim) {
207 av_log(s, loglevel,
208 "ANMF chunk present, but no previous ANIM chunk found\n");
209 if (explode)
210 return AVERROR_INVALIDDATA;
211 ctx->loop_count = 1;
212 } else if (!ctx->ignore_loop && ctx->loop_count != 1) {
213 int64_t file_size = avio_size(pb);
214 if (file_size < 0 || offset < 0 ||
215 (ret = ffio_ensure_seekback(pb, file_size - offset)) < 0) {
217 "Could not ensure seekback, will not loop\n");
218 ctx->loop_count = 1;
219 }
220 }
221 ctx->first_anmf_offset = offset;
222 ret = avio_seek(pb, -8, SEEK_CUR);
223 if (ret < 0)
224 return ret;
225 return 0;
226 default:
227 av_log(s, AV_LOG_WARNING, "Skipping chunk: %s\n", av_fourcc2str(fourcc));
228 avio_skip(pb, size);
229 break;
230 }
231 }
232
233 return AVERROR_INVALIDDATA;
234}
235
237{
238 WebPAnimDemuxContext *ctx = s->priv_data;
239 AVIOContext *pb = s->pb;
240 int ret;
241
242 int explode = (s->error_recognition & AV_EF_EXPLODE);
243 int loglevel = explode ? AV_LOG_ERROR : AV_LOG_WARNING;
244 while (1) {
246 uint32_t fourcc = avio_rl32(pb);
247 uint32_t size = avio_rl32(pb);
248
249 if (size == UINT32_MAX)
250 return AVERROR_INVALIDDATA;
251 size += size & 1;
252
253 if (avio_feof(pb)) {
254 if (!ctx->ignore_loop &&
255 (ctx->loop_count == 0 || ++ctx->cur_loop < ctx->loop_count)) {
256 ctx->cur_frame = 0;
257 ret = avio_seek(pb, ctx->first_anmf_offset, SEEK_SET);
258 if (ret < 0)
259 return ret;
260 continue;
261 }
262 break;
263 }
264
265 av_log(s, AV_LOG_DEBUG, "Chunk %s of size %u at offset %" PRId64 "\n",
267
268 switch (fourcc) {
269 case MKTAG('A', 'N', 'M', 'F'):
270 if (size < 16)
271 return AVERROR_INVALIDDATA;
272 ret = av_get_packet(pb, pkt, size);
273 if (ret < 0)
274 return ret;
275 if (!ctx->cur_frame++)
276 pkt->flags |= AV_PKT_FLAG_KEY;
277 pkt->pts = AV_NOPTS_VALUE;
278 pkt->dts = AV_NOPTS_VALUE;
279 uint32_t duration = AV_RL24(pkt->data + 12);
280 if (duration <= ctx->min_delay)
281 duration = ctx->default_delay;
282 pkt->duration = FFMIN(duration, ctx->max_delay);
283 return ret;
284 case MKTAG('E', 'X', 'I', 'F'):
285 if (ctx->has_exif) {
286 av_log(s, loglevel, "Extra EXIF chunk found\n");
287 if (explode)
288 return AVERROR_INVALIDDATA;
289 avio_skip(pb, size);
290 } else {
291 if (!(ctx->vp8x_flags & VP8X_FLAG_EXIF_METADATA)) {
292 av_log(s, loglevel,
293 "EXIF chunk present, but EXIF bit not set in the VP8X header\n");
294 if (explode)
295 return AVERROR_INVALIDDATA;
296 }
297
298 AVStream *st = s->streams[0];
302 if (!sd)
303 return AVERROR(ENOMEM);
304 ret = ffio_read_size(pb, sd->data, size);
305 if (ret < 0) {
307 return ret;
308 }
309 ctx->has_exif = 1;
310 }
311 break;
312 case MKTAG('X', 'M', 'P', ' '):
313 if (ctx->has_xmp) {
314 av_log(s, loglevel, "Extra XMP chunk found\n");
315 if (explode)
316 return AVERROR_INVALIDDATA;
317 avio_skip(pb, size);
318 } else {
319 if (!(ctx->vp8x_flags & VP8X_FLAG_XMP_METADATA)) {
320 av_log(s, loglevel,
321 "XMP chunk present, but XMP bit not set in the VP8X header\n");
322 if (explode)
323 return AVERROR_INVALIDDATA;
324 }
325
326 uint8_t *xmp = av_malloc(size + 1);
327 if (!xmp)
328 return AVERROR(ENOMEM);
329 ret = ffio_read_size(pb, xmp, size);
330 if (ret < 0) {
331 av_free(xmp);
332 return ret;
333 }
334 xmp[size] = '\0';
335 av_dict_set(&s->metadata, "xmp", xmp, AV_DICT_DONT_STRDUP_VAL);
336 ctx->has_xmp = 1;
337 }
338 break;
339 default:
340 av_log(s, AV_LOG_WARNING, "Skipping chunk: %s\n", av_fourcc2str(fourcc));
341 avio_skip(pb, size);
342 break;
343 }
344 }
345
346 return AVERROR_EOF;
347}
348
349static const AVOption options[] = {
350 { "min_delay", "minimum valid delay between frames (in milliseconds)", offsetof(WebPAnimDemuxContext, min_delay), AV_OPT_TYPE_INT, {.i64 = WEBP_MIN_DELAY}, 0, 1000 * 60, AV_OPT_FLAG_DECODING_PARAM },
351 { "max_webp_delay", "maximum valid delay between frames (in milliseconds)", offsetof(WebPAnimDemuxContext, max_delay), AV_OPT_TYPE_INT, {.i64 = 0xffffff}, 0, 0xffffff, AV_OPT_FLAG_DECODING_PARAM },
352 { "default_delay", "default delay between frames (in milliseconds)", offsetof(WebPAnimDemuxContext, default_delay), AV_OPT_TYPE_INT, {.i64 = WEBP_DEFAULT_DELAY}, 0, 1000 * 60, AV_OPT_FLAG_DECODING_PARAM },
353 { "ignore_loop", "ignore loop setting", offsetof(WebPAnimDemuxContext, ignore_loop), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
354 { "usebgcolor", "use background color from ANIM chunk", offsetof(WebPAnimDemuxContext, usebgcolor), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
355 { NULL },
356};
357
358static const AVClass demuxer_class = {
359 .class_name = "Animated WebP demuxer",
360 .item_name = av_default_item_name,
361 .option = options,
362 .version = LIBAVUTIL_VERSION_INT,
363 .category = AV_CLASS_CATEGORY_DEMUXER,
364};
365
367 .p.name = "webp_anim",
368 .p.long_name = NULL_IF_CONFIG_SMALL("Animated WebP"),
369 .p.flags = AVFMT_GENERIC_INDEX,
370 .p.priv_class = &demuxer_class,
371 .priv_data_size = sizeof(WebPAnimDemuxContext),
375};
const FFInputFormat ff_webp_anim_demuxer
static AVFormatContext * ctx
static const AVClass demuxer_class
Definition apngdec.c:418
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
Main libavformat public API header.
#define AVPROBE_SCORE_MAX
maximum score
Definition avformat.h:483
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
Allocate and read the payload of a packet and initialize its fields with default values.
Definition utils.c:98
#define AVFMT_GENERIC_INDEX
Use generic index building code.
Definition avformat.h:499
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition aviobuf.c:236
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition aviobuf.c:326
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition aviobuf.c:349
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition avio.h:494
unsigned int avio_rl16(AVIOContext *s)
Definition aviobuf.c:717
int64_t avio_skip(AVIOContext *s, int64_t offset)
Skip given number of bytes forward.
Definition aviobuf.c:321
unsigned int avio_rl32(AVIOContext *s)
Definition aviobuf.c:733
unsigned int avio_rl24(AVIOContext *s)
Definition aviobuf.c:725
unsigned int avio_rb32(AVIOContext *s)
Definition aviobuf.c:764
int avio_r8(AVIOContext *s)
Definition aviobuf.c:606
int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size)
Ensures that the requested seekback buffer size will be available.
Definition aviobuf.c:1026
int ffio_read_size(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition aviobuf.c:665
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
#define s(width, name)
Definition cbs_vp9.c:198
static int read_probe(const AVProbeData *p)
Definition cdg.c:30
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
#define AV_EF_EXPLODE
abort decoding on minor error detection
Definition defs.h:51
static AVPacket * pkt
static int64_t duration
Definition ffplay.c:330
static int read_header(FFV1Context *f, RangeCoder *c)
Definition ffv1dec.c:578
#define AV_OPT_FLAG_DECODING_PARAM
A generic parameter which can be set by the user for demuxing or decoding.
Definition opt.h:355
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ AV_CODEC_ID_WEBP_ANIM
Definition codec_id.h:326
#define AV_INPUT_BUFFER_PADDING_SIZE
Required number of additionally allocated bytes at the end of the input bitstream for decoding.
Definition defs.h:40
AVPacketSideData * av_packet_side_data_new(AVPacketSideData **psd, int *pnb_sd, enum AVPacketSideDataType type, size_t size, int flags)
Allocate a new packet side data.
Definition packet.c:620
void av_packet_side_data_remove(AVPacketSideData *sd, int *pnb_sd, enum AVPacketSideDataType type)
Remove side data of the given type from a side data array.
Definition packet.c:642
@ AV_PKT_DATA_ICC_PROFILE
ICC profile data consisting of an opaque octet buffer following the format described by ISO 15076-1.
Definition packet.h:271
@ AV_PKT_DATA_EXIF
Extensible image file format metadata.
Definition packet.h:369
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition packet.h:650
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that's been allocated with av_malloc() or another memory allocation functio...
Definition dict.h:79
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition dict.c:86
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR_EOF
End of file.
Definition error.h:57
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
#define av_fourcc2str(fourcc)
Definition avutil.h:323
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition avutil.h:247
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define b
Definition input.c:43
#define AV_RL24(x)
#define AV_WB32(p, v)
#define AV_RL32(p)
unsigned offset
Definition libaomenc.c:763
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
@ AV_CLASS_CATEGORY_DEMUXER
Definition log.h:33
#define FFMIN(a, b)
Definition macros.h:49
#define MKTAG(a, b, c, d)
Definition macros.h:55
Memory handling functions.
#define av_malloc(s)
Definition ops_static.c:52
AVOptions.
@ AV_PIX_FMT_ARGB
packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
Definition pixfmt.h:99
Describe the class of an AVClass context structure.
Definition log.h:76
int extradata_size
Size of the extradata content in bytes.
Definition codec_par.h:75
int height
The height of the video frame in pixels.
Definition codec_par.h:150
int nb_coded_side_data
Amount of entries in coded_side_data.
Definition codec_par.h:88
int width
The width of the video frame in pixels.
Definition codec_par.h:143
enum AVMediaType codec_type
General type of the encoded data.
Definition codec_par.h:53
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition codec_par.h:71
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition codec_par.h:57
AVPacketSideData * coded_side_data
Additional data associated with the entire stream.
Definition codec_par.h:83
Format I/O context.
Definition avformat.h:1333
Bytestream IO Context.
Definition avio.h:160
AVOption.
Definition opt.h:428
This structure stores auxiliary information for decoding, presenting, or otherwise processing the cod...
Definition packet.h:424
uint8_t * data
Definition packet.h:425
This structure stores compressed data.
Definition packet.h:580
This structure contains the data a format has to probe a file.
Definition avformat.h:471
Stream structure.
Definition avformat.h:766
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:789
int64_t start_time
Decoding: pts of the first frame of the stream in presentation order, in stream time base.
Definition avformat.h:815
int cur_frame
number of frames of the current animation file
int cur_loop
current loop counter
int min_delay
Minimum allowed delay between frames in milliseconds.
int loop_count
number of times to loop the animation
int ignore_loop
ignore loop setting
#define av_free(p)
#define av_mallocz(s)
#define av_log(a,...)
int size
uint32_t fourcc
#define VP8X_FLAG_ICC
Definition webp.c:67
#define VP8X_FLAG_EXIF_METADATA
Definition webp.c:65
#define VP8X_FLAG_ANIMATION
Definition webp.c:63
#define VP8X_FLAG_XMP_METADATA
Definition webp.c:64
static int webp_anim_probe(const AVProbeData *p)
#define WEBP_DEFAULT_DELAY
Major web browsers display WebPs at ~10-15fps when rate is not explicitly set or have too low values.
static int webp_anim_read_header(AVFormatContext *s)
static int webp_anim_read_packet(AVFormatContext *s, AVPacket *pkt)
#define WEBP_MIN_DELAY
By default delay values less than this threshold considered to be invalid.