* [FFmpeg-devel] [PATCH] avcodec/libwebpdec: Add libwebp WebP decoder. (PR #20565)
@ 2025-09-20 23:45 rcombs via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: rcombs via ffmpeg-devel @ 2025-09-20 23:45 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: rcombs
PR #20565 opened by rcombs
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20565
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20565.patch
Adds support for decoding animated webp files.
This is a revised version of the patch from https://patchwork.ffmpeg.org/project/ffmpeg/patch/20250404011719.21654-1-aattyy@gmail.com/ with review comments applied and some other cleanup performed. The design is fairly clunky, so this is intended as a stopgap until a more complete implementation (with a dedicated demuxer and internal blend handling) can be completed.
Due to its reliance on the imgpipe demuxer, when using the ffmpeg command-line tool to convert an animated webp to another format using this decoder, the frame rate will be capped at the value specified at demux (by default this is 25). To decode at a higher frame rate, the user can specify `-framerate 1000` as a demuxer option, which will preserve the full millisecond precision of the WebP timestamps.
>From ea476d5551be455f2665998081f6a5819d6af1b1 Mon Sep 17 00:00:00 2001
From: Peter Xia <aattyy@gmail.com>
Date: Tue, 1 Apr 2025 03:29:02 -0700
Subject: [PATCH] avcodec/libwebpdec: Add libwebp WebP decoder.
Adds support of decoding animated webp.
Signed-off-by: Peter Xia <aattyy@gmail.com>
---
Changelog | 1 +
configure | 4 +-
doc/general_contents.texi | 2 +-
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/libwebpdec.c | 201 ++++++++++++++++++++++++++++++++++++++
libavcodec/version.h | 2 +-
7 files changed, 209 insertions(+), 3 deletions(-)
create mode 100644 libavcodec/libwebpdec.c
diff --git a/Changelog b/Changelog
index 88e1ce6659..5e9afc082f 100644
--- a/Changelog
+++ b/Changelog
@@ -5,6 +5,7 @@ version <next>:
- ffprobe -codec option
- EXIF Metadata Parsing
- gfxcapture: Windows.Graphics.Capture based window/monitor capture
+- WebP decoding via libwebp, including animation support
version 8.0:
diff --git a/configure b/configure
index 8def62a5d9..edd433c3c0 100755
--- a/configure
+++ b/configure
@@ -3697,6 +3697,7 @@ libvpx_vp9_encoder_deps="libvpx"
libvvenc_encoder_deps="libvvenc"
libwebp_encoder_deps="libwebp"
libwebp_anim_encoder_deps="libwebp"
+libwebp_decoder_deps="libwebp"
libx262_encoder_deps="libx262"
libx264_encoder_deps="libx264"
libx264_encoder_select="atsc_a53 golomb"
@@ -7260,7 +7261,8 @@ enabled libvpx && {
enabled libvvenc && require_pkg_config libvvenc "libvvenc >= 1.6.1" "vvenc/vvenc.h" vvenc_get_version
enabled libwebp && {
enabled libwebp_encoder && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion
- enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; }
+ enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit
+ enabled libwebp_decoder && check_pkg_config libwebp_decoder "libwebpdemux >= 1.5.0" webp/demux.h WebPAnimDecoderOptionsInitInternal; }
enabled libx264 && require_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode &&
require_cpp_condition libx264 x264.h "X264_BUILD >= 155" && {
[ "$toolchain" != "msvc" ] ||
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index b9dab580f1..a73d577ce2 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -859,7 +859,7 @@ following image formats are supported:
@item WBMP @tab X @tab X
@tab Wireless Application Protocol Bitmap image format
@item WebP @tab E @tab X
- @tab WebP image format, encoding supported through external library libwebp
+ @tab WebP image format, supported through external library libwebp
@item XBM @tab X @tab X
@tab X BitMap image format
@item XFace @tab X @tab X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index a321cda3a5..2c9ec72c71 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1202,6 +1202,7 @@ OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.o
OBJS-$(CONFIG_LIBVVENC_ENCODER) += libvvenc.o
OBJS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.o libwebpenc.o
OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_animencoder.o
+OBJS-$(CONFIG_LIBWEBP_DECODER) += libwebpdec.o
OBJS-$(CONFIG_LIBX262_ENCODER) += libx264.o
OBJS-$(CONFIG_LIBX264_ENCODER) += libx264.o
OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index f5ec2e01e8..5ff429b83b 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -817,6 +817,7 @@ extern const FFCodec ff_libvvenc_encoder;
/* preferred over libwebp */
extern const FFCodec ff_libwebp_anim_encoder;
extern const FFCodec ff_libwebp_encoder;
+extern const FFCodec ff_libwebp_decoder;
extern const FFCodec ff_libx262_encoder;
extern const FFCodec ff_libx264_encoder;
extern const FFCodec ff_libx264rgb_encoder;
diff --git a/libavcodec/libwebpdec.c b/libavcodec/libwebpdec.c
new file mode 100644
index 0000000000..b084a1bb03
--- /dev/null
+++ b/libavcodec/libwebpdec.c
@@ -0,0 +1,201 @@
+/*
+ * LibWebP decoder
+ * Copyright (c) 2025 Peter Xia
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * LibWebP decoder
+ */
+
+#include "decode.h"
+#include "codec_internal.h"
+#include "libavutil/avutil.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavformat/avformat.h"
+#include "libavformat/avio.h"
+
+#include <webp/demux.h>
+#include <webp/decode.h>
+
+typedef struct AnimatedWebPContext
+{
+ const AVClass *class;
+ WebPAnimDecoderOptions dec_options;
+ WebPAnimDecoder *dec;
+ WebPDemuxer *demuxer;
+ WebPIterator iter;
+ AVBufferRef *file_content;
+ WebPData webp_data; // references |file_content|
+ uint32_t loop_to_send;
+ uint32_t loop_sent;
+ int64_t duration;
+ int64_t frame_count;
+
+ // --- Options ---
+ int ignore_loop;
+} AnimatedWebPContext;
+
+// Initialize the decoder context
+static av_cold int decode_libwebp_init(AVCodecContext *avctx)
+{
+ AnimatedWebPContext *s = avctx->priv_data;
+
+ if (!WebPAnimDecoderOptionsInit(&s->dec_options))
+ return AVERROR_EXTERNAL;
+
+ s->dec_options.color_mode = MODE_RGBA;
+ s->dec_options.use_threads = 1;
+ s->file_content = NULL;
+ s->loop_sent = 0;
+
+ avctx->pix_fmt = AV_PIX_FMT_RGBA;
+ avctx->pkt_timebase = av_make_q(1, 1000);
+ avctx->framerate = av_make_q(1, 0);
+
+ return 0; // Success
+}
+
+// Decode one frame of the animated WebP
+// The first call receives the AVPacket with the full WebP file.
+// Subsequent calls receive empty AVPacket until all frames are decoded.
+static int decode_libwebp_frame(AVCodecContext *avctx, AVFrame *p,
+ int *got_frame, AVPacket *avpkt)
+{
+ WebPAnimInfo anim_info;
+ uint8_t *image_data[4] = {NULL};
+ int linesizes[4] = {0};
+ int timestamp_ms;
+
+ AnimatedWebPContext *s = avctx->priv_data;
+ int ret = avpkt->size;
+
+ // Initialization Phase (First Call)
+ // |avpkt| contains the entire file.
+ if (!s->dec) {
+ if (!avpkt || avpkt->size <= 0) {
+ // Should not happen on the first call, but check anyway.
+ av_log(avctx, AV_LOG_ERROR, "No input data provided on first call.\n");
+ return AVERROR(EINVAL);
+ }
+
+ // Store entire WebP file in memory.
+ s->file_content = av_buffer_ref(avpkt->buf);
+ if (!s->file_content)
+ return AVERROR(ENOMEM);
+
+ s->webp_data.bytes = s->file_content->data;
+ s->webp_data.size = s->file_content->size;
+
+ s->demuxer = WebPDemux(&s->webp_data);
+ if (!s->demuxer)
+ return AVERROR_EXTERNAL;
+
+ if (!WebPDemuxGetFrame(s->demuxer, 1, &s->iter))
+ return AVERROR_EXTERNAL;
+
+ do {
+ s->duration += s->iter.duration;
+ s->frame_count++;
+ } while (WebPDemuxNextFrame(&s->iter));
+
+ av_reduce(&avctx->framerate.num, &avctx->framerate.den, s->frame_count * 1000, s->duration, 1000);
+
+ s->dec = WebPAnimDecoderNew(&s->webp_data, &s->dec_options);
+ if (!s->dec)
+ return AVERROR_EXTERNAL;
+
+ WebPAnimDecoderGetInfo(s->dec, &anim_info);
+
+ s->loop_to_send = s->ignore_loop ? 1 : anim_info.loop_count;
+ ff_set_dimensions(avctx, anim_info.canvas_width, anim_info.canvas_height);
+
+ avctx->pix_fmt = AV_PIX_FMT_RGBA;
+ }
+
+ if (!WebPAnimDecoderHasMoreFrames(s->dec)) {
+ s->loop_sent++;
+ WebPAnimDecoderReset(s->dec);
+ }
+
+ if (s->loop_sent >= s->loop_to_send) {
+ av_log(avctx, AV_LOG_DEBUG, "End of animated WebP stream.\n");
+ return AVERROR_EOF;
+ }
+
+ if (!WebPAnimDecoderGetNext(s->dec, &image_data[0], ×tamp_ms)) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting next frame from WebPAnimDecoder.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ret = ff_decode_frame_props(avctx, p);
+ if (ret < 0)
+ return ret;
+
+ ret = ff_get_buffer(avctx, p, 0);
+ if (ret < 0)
+ return AVERROR(ENOMEM);
+
+ p->pts = av_rescale_q(timestamp_ms, avctx->pkt_timebase, (AVRational){1, 1000});
+ p->pict_type = AV_PICTURE_TYPE_I;
+
+ linesizes[0] = avctx->width * 4;
+ av_image_copy2(p->data, p->linesize, image_data, linesizes, p->format, p->width, p->height);
+
+ *got_frame = 1;
+ return ret;
+}
+
+static av_cold int decode_libwebp_close(AVCodecContext *avctx)
+{
+ AnimatedWebPContext *s = avctx->priv_data;
+ av_buffer_unref(&s->file_content);
+ if (s->dec) {
+ WebPAnimDecoderDelete(s->dec);
+ s->dec = NULL;
+ }
+ return 0;
+}
+
+static const AVOption options[] = {
+ {"ignore_loop", "ignore loop setting", offsetof(AnimatedWebPContext, ignore_loop), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM},
+ {NULL}};
+
+static const AVClass libwebp_decoder_class = {
+ .class_name = "libwebp_decoder",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DECODER,
+};
+
+const FFCodec ff_libwebp_decoder = {
+ .p.name = "libwebp",
+ CODEC_LONG_NAME("libwebp image/animation decoder"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_WEBP,
+ .p.priv_class = &libwebp_decoder_class,
+ .priv_data_size = sizeof(AnimatedWebPContext),
+ .p.wrapper_name = "libwebp",
+ .init = decode_libwebp_init,
+ FF_CODEC_DECODE_CB(decode_libwebp_frame),
+ .close = decode_libwebp_close,
+ .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 9b8c267529..82a86fe9d9 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 15
+#define LIBAVCODEC_VERSION_MINOR 16
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
--
2.49.1
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-09-20 23:46 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-20 23:45 [FFmpeg-devel] [PATCH] avcodec/libwebpdec: Add libwebp WebP decoder. (PR #20565) rcombs via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git