From patchwork Sun Sep 12 20:20:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Reboredo X-Patchwork-Id: 30198 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2a4a:0:0:0:0 with SMTP id k10csp3423652iov; Sun, 12 Sep 2021 13:21:11 -0700 (PDT) X-Google-Smtp-Source: ABdhPJy5GlaPQdbLwSLmsOg4hSs1YXYAc9Phfy4v6GtXuyKHw4rgVR6Z/5ORA/wbeo0OOOEKTQSd X-Received: by 2002:a17:907:175d:: with SMTP id lf29mr4366719ejc.163.1631478070882; Sun, 12 Sep 2021 13:21:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1631478070; cv=none; d=google.com; s=arc-20160816; b=XG+Atr6tc9/AQFkICY1tHHSUR4SolUlDErcQoESY2h5g+flUWhQ1aNFAQ3/nk9Xgcc aT8HqIxacNbRA9pQOAxPUS8DurYW7v0H+wYT4HKaUvsg8dsc7XEgUX0C26yaP/Zog945 i+loEb/sDb+vQ+U5Tg6Fnr+4vrNh/iCi+m34M82hYE2YUrnagruVmzKKXCASIC7wEC7F 6zEmP0tXtndoWQYk7OkRGpS3V3rm+Wpx4dyaiHeOsmX04qhL7ax95N+88Nw827oq56aY 1EDTCUMRRFFfBvp5ykCI5FBOXg8v/HH1GQ8QwEP5rJcazObpSjXp0G19oIiI0Lw8jvyD Ahvw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=KU1E11EHOCjHPJ2KpmO307LdhyfKCBorI4bi3+8kMzw=; b=Kz8h3qaDFtGAdtPc/fa+QZSEp7c9GEI0E9ULxW8tB+MVdmD1tBgfsVDeZEyjn/QCB8 WGZNsg9XZPr+YSVGUUvhYMSQMtsTYdp91UIkR3y9ZeRreAIObL9jAge6C0qRjryxiXef Z6nmAci4VdOfv7jgHKAJjTMfou1cY/BgmxbjtnIp6MoMEiCltUUnCdGCbrI4/KvQfKPg ETgYlT8OUmjWClrvNmcaVU7mpo/gICTF3ZNp6OPPv8nL/h2aIZWvjynOdAxYXOeOkvzS EH06rpzMbksvsMyYon9ipsNkYiCSWWHWxask+jta03mk90izMdpt5Dp3ZeawldOGjGoA hikw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Jwp1iWxB; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id w22si4756002edc.482.2021.09.12.13.21.10; Sun, 12 Sep 2021 13:21:10 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Jwp1iWxB; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 44A9768AA2A; Sun, 12 Sep 2021 23:20:38 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ot1-f49.google.com (mail-ot1-f49.google.com [209.85.210.49]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AA6D168A9ED for ; Sun, 12 Sep 2021 23:20:31 +0300 (EEST) Received: by mail-ot1-f49.google.com with SMTP id k12-20020a056830150c00b0051abe7f680bso10470744otp.1 for ; Sun, 12 Sep 2021 13:20:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=+P8T1VuD/Uv/DJFyomkA3aAbh1XQ9Q82LT6nTc/LHv0=; b=Jwp1iWxBvKTwia6QZR3Xd+Pp5PslXB4PQmccPwwWEA/NcoVC4EHHagTC5cGcPR1+Es /nP5OdMk9cBEU5BHv9DTw6RvtSADNNPLO6yCWnpk+DkvVRE4A4BA6uvunEsyu9MCVZPM vLXq8vzPPBDd6pAun7p2PKWyRwh38VjoGiiNjjzCSV19IS+t1C/9CdLQHLMEgwlqzGhU UVNZrNfj2Qv95BJ7SkGZsMk/m2MG17KaR/7Iv5R0ox/eYJU6KONuwGd8DXcywW4k0aVJ 5tEaTOHLbxjbWyvqY/4boB0FOStwSsPuK9oYymQn0dCn3NlWWxfqL7kAm2J51U+3oKtq NUyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+P8T1VuD/Uv/DJFyomkA3aAbh1XQ9Q82LT6nTc/LHv0=; b=1bA6V83ubBlqFPqaJVM434pvpm0iRC4MJrimUhc9HKnmEHolRo5bOPjW6H4+63in1v MowMlLrc8p17rNECC48EIdNq8HW93jWvfjs/hBQlN6muDEnaIuuxZgKDCy0F4MHY2Jly 4FXv5ti+HuiSbaVPbZAM9ohtfkuSNy/CG+idRKfPrR3V0arlXtpOsaeVP9X1B5km+VJV wqk+L3w+CLLj5GI0ANuI1JtX+wOHxcd+zsHzRgqNnslGiNtz8iGEXCHeb9cUNwVIC5z9 TsCjVNGW7HM53EGQdTyUSlxwagDGOZ7ze93urOiAIsJ5GnmweepsaTnx3djAXvbS4urP 2xFw== X-Gm-Message-State: AOAM531QDXwQ3xAKXKxjVHwNJKHjbfKJKjleNsOcj+7/UIMy+u3aEoxp qrkWDZNfW+MVj311MyYmP2S5pcxmFjc= X-Received: by 2002:a05:6830:3114:: with SMTP id b20mr7259532ots.17.1631478030004; Sun, 12 Sep 2021 13:20:30 -0700 (PDT) Received: from localhost.localdomain (static.220.238.itcsa.net. [190.15.220.238]) by smtp.gmail.com with ESMTPSA id m24sm1206111oie.50.2021.09.12.13.20.28 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 12 Sep 2021 13:20:29 -0700 (PDT) From: Martin Reboredo To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Sep 2021 17:20:10 -0300 Message-Id: <20210912202010.1542872-5-yakoyoku@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210912202010.1542872-1-yakoyoku@gmail.com> References: <20210912202010.1542872-1-yakoyoku@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v3 4/4] avcodec/libwebpdec: libwebp decoder implementation X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Q8uhaNSJihPK Followup of the webp demuxer implementation. As the demuxer sends RIFF packets, the decoder choses what to do with the arriving chunks. Completely fixes #4907. Signed-off-by: Martin Reboredo --- configure | 4 +- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/libwebpdec.c | 419 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 libavcodec/libwebpdec.c diff --git a/configure b/configure index 98987ed186..73dc45fb0d 100755 --- a/configure +++ b/configure @@ -285,7 +285,7 @@ External library support: --enable-libvorbis enable Vorbis en/decoding via libvorbis, native implementation exists [no] --enable-libvpx enable VP8 and VP9 de/encoding via libvpx [no] - --enable-libwebp enable WebP encoding via libwebp [no] + --enable-libwebp enable WebP de/encoding via libwebp [no] --enable-libx264 enable H.264 encoding via x264 [no] --enable-libx265 enable HEVC encoding via x265 [no] --enable-libxavs enable AVS encoding via xavs [no] @@ -3314,6 +3314,7 @@ libvpx_vp8_decoder_deps="libvpx" libvpx_vp8_encoder_deps="libvpx" libvpx_vp9_decoder_deps="libvpx" libvpx_vp9_encoder_deps="libvpx" +libwebp_decoder_deps="libwebpdecoder" libwebp_encoder_deps="libwebp" libwebp_anim_encoder_deps="libwebp" libx262_encoder_deps="libx262" @@ -6518,6 +6519,7 @@ enabled libvpx && { } enabled libwebp && { + enabled libwebp_decoder && require_pkg_config libwebpdecoder "libwebpdecoder >= 0.2.0" webp/decode.h WebPGetDecoderVersion 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 libx264 && { check_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode || diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 11873eecae..81936b9828 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1062,6 +1062,7 @@ OBJS-$(CONFIG_LIBVPX_VP8_DECODER) += libvpxdec.o OBJS-$(CONFIG_LIBVPX_VP8_ENCODER) += libvpxenc.o OBJS-$(CONFIG_LIBVPX_VP9_DECODER) += libvpxdec.o libvpx.o OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.o libvpx.o +OBJS-$(CONFIG_LIBWEBP_DECODER) += libwebpdec.o OBJS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.o libwebpenc.o OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_animencoder.o OBJS-$(CONFIG_LIBX262_ENCODER) += libx264.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index c42aba140d..223f8bbf15 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -768,6 +768,7 @@ extern AVCodec ff_libvpx_vp9_decoder; /* preferred over libwebp */ extern const AVCodec ff_libwebp_anim_encoder; extern const AVCodec ff_libwebp_encoder; +extern const AVCodec ff_libwebp_decoder; extern const AVCodec ff_libx262_encoder; #if CONFIG_LIBX264_ENCODER #include diff --git a/libavcodec/libwebpdec.c b/libavcodec/libwebpdec.c new file mode 100644 index 0000000000..c583f919e0 --- /dev/null +++ b/libavcodec/libwebpdec.c @@ -0,0 +1,419 @@ +/* + * WebP decoding support via libwebp + * Copyright (c) 2021 Martin Reboredo + * + * 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 + * WebP decoder using libwebp (WebPDecode API) + */ + +#include +#include + +#include "decode.h" +#include "internal.h" +#include "libavutil/colorspace.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/error.h" +#include "libavutil/opt.h" + +struct WebPDecBgColor { + uint8_t y; + uint8_t u; + uint8_t v; + uint8_t a; +}; + +typedef struct LibWebPDecContext { + AVClass *class; // class for AVOptions + struct WebPDecBgColor bg_color; // background color for frame disposals + int loop; // number of times to loop all the pictures (0 means indefinitely) + int bypass_filter; // bypass filtering for decoded frames + int flip; // flip output images vertically + int dither_strength; // dithering strength applied to the images + int dispose; // dispose the previous frame with background color + int blend; // alpha blend with the previous frame + int chunk_unread; // chunk read is not yet complete + WebPDecoderConfig config; // libwebpdecoder configuration + AVFrame *frame; // current decoded frame + AVFrame *prev; // previously decoded frame + int prev_alpha; // previous frame had an alpha channel +} LibWebPDecContext; + +static int ff_libwebpdec_error_to_averror(int err) +{ + switch (err) { + case VP8_STATUS_OUT_OF_MEMORY: + return AVERROR(ENOMEM); + case VP8_STATUS_UNSUPPORTED_FEATURE: + case VP8_STATUS_BITSTREAM_ERROR: + case VP8_STATUS_INVALID_PARAM: + return AVERROR_INVALIDDATA; + case VP8_STATUS_NOT_ENOUGH_DATA: + return AVERROR(EAGAIN); + } + return AVERROR_UNKNOWN; +} + +static struct WebPDecBgColor ff_libwebpdec_bgra2yuv(int bgra) +{ + uint8_t r = (bgra >> 8) & 0xFF; + uint8_t g = (bgra >> 16) & 0xFF; + uint8_t b = bgra >> 24; + return (struct WebPDecBgColor) { + RGB_TO_Y_JPEG(r, g, b), + RGB_TO_U_JPEG(r, g, b), + RGB_TO_V_JPEG(r, g, b), + bgra & 0xFF, + }; +} + +static int ff_libwebpdec_parse_animation_frame(AVCodecContext *avctx, uint8_t *chunk) +{ + LibWebPDecContext *w = avctx->priv_data; + int flags = 0; + + flags = *(chunk + 23); + w->dispose = flags & 0x01; + w->blend = (flags & 0x02) == 0; + + return 0; +} + +// divide by 255 and round to nearest +// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16 +#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16) + +static void ff_libwebpdec_alpha_blend_frames(AVFrame *dst, const AVFrame *src, int alpha_bg) +{ + const uint8_t *y_src = src->data[0]; + const uint8_t *u_src = src->data[1]; + const uint8_t *v_src = src->data[2]; + const uint8_t *a_src = src->data[3]; + uint8_t *y_dst = dst->data[0]; + uint8_t *u_dst = dst->data[1]; + uint8_t *v_dst = dst->data[2]; + uint8_t *a_dst = dst->data[3]; + + for (int y = 0; y < src->height; y++) { + if ((y & 1) == 0) { + for (int x = 0; x < (src->width >> 1); x++) { + const uint8_t *a_bgp = (y + 1 == src->height) ? a_src : a_src + src->linesize[3]; + uint8_t *a_fgp = (y + 1 == src->height) ? a_dst : a_dst + dst->linesize[3]; + uint8_t a_bg = (alpha_bg) ? (a_src[2 * x] + a_src[2 * x + 1] + a_bgp[2 * x] + a_bgp[2 * x + 1]) >> 2 : 255; + uint8_t a_fg = (a_dst[2 * x] + a_dst[2 * x + 1] + a_fgp[2 * x] + a_fgp[2 * x + 1]) >> 2; + uint8_t out_uv_alpha = a_fg + FAST_DIV255((255 - a_fg) * a_bg); + + if (out_uv_alpha == 0) { + u_dst[x] = 0; + v_dst[x] = 0; + } else if (out_uv_alpha >= 255) { + u_dst[x] = FAST_DIV255(a_fg * u_dst[x] + (255 - a_fg) * u_src[x]); + v_dst[x] = FAST_DIV255(a_fg * v_dst[x] + (255 - a_fg) * v_src[x]); + } else { + u_dst[x] = (255 * a_fg * u_dst[x] + (255 - a_fg) * a_bg * u_src[x]) / (255 * out_uv_alpha); + v_dst[x] = (255 * a_fg * v_dst[x] + (255 - a_fg) * a_bg * v_src[x]) / (255 * out_uv_alpha); + } + } + u_src += src->linesize[1]; + v_src += src->linesize[2]; + u_dst += dst->linesize[1]; + v_dst += dst->linesize[2]; + } + for (int x = 0; x < src->width; x++) { + uint8_t a_bg = (alpha_bg) ? a_src[x] : 255; + uint8_t a_fg = a_dst[x]; + uint8_t out_y_alpha = a_fg + FAST_DIV255((255 - a_fg) * a_bg); + + if (out_y_alpha == 0) { + y_dst[x] = 0; + } else if (out_y_alpha == 255) { + y_dst[x] = FAST_DIV255(a_fg * y_dst[x] + (255 - a_fg) * y_src[x]); + } else { + y_dst[x] = (255 * a_fg * y_dst[x] + (255 - a_fg) * a_bg * y_src[x]) / (255 * out_y_alpha); + } + + a_dst[x] = out_y_alpha; + } + y_src += src->linesize[0]; + a_src += src->linesize[3]; + y_dst += dst->linesize[0]; + a_dst += dst->linesize[3]; + } +} + +static av_cold int libwebp_decode_init(AVCodecContext *avctx) +{ + LibWebPDecContext *s = avctx->priv_data; + int ret; + + if (!WebPInitDecoderConfig(&s->config)) { + return AVERROR_INVALIDDATA; + } + + s->config.options.bypass_filtering = s->bypass_filter; + s->config.options.dithering_strength = s->dither_strength; + s->config.options.alpha_dithering_strength = s->dither_strength; + s->config.options.flip = s->flip; + s->config.options.use_threads = avctx->thread_count; + + s->loop = -1; + s->chunk_unread = 0; + + s->frame = av_frame_alloc(); + s->prev = av_frame_alloc(); + if (s->frame == NULL || s->prev == NULL) { + av_frame_free(&s->frame); + av_frame_free(&s->prev); + return AVERROR(ENOMEM); + } + + ret = ff_get_buffer(avctx, s->frame, 0); + if (ret < 0) + return ret; + + s->frame->format = s->prev->format = avctx->pix_fmt; + s->frame->width = s->prev->width = avctx->width; + s->frame->height = s->prev->height = avctx->height; + + ret = av_frame_get_buffer(s->frame, 0); + if (ret < 0) + return ret; + + ret = av_frame_get_buffer(s->prev, 0); + if (ret < 0) + return ret; + + s->config.output.is_external_memory = 1; + s->config.output.width = avctx->width; + s->config.output.height = avctx->height; + + if (s->frame->format == AV_PIX_FMT_YUVA420P || s->frame->format == AV_PIX_FMT_YUV420P) { + s->config.output.u.YUVA.y = s->frame->data[0]; + s->config.output.u.YUVA.u = s->frame->data[1]; + s->config.output.u.YUVA.v = s->frame->data[2]; + s->config.output.u.YUVA.a = s->frame->data[3]; + s->config.output.u.YUVA.y_stride = s->frame->linesize[0]; + s->config.output.u.YUVA.u_stride = s->frame->linesize[1]; + s->config.output.u.YUVA.v_stride = s->frame->linesize[2]; + s->config.output.u.YUVA.a_stride = s->frame->linesize[3]; + s->config.output.u.YUVA.y_size = s->frame->linesize[0] * avctx->height; + s->config.output.u.YUVA.u_size = s->frame->linesize[1] * (avctx->height / 2); + s->config.output.u.YUVA.v_size = s->frame->linesize[2] * (avctx->height / 2); + s->config.output.u.YUVA.a_size = s->frame->linesize[3] * avctx->height; + if (s->frame->format == AV_PIX_FMT_YUVA420P) { + s->prev_alpha = 1; + s->config.output.colorspace = MODE_YUVA; + } else { + s->prev_alpha = 0; + s->config.output.colorspace = MODE_YUV; + } + } else { + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static void ff_libwebpdec_dispose_frame(AVCodecContext *avctx) +{ + LibWebPDecContext *s = avctx->priv_data; + + if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) { + memset(s->prev->data[0], s->bg_color.y, s->config.output.u.YUVA.y_size); + memset(s->prev->data[1], s->bg_color.u, s->config.output.u.YUVA.u_size); + memset(s->prev->data[2], s->bg_color.v, s->config.output.u.YUVA.v_size); + memset(s->prev->data[3], s->bg_color.a, s->config.output.u.YUVA.a_size); + } +} + +static int libwebp_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *pkt) +{ + LibWebPDecContext *s = avctx->priv_data; + AVFrame *picture = data; + uint8_t *chunk = pkt->data, *alpha_chunk = NULL; + int chunk_size = 0, alpha_chunk_size = 0, offset = 0; + int ret = 0, cont = 1, alpha = 0; + + if (s->dispose) { + ff_libwebpdec_dispose_frame(avctx); + } + + while (cont && ret >= 0) { + int skip = 1; + int fourcc = AV_RL32(chunk); + int size = AV_RL32(chunk + 4); + int padded_size = size + (size & 1); + chunk_size = padded_size + 8; + + cont = 0; + + switch (fourcc) { + case MKTAG('R', 'I', 'F', 'F'): + chunk_size = 12; + cont = 1; + break; + case MKTAG('V', 'P', '8', 'X'): + chunk_size = 18; + cont = 1; + break; + case MKTAG('A', 'N', 'I', 'M'): + if (s->loop == -1) { + s->bg_color = ff_libwebpdec_bgra2yuv(AV_RL32(chunk + 8)); + ff_libwebpdec_dispose_frame(avctx); + + s->loop = AV_RL16(chunk + 12); + } + + chunk_size = 14; + cont = 1; + break; + case MKTAG('A', 'N', 'M', 'F'): + ret = ff_libwebpdec_parse_animation_frame(avctx, chunk); + if (ret < 0) + return ret; + + chunk_size = 24; + if (s->chunk_unread) + return AVERROR_INVALIDDATA; + s->chunk_unread = 1; + cont = 1; + break; + case MKTAG('A', 'L', 'P', 'H'): + if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) + return AVERROR_INVALIDDATA; + if (pkt->size < offset + chunk_size) + return AVERROR(EAGAIN); + alpha_chunk = chunk; + alpha_chunk_size = chunk_size + AV_RL32(chunk + chunk_size + 4) + 8; + alpha = 1; + cont = 1; + break; + case MKTAG('V', 'P', '8', 'L'): + if (*(chunk + 12) & 0x10) { + if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) + return AVERROR_INVALIDDATA; + alpha_chunk = chunk; + alpha_chunk_size = chunk_size; + alpha = 1; + } + case MKTAG('V', 'P', '8', ' '): + s->config.output.colorspace = (alpha_chunk != NULL) ? MODE_YUVA : MODE_YUV; + s->chunk_unread = 0; + skip = 0; + break; + default: + cont = 1; + break; + } + + offset += chunk_size; + if (skip) + chunk += chunk_size; + + if (cont && offset > pkt->size) + return AVERROR(EAGAIN); + } + + if (alpha_chunk != NULL) { + chunk = alpha_chunk; + chunk_size = alpha_chunk_size; + } + ret = WebPDecode(chunk, chunk_size, &s->config); + if (ret != VP8_STATUS_OK) { + av_log(avctx, AV_LOG_ERROR, "WebPDecode() failed with error: %d\n", + ret); + + if (ret == VP8_STATUS_NOT_ENOUGH_DATA) + return AVERROR_INVALIDDATA; + + ret = ff_libwebpdec_error_to_averror(ret); + return ret; + } + + if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) { + if (!alpha) + memset(s->frame->data[3], 0xFF, s->config.output.u.YUVA.a_size); + if (s->blend) + ff_libwebpdec_alpha_blend_frames(s->frame, s->prev, s->prev_alpha); + } + + s->prev_alpha = alpha; + + ret = av_frame_copy(s->prev, s->frame); + if (ret < 0) + return ret; + + ret = av_frame_ref(picture, s->frame); + if (ret < 0) + return ret; + + *got_frame = 1; + + return pkt->size; +} + +static int libwebp_decode_close(AVCodecContext *avctx) +{ + LibWebPDecContext *s = avctx->priv_data; + av_frame_unref(s->frame); + av_frame_free(&s->frame); + av_frame_free(&s->prev); + + return 0; +} + +const enum AVPixelFormat ff_libwebpdec_pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_NONE +}; + +#define OFFSET(x) offsetof(LibWebPDecContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "bypass", "Bypass filter", OFFSET(bypass_filter), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VD }, + { "dither_strength", "Dithering strength", OFFSET(dither_strength), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, VD }, + { "flip", "Flip decoded pictures", OFFSET(flip), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VD }, + { NULL }, +}; + +const AVClass ff_libwebpdec_class = { + .class_name = "libwebp decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const AVCodec ff_libwebp_decoder = { + .name = "libwebp", + .long_name = NULL_IF_CONFIG_SMALL("libwebp WebP image"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_WEBP, + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = AV_CODEC_CAP_AUTO_THREADS, + .pix_fmts = ff_libwebpdec_pix_fmts, + .priv_class = &ff_libwebpdec_class, + .priv_data_size = sizeof(LibWebPDecContext), + .init = libwebp_decode_init, + .decode = libwebp_decode_frame, + .close = libwebp_decode_close, + .wrapper_name = "libwebp", +};