From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 1302848D6F for ; Sat, 20 Sep 2025 23:46:29 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'1UBqtT9sFxUBvdN9SHgxXdxYEvG7u9nkGibUDz8LCUo=', expected b'wX7bKgb1Lk+zOnNEupzXU2w6G2hnnf51QMxj6SHCqCM=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1758411948; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=1UBqtT9sFxUBvdN9SHgxXdxYEvG7u9nkGibUDz8LCUo=; b=4Wkz65mWA0gBTeowHbw0638h4gxRx1E47dAkkSiKjQMDH70f72X2M12BamuQCbsCIVneQ euhtrSNYLm+EAqA73boLAHP7rIVCVICPAnVhRoapejCLRfUiZqJCf1fmxWImTnmA9UiIXqO 4UN4fIYRbTYsDrrMsbEDvv8dQnkX3+IH3RJc+wWQB6B0agV9qdAGDCs08mC5fxOvtvl+0a8 hhPVZcQI5qfGeAZQE9tR/AEzgKby5Ieu7IVvzdCKKIEntnF5WAduSe7upO30DS+k5gb9w/A P/icvcyAV55Zm51vuIagtNatWvQIRsEAjlZGUqiZBZTfA3cisPzpZAyby+mA== Received: from [172.19.0.4] (unknown [172.19.0.4]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 0C90968E844; Sun, 21 Sep 2025 02:45:48 +0300 (EEST) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1758411945; b=lRSjiLGyDCRfbp3hTClZGA9N2DN6Iys7s3jXhpK7QzsOYk2XPmLy3irNr9wO4MbgS2Nvc Vj9Rt+O4n6uFWdDZh2HkNUumHKVOR1tUh5IoPDVbQqkShdlOih6lKjJsqV8AKXOn2TS6f0f 9ojd/KpH/YotVaDXT6mYBCHm7kGA0uUSMEkQZLIvCY6dmVZlBzHJABSE+1u4Au7BnDvVKLe dsbjJp1yIa1FBObSH0jovredlXECd3L5RNyLZQhkD4A2ZaaqDDuEXoX2O7Gsqpl1I+EP9J6 aWWP3PAHJBP6N6sQ6zt88Y0uqMhL93qBACK/iWWTZsv+P7lsnvWUojEwB8lA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1758411945; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=v/tHD+aKG2Fq8dptrV7eURHE1/0bc/rKXeCYV9R5B14=; b=imcCv8JsTYMOMtqg9EJr+yIX0MRsfCH1MrUOUE0K7FF7K9z60hlPmxInmtMn9B4MXODH5 wXNuI8wvLop/iMMfaa7NYv+0ymeq4wcdax3HWTrQcTIsAvXp2J3QCkNKLJeYtWYqnBYltYd TLTsqmHzs86RvDyE+eivJyrBOd/lvWTg1naFWOyn6p8H+e2XjWwGyTUh3PwnRSNC86n7lyl AybFxgEZVE+6O1rdmn+b2FyiRvt4HnT0gOzV4bELAtnBeeCQdwDkAJ3GfXMi2J0izkttvuo y9EUE5qrCXG9AfdUGBw0lt1c08tc421IIuwGDkqKsEDUqUi0+bVnTJ60VxDg== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=none Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1758411931; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=wX7bKgb1Lk+zOnNEupzXU2w6G2hnnf51QMxj6SHCqCM=; b=Mw7nuddcqqBNTElO+Zn+neTO79rs2dISqAoLfRtgjb1lGsweWDQ3zj8N1FgIUI5OgGLI2 pihiZMxj4d708TzPIV6SUF5onenU3b6OpBNFS/ffQh34dEBuqXOnA0+3k+grpQZohKjfQrw InJt2QA8jQZdK770Ir0oO3VcYQ+Jq6WUcNCpN9WtyVvw8znnMLEBwxdPhJiwKnwOKeRMqfC aqANP5ejiI3LT9FkKs9gJn2N90ZgAR7MXnxRJWmsRJef8wIYkGXcREcgQyTJqDgrBTcBnqq cYa+cDIteFhY2v1JyoplxVNKY/azvD29oqd21Fplmib/nuB5Y9krGHmDxpvw== Received: from ed19c606a818 (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id EDD2A68E83A for ; Sun, 21 Sep 2025 02:45:30 +0300 (EEST) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Sat, 20 Sep 2025 23:45:30 -0000 Message-ID: <175841193129.25.18188661179341207628@463a07221176> Message-ID-Hash: GDFD2UAHDCQ5DJPYGHSZUASQKMIRQ5EF X-Message-ID-Hash: GDFD2UAHDCQ5DJPYGHSZUASQKMIRQ5EF X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] avcodec/libwebpdec: Add libwebp WebP decoder. (PR #20565) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: rcombs via ffmpeg-devel Cc: rcombs Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: 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 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 --- 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 : - 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 +#include + +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