From patchwork Thu Jun 8 14:20:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42007 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp434273pzb; Thu, 8 Jun 2023 07:20:55 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6BYiP4EfxPzA8l6Vo98qHuEyMG4XuE+WtTxAbq4ViFnVh80m4bFoB4Fx1Dcr8Mm9VlnZmj X-Received: by 2002:a17:906:6a15:b0:970:c9f:2db6 with SMTP id qw21-20020a1709066a1500b009700c9f2db6mr9805012ejc.63.1686234055283; Thu, 08 Jun 2023 07:20:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686234055; cv=none; d=google.com; s=arc-20160816; b=gsYUsIpDCRfSl67Kp69XlTTRsX5tSdwNnEO+XCMgOirraqBdfKwPxoeM8XPHfCAJeQ mGONR8aajoMJYcnVHVS/lMq+1xIvJn/3EzUCAGOFpKAGmCuN4r+et0v6N6XfyA+5zcyg VfZh4qqnoFDANLRktTRV0/p1inTM/YVIRGg9cXALfFtAWTVJWeuu4wG/pFRPOySjbet8 KpCWVIj9sqBV1W6Zd9TMOu6Y+XP5/Kgyv2ClmIPOJ4167RNPKWhP0V+Y5NvUirp+I2Z7 q7AW2PqT8SR9+voQ2/D0M1oPcvAdwEMph3lJxbOa6G08M1mvHCh93M47glnK2Q7EJF6V hBZw== 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=gPsmioLk/FRba/fCMrQZIbORqVP6Gewx0vqZSghJlgg=; b=ygtA/s3eXLQeEIEtKgPK3Nzr8xisPDoF+aGxbnLkHzVqQ20GaO0ztOxNFxdxMjNOy4 ZFbcee2Je+L39CS91rDAXy6Bq8y4y3j6kpEFXhCNnAb4cpkblyGlQL+ZbkKv6ytkhuO3 GRQW8hLds/hl+nxkQQY+Y4LYxuIsQ/So+tF1I6LMnP0H5i310e5vHBvJvN5knzguGLET OuqXYhtsXpeoZVq7hM4Ei8CU0Q0KgHnAr4I8D5jppvgq+xLeHBEDdv2itTtMuRqrGpsn Hn16AvoePpFhqnu4+frHiX/c6jkI79PEmKZHRu3bvj5ll7lccn+Z7n05XozYiEAMi85V 6nqQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b="6b/k+DGg"; 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=NONE dis=NONE) header.from=mail.de Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d24-20020a50fe98000000b005163bfbd479si730674edt.66.2023.06.08.07.20.54; Thu, 08 Jun 2023 07:20:55 -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=@mail.de header.s=mailde202009 header.b="6b/k+DGg"; 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=NONE dis=NONE) header.from=mail.de Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 99C7868C2D1; Thu, 8 Jun 2023 17:20:38 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from shout01.mail.de (shout01.mail.de [62.201.172.24]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7B4EB68C2C7 for ; Thu, 8 Jun 2023 17:20:31 +0300 (EEST) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout01.mail.de (Postfix) with ESMTP id 25F5DA065B for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix03.mail.de (Postfix) with ESMTP id 0CE7580167 for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1686234031; bh=FZm94jNMGMbNWGwYgQ/rQKeOJwbPJuTCr+PeQHFjtV4=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=6b/k+DGgplhlGQ2rK/YzDkYFrlKi7lOtcJk7eWdDAEbbqdRuKigV4hHw2PTYMkjlZ ZD5jFjl381xBvAfwhOJTmTZvyrIG724WCklI6xXBUcwjtQajiy8YPkeeAPa4a/r2lv t5XSVURcFK+/Oc490bLsOy8InSsajEsBemOFtBg3UMt5ARGOHHHPalJy26knFU1OPb Z5VPpg1bvzSwdpoaPcHfj2cdKBrLnm69OsRF5428AjeuDSie/W5XxRZ0bvtnSd+3K1 ngNsymCzWM9HuWhPZ0vDY6jdb0MPHlvnmYoazj8EUVx4vky0MEBOujnmUEnqfrHRrm M43OsacsOExJg== Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtp01.mail.de (Postfix) with ESMTPSA id C6D87100594 for ; Thu, 8 Jun 2023 16:20:30 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Thu, 8 Jun 2023 16:20:26 +0200 Message-Id: <20230608142029.16564-2-thilo.borgmann@mail.de> In-Reply-To: <20230608142029.16564-1-thilo.borgmann@mail.de> References: <20230608142029.16564-1-thilo.borgmann@mail.de> MIME-Version: 1.0 X-purgate: clean X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate-type: clean X-purgate-Ad: Categorized by eleven eXpurgate (R) http://www.eleven.de X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate: clean X-purgate-size: 3382 X-purgate-ID: 154282::1686234030-077FA7B6-FAA70D71/0/0 Subject: [FFmpeg-devel] [PATCH v1 1/4] avcodec/webp: move definitions into header 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: o8DHfdBINZHf --- libavcodec/webp.c | 17 +-------------- libavcodec/webp.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 libavcodec/webp.h diff --git a/libavcodec/webp.c b/libavcodec/webp.c index d35cb66f8d..15152ec8fb 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -52,22 +52,7 @@ #include "thread.h" #include "tiff_common.h" #include "vp8.h" - -#define VP8X_FLAG_ANIMATION 0x02 -#define VP8X_FLAG_XMP_METADATA 0x04 -#define VP8X_FLAG_EXIF_METADATA 0x08 -#define VP8X_FLAG_ALPHA 0x10 -#define VP8X_FLAG_ICC 0x20 - -#define MAX_PALETTE_SIZE 256 -#define MAX_CACHE_BITS 11 -#define NUM_CODE_LENGTH_CODES 19 -#define HUFFMAN_CODES_PER_META_CODE 5 -#define NUM_LITERAL_CODES 256 -#define NUM_LENGTH_CODES 24 -#define NUM_DISTANCE_CODES 40 -#define NUM_SHORT_DISTANCES 120 -#define MAX_HUFFMAN_CODE_LENGTH 15 +#include "webp.h" static const uint16_t alphabet_sizes[HUFFMAN_CODES_PER_META_CODE] = { NUM_LITERAL_CODES + NUM_LENGTH_CODES, diff --git a/libavcodec/webp.h b/libavcodec/webp.h new file mode 100644 index 0000000000..90baa71182 --- /dev/null +++ b/libavcodec/webp.h @@ -0,0 +1,55 @@ +/* + * WebP image format definitions + * Copyright (c) 2020 Pexeso Inc. + * + * 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 image format definitions. + */ + +#ifndef AVCODEC_WEBP_H +#define AVCODEC_WEBP_H + +#define VP8X_FLAG_ANIMATION 0x02 +#define VP8X_FLAG_XMP_METADATA 0x04 +#define VP8X_FLAG_EXIF_METADATA 0x08 +#define VP8X_FLAG_ALPHA 0x10 +#define VP8X_FLAG_ICC 0x20 + +#define ANMF_DISPOSAL_METHOD 0x01 +#define ANMF_DISPOSAL_METHOD_UNCHANGED 0x00 +#define ANMF_DISPOSAL_METHOD_BACKGROUND 0x01 + +#define ANMF_BLENDING_METHOD 0x02 +#define ANMF_BLENDING_METHOD_ALPHA 0x00 +#define ANMF_BLENDING_METHOD_OVERWRITE 0x02 + +#define MAX_PALETTE_SIZE 256 +#define MAX_CACHE_BITS 11 +#define NUM_CODE_LENGTH_CODES 19 +#define HUFFMAN_CODES_PER_META_CODE 5 +#define NUM_LITERAL_CODES 256 +#define NUM_LENGTH_CODES 24 +#define NUM_DISTANCE_CODES 40 +#define NUM_SHORT_DISTANCES 120 +#define MAX_HUFFMAN_CODE_LENGTH 15 + + +#endif /* AVCODEC_WEBP_H */ From patchwork Thu Jun 8 14:20:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42008 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp434348pzb; Thu, 8 Jun 2023 07:21:05 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5nYly6H8UOOxKUQM33LYZGtY/Al86fxa5yAKT0HvEvWabXPPLyBftbVrj1mGVO5Mzq5rIu X-Received: by 2002:a17:907:8a1a:b0:977:bf02:b158 with SMTP id sc26-20020a1709078a1a00b00977bf02b158mr9723198ejc.10.1686234064835; Thu, 08 Jun 2023 07:21:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686234064; cv=none; d=google.com; s=arc-20160816; b=G/mR+gptH30mcOjxrmMpWmCMQUPCwbAHb9pssoOFxp/MhSFQTjWbKhYLDT82lfB3i4 6ceWcA8OtfLeq98krvHfRx7asDc1BIKA4IO4r8UYeHze441Ss0vrQ9JJb5Tz5+8sehbZ wiU1649xjaMtU7mFYDfrZxx5aDJPVobU8QypppkmZiy5CIdTEPLpy4bRN40yFAkSLx8j EO9hnV4ObIzrO83z0dWfrQXIcyaM2aSIbM7saIvB4HNVO/XCcDgBNRKtp8UWfSgvqCAO Ih2KRZR9mR41JNt11kGX6mL2WfyH3xMSiVUgWml3QYCihBa/G5Mwlma5Pije/jA0UUSs jxcw== 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=eLfssWoVam8mJfbkxDH4oodYcBhJy912Q1oaTofHGrs=; b=XH/eo18LQY1FFk++ig8U9FrJYUsOrO5cHjy7aAtWLVlR7OGXWY1ufO7/dKqivC/ker jL2WnMGSrNPCIn/bFp5eeXgqRkQjoUiEGyai8ISNAL5NHlL1IWtSs/JZlWX+cw4fhoQn wzyeCkqeBvMHGbTgfsfcwJcp8VX6opjmkpuySDGkjojuF3HbPjjGF15Fu5jhtep+wEzJ E+vE6m/hVEarx9HLsWeu86kYILZrWtlMU1Wtln7A8wXK871k01z1Zb6lrKtM3hzxhBdr 6ynDmPh5xAqvU/zg9ixVhbEEn2hNIYUalBdv9ToRSHh7qjOGPgB9Fcmu/TU0TPLrHvhF MM1g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=wBrz9Y2J; 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=NONE dis=NONE) header.from=mail.de Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id g9-20020a170906868900b009745eb4fa51si1009032ejx.98.2023.06.08.07.21.04; Thu, 08 Jun 2023 07:21:04 -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=@mail.de header.s=mailde202009 header.b=wBrz9Y2J; 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=NONE dis=NONE) header.from=mail.de Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B0D4F68C2EE; Thu, 8 Jun 2023 17:20:39 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from shout02.mail.de (shout02.mail.de [62.201.172.25]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D124068B896 for ; Thu, 8 Jun 2023 17:20:31 +0300 (EEST) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout02.mail.de (Postfix) with ESMTP id 717BAA0635 for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix03.mail.de (Postfix) with ESMTP id 5A43280167 for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1686234031; bh=xQheYRATXB4BcI9cqWFYQPNSM3MXnD0AreygwU3Yav4=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=wBrz9Y2JEmTpwFMnwuu0lhUFSc0KtSvL/j5wbMrIi8LmLc9xt+1KO211hZsJAKCkr mnYQQpCG6QP8NHkZUlugxFkWT5X0Xt8NshHjrgElugRJViQmOkz7d5OI3RW8mRj+97 TC579fNBcrdmmeAiyz7J42+qXbHpppqXXD35m7aqUXriosLYNERLUFK1oDvbddDPWz 9khQuD1p+0040UP8c2jvHgcp8bB5JIvglsmvSIYc4nD6ja2NE/D0ksgQqskgVdYxk2 cTHrezBI5ncX88eZaRfgbmC55UHZxS+/APWYzReMECoPpY+4PxxgZU++UtgE5hnGQo 0GpLRuasz3CFw== Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtp01.mail.de (Postfix) with ESMTPSA id 17500100462 for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Thu, 8 Jun 2023 16:20:27 +0200 Message-Id: <20230608142029.16564-3-thilo.borgmann@mail.de> In-Reply-To: <20230608142029.16564-1-thilo.borgmann@mail.de> References: <20230608142029.16564-1-thilo.borgmann@mail.de> MIME-Version: 1.0 X-purgate: clean X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate-type: clean X-purgate-Ad: Categorized by eleven eXpurgate (R) http://www.eleven.de X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate: clean X-purgate-size: 6672 X-purgate-ID: 154282::1686234031-077FA7B6-4B2A3693/0/0 Subject: [FFmpeg-devel] [PATCH v1 2/4] avcodec/webp_parser: parse each frame into one packet 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: h2S/8B7bCKu2 --- libavcodec/webp_parser.c | 132 ++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 42 deletions(-) diff --git a/libavcodec/webp_parser.c b/libavcodec/webp_parser.c index bd5f94dac5..d10d06bd0e 100644 --- a/libavcodec/webp_parser.c +++ b/libavcodec/webp_parser.c @@ -25,13 +25,17 @@ #include "libavutil/bswap.h" #include "libavutil/common.h" +#include "libavutil/intreadwrite.h" #include "parser.h" typedef struct WebPParseContext { ParseContext pc; + int frame; + int first_frame; uint32_t fsize; - uint32_t remaining_size; + uint32_t remaining_file_size; + uint32_t remaining_tag_size; } WebPParseContext; static int webp_parse(AVCodecParserContext *s, AVCodecContext *avctx, @@ -41,62 +45,106 @@ static int webp_parse(AVCodecParserContext *s, AVCodecContext *avctx, WebPParseContext *ctx = s->priv_data; uint64_t state = ctx->pc.state64; int next = END_NOT_FOUND; - int i = 0; + int i, len; - *poutbuf = NULL; - *poutbuf_size = 0; - -restart: - if (ctx->pc.frame_start_found <= 8) { - for (; i < buf_size; i++) { + for (i = 0; i < buf_size;) { + if (ctx->remaining_tag_size) { + /* consuming tag */ + len = FFMIN(ctx->remaining_tag_size, buf_size - i); + i += len; + ctx->remaining_tag_size -= len; + ctx->remaining_file_size -= len; + } else { + /* scan for the next tag or file */ state = (state << 8) | buf[i]; - if (ctx->pc.frame_start_found == 0) { - if ((state >> 32) == MKBETAG('R', 'I', 'F', 'F')) { - ctx->fsize = av_bswap32(state); - if (ctx->fsize > 15 && ctx->fsize <= UINT32_MAX - 10) { - ctx->pc.frame_start_found = 1; - ctx->fsize += 8; + i++; + + if (!ctx->remaining_file_size) { + /* scan for the next file */ + if (ctx->pc.frame_start_found == 4) { + ctx->pc.frame_start_found = 0; + if ((uint32_t) state == MKBETAG('W', 'E', 'B', 'P')) { + if (ctx->frame || i != 12) { + ctx->frame = 0; + next = i - 12; + state = 0; + ctx->pc.frame_start_found = 0; + break; + } + ctx->remaining_file_size = ctx->fsize - 4; + ctx->first_frame = 1; + continue; } } - } else if (ctx->pc.frame_start_found == 8) { - if ((state >> 32) != MKBETAG('W', 'E', 'B', 'P')) { + if (ctx->pc.frame_start_found == 0) { + if ((state >> 32) == MKBETAG('R', 'I', 'F', 'F')) { + ctx->fsize = av_bswap32(state); + if (ctx->fsize > 15 && ctx->fsize <= UINT32_MAX - 10) { + ctx->fsize += (ctx->fsize & 1); + ctx->pc.frame_start_found = 1; + } + } + } else + ctx->pc.frame_start_found++; + } else { + /* read the next tag */ + ctx->remaining_file_size--; + if (ctx->remaining_file_size == 0) { ctx->pc.frame_start_found = 0; continue; } ctx->pc.frame_start_found++; - ctx->remaining_size = ctx->fsize + i - 15; - if (ctx->pc.index + i > 15) { - next = i - 15; - state = 0; - break; - } else { - ctx->pc.state64 = 0; - goto restart; + if (ctx->pc.frame_start_found < 8) + continue; + + switch (state >> 32) { + case MKBETAG('A', 'N', 'M', 'F'): + case MKBETAG('V', 'P', '8', ' '): + case MKBETAG('V', 'P', '8', 'L'): + if (ctx->frame) { + ctx->frame = 0; + next = i - 8; + state = 0; + ctx->pc.frame_start_found = 0; + goto flush; + } + ctx->frame = 1; + break; + default: + break; } - } else if (ctx->pc.frame_start_found) - ctx->pc.frame_start_found++; - } - ctx->pc.state64 = state; - } else { - if (ctx->remaining_size) { - i = FFMIN(ctx->remaining_size, buf_size); - ctx->remaining_size -= i; - if (ctx->remaining_size) - goto flush; - ctx->pc.frame_start_found = 0; - goto restart; + ctx->remaining_tag_size = av_bswap32(state); + ctx->remaining_tag_size += ctx->remaining_tag_size & 1; + if (ctx->remaining_tag_size > ctx->remaining_file_size) { + /* this is probably trash at the end of file */ + ctx->remaining_tag_size = ctx->remaining_file_size; + } + ctx->pc.frame_start_found = 0; + state = 0; + } } } - flush: - if (ff_combine_frame(&ctx->pc, next, &buf, &buf_size) < 0) + ctx->pc.state64 = state; + + if (ff_combine_frame(&ctx->pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; return buf_size; + } - if (next != END_NOT_FOUND && next < 0) - ctx->pc.frame_start_found = FFMAX(ctx->pc.frame_start_found - i - 1, 0); - else - ctx->pc.frame_start_found = 0; + // Extremely simplified key frame detection: + // - the first frame (containing headers) is marked as a key frame + // - other frames are marked as non-key frames + if (ctx->first_frame) { + ctx->first_frame = 0; + s->pict_type = AV_PICTURE_TYPE_I; + s->key_frame = 1; + } else { + s->pict_type = AV_PICTURE_TYPE_P; + s->key_frame = 0; + } *poutbuf = buf; *poutbuf_size = buf_size; From patchwork Thu Jun 8 14:20:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42009 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp434485pzb; Thu, 8 Jun 2023 07:21:16 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5DflM6EFNQHICNlIpuDCSMPDKUgV6DoD/aRHmjZKS+ryIcgzre/ZPbGTDGZB1pVbKm1jlE X-Received: by 2002:a17:907:724c:b0:978:337e:c41a with SMTP id ds12-20020a170907724c00b00978337ec41amr9423313ejc.14.1686234075778; Thu, 08 Jun 2023 07:21:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686234075; cv=none; d=google.com; s=arc-20160816; b=XLKuOzItx+r/lPhF58JAYmuKLlmI8yvKSIt/1LVZN3K8tFiRshNYsDHWZqlLaEEaSa dkYzf1S33BELM7PbjXajvuKN1Fa5OnUM26+MvTH0WVt8SQUDWbIYpcU5sDYwJLilzelV WrNqS3aJykE0En7DCuz2y3qk9KBYZ9RgLHZFyhNTDFSSBHP1P0KqONCA17ll6/BsJLcA XijFmjMc2nJ+hquPLzGNpjzcdb8QYcOjyOvYy+p89MWZ6jaZBhjRDq9IEjpLjC2Obifd xaAL3PZaxyEkDVQ+UP54s4tI0Cxx7acNA5IFclmNxNh2GkfnP1F+j3sQqyZJux+n6lvU IjRw== 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=xqZVpq7K13qwfeBzplQKdGgpI1SgKlncOlAExnvLFfQ=; b=b+NW/iO1+5DpwLSMIPaEUAftSDgR/4udAZGiKVxH7otZdad+jnaJqKraRK8yMjPIul GCydWtkvjd3DIwq8+A4DEuHEd/3iEXueU69R/ZgJMPJacV9yTuDLNqtMQuIuzXfdmo5/ 9d1uboGrIdBZZ6PfEImb2gOqmHyxQGuEC76BhvWfQWdUHyhu0lsLU2W9CB/xi9DeRWbM Mw0BoJgwAWDm7ivLuBYw8qnp3F/T7JcGKneyrbTp/RDQIITzW2viAc42xKB3nNTXTjs3 seLqfd8UuKww/xUQZiT+1Pvg7Bm+TUpvZzi0jbYeV/ff3mw852VMMEXVb9QYzsCYPMet tTCQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=I9BbVME0; 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=NONE dis=NONE) header.from=mail.de Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id bu12-20020a170906a14c00b0095382d71c61si846975ejb.159.2023.06.08.07.21.15; Thu, 08 Jun 2023 07:21:15 -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=@mail.de header.s=mailde202009 header.b=I9BbVME0; 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=NONE dis=NONE) header.from=mail.de Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id C50DC68C2FD; Thu, 8 Jun 2023 17:20:40 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from shout02.mail.de (shout02.mail.de [62.201.172.25]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 27B0968C2DF for ; Thu, 8 Jun 2023 17:20:32 +0300 (EEST) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout02.mail.de (Postfix) with ESMTP id C0C54A063A for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix03.mail.de (Postfix) with ESMTP id A9BEF80167 for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1686234031; bh=wlKKOEXb1tPQx7i3JUnXvm78BTJwMDJpPeM4wxtGD6E=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=I9BbVME0lFGoOF03IKQqaYpZDtLG9PgxPHslWsES7oth3mSRjbEE7azJDr3qDo+Su 04pLBH6vGdGKVlCLMWuFjYPtvHh0/G8cXmP/mtVY3l2fRrVCELzm30TkJbih/0Sl29 y5tfiQ122X0D7jYx3I0a5/NO1sGe5+DwE33Ek4K2Qe766fcOGemGwv0K4eTePc93zX iFem8mYlYjJYXC+nXtm3vYw33mD7XEEm+O28NuY+w27x2PfCbe6hxpLvsjEVuHVifO QsRJ1zyqlBXiKw35wLAz+8lUIC2YR/G20bC3VhCaDA7nCN7beoJjEwXRqeBlDr0BaY v2duRdAFRhiJg== Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtp01.mail.de (Postfix) with ESMTPSA id 68758100595 for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Thu, 8 Jun 2023 16:20:28 +0200 Message-Id: <20230608142029.16564-4-thilo.borgmann@mail.de> In-Reply-To: <20230608142029.16564-1-thilo.borgmann@mail.de> References: <20230608142029.16564-1-thilo.borgmann@mail.de> MIME-Version: 1.0 X-purgate: clean X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate-type: clean X-purgate-Ad: Categorized by eleven eXpurgate (R) http://www.eleven.de X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate: clean X-purgate-size: 41761 X-purgate-ID: 154282::1686234031-077FA7B6-22108777/0/0 Subject: [FFmpeg-devel] [PATCH v1 3/4] libavcodec/webp: add support for animated WebP decoding 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: 2mmy3GHwAMR8 From: Josef Zlomek Fixes: 4907 Adds support for decoding of animated WebP. The WebP decoder adds the animation related features according to the specs: https://developers.google.com/speed/webp/docs/riff_container#animation The frames of the animation may be smaller than the image canvas. Therefore, the frame is decoded to a temporary frame, then it is blended into the canvas, the canvas is copied to the output frame, and finally the frame is disposed from the canvas. The output to AV_PIX_FMT_YUVA420P/AV_PIX_FMT_YUV420P is still supported. The background color is specified only as BGRA in the WebP file so it is converted to YUVA if YUV formats are output. Signed-off-by: Josef Zlomek --- Changelog | 1 + libavcodec/codec_desc.c | 3 +- libavcodec/version.h | 2 +- libavcodec/webp.c | 714 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 658 insertions(+), 62 deletions(-) diff --git a/Changelog b/Changelog index d51e03b8eb..31651b8c29 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version : - nlmeans_vulkan filter - RivaTuner video decoder - xfade_vulkan filter +- animated WebP parser/decoder version 6.0: - Radiance HDR image support diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 41293a78dc..383d7c2394 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1259,8 +1259,7 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_VIDEO, .name = "webp", .long_name = NULL_IF_CONFIG_SMALL("WebP"), - .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | - AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, .mime_types= MT("image/webp"), }, { diff --git a/libavcodec/version.h b/libavcodec/version.h index 2618016a83..d6e0724326 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -30,7 +30,7 @@ #include "version_major.h" #define LIBAVCODEC_VERSION_MINOR 17 -#define LIBAVCODEC_VERSION_MICRO 100 +#define LIBAVCODEC_VERSION_MICRO 101 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ diff --git a/libavcodec/webp.c b/libavcodec/webp.c index 15152ec8fb..bee43fcf19 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -35,12 +35,16 @@ * Exif metadata * ICC profile * + * @author Josef Zlomek, Pexeso Inc. + * Animation + * * Unimplemented: - * - Animation * - XMP metadata */ +#include "libavcodec/packet.h" #include "libavutil/imgutils.h" +#include "libavutil/colorspace.h" #define BITSTREAM_READER_LE #include "avcodec.h" @@ -178,6 +182,8 @@ typedef struct ImageContext { typedef struct WebPContext { VP8Context v; /* VP8 Context used for lossy decoding */ GetBitContext gb; /* bitstream reader for main image chunk */ + ThreadFrame canvas_frame; /* ThreadFrame for canvas */ + AVFrame *frame; /* AVFrame for decoded frame */ AVFrame *alpha_frame; /* AVFrame for alpha data decompressed from VP8L */ AVPacket *pkt; /* AVPacket to be passed to the underlying VP8 decoder */ AVCodecContext *avctx; /* parent AVCodecContext */ @@ -189,9 +195,24 @@ typedef struct WebPContext { int alpha_data_size; /* alpha chunk data size */ int has_exif; /* set after an EXIF chunk has been processed */ int has_iccp; /* set after an ICCP chunk has been processed */ - int width; /* image width */ - int height; /* image height */ - int lossless; /* indicates lossless or lossy */ + int vp8x_flags; /* global flags from VP8X chunk */ + int canvas_width; /* canvas width */ + int canvas_height; /* canvas height */ + int anmf_flags; /* frame flags from ANMF chunk */ + int width; /* frame width */ + int height; /* frame height */ + int pos_x; /* frame position X */ + int pos_y; /* frame position Y */ + int prev_anmf_flags; /* previous frame flags from ANMF chunk */ + int prev_width; /* previous frame width */ + int prev_height; /* previous frame height */ + int prev_pos_x; /* previous frame position X */ + int prev_pos_y; /* previous frame position Y */ + int await_progress; /* value of progress to wait for */ + uint8_t background_argb[4]; /* background color in ARGB format */ + uint8_t background_yuva[4]; /* background color in YUVA format */ + const uint8_t *background_data[4]; /* "planes" for background color in YUVA format */ + uint8_t transparent_yuva[4]; /* transparent black in YUVA format */ int nb_transforms; /* number of transforms */ enum TransformType transforms[4]; /* transformations used in the image, in order */ @@ -555,7 +576,7 @@ static int decode_entropy_coded_image(WebPContext *s, enum ImageRole role, img->frame->height = h; if (role == IMAGE_ROLE_ARGB && !img->is_alpha_primary) { - ret = ff_thread_get_buffer(s->avctx, img->frame, 0); + ret = ff_get_buffer(s->avctx, img->frame, 0); } else ret = av_frame_get_buffer(img->frame, 1); if (ret < 0) @@ -1053,7 +1074,7 @@ static int apply_color_indexing_transform(WebPContext *s) return 0; } -static void update_canvas_size(AVCodecContext *avctx, int w, int h) +static void update_frame_size(AVCodecContext *avctx, int w, int h) { WebPContext *s = avctx->priv_data; if (s->width && s->width != w) { @@ -1076,7 +1097,6 @@ static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p, int w, h, ret, i, used; if (!is_alpha_chunk) { - s->lossless = 1; avctx->pix_fmt = AV_PIX_FMT_ARGB; } @@ -1093,7 +1113,7 @@ static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p, w = get_bits(&s->gb, 14) + 1; h = get_bits(&s->gb, 14) + 1; - update_canvas_size(avctx, w, h); + update_frame_size(avctx, w, h); ret = ff_set_dimensions(avctx, s->width, s->height); if (ret < 0) @@ -1290,7 +1310,6 @@ static int vp8_lossy_decode_frame(AVCodecContext *avctx, AVFrame *p, s->v.actually_webp = 1; } avctx->pix_fmt = s->has_alpha ? AV_PIX_FMT_YUVA420P : AV_PIX_FMT_YUV420P; - s->lossless = 0; if (data_size > INT_MAX) { av_log(avctx, AV_LOG_ERROR, "unsupported chunk size\n"); @@ -1308,7 +1327,7 @@ static int vp8_lossy_decode_frame(AVCodecContext *avctx, AVFrame *p, if (!*got_frame) return AVERROR_INVALIDDATA; - update_canvas_size(avctx, avctx->width, avctx->height); + update_frame_size(avctx, avctx->width, avctx->height); if (s->has_alpha) { ret = vp8_lossy_decode_alpha(avctx, p, s->alpha_data, @@ -1318,41 +1337,17 @@ static int vp8_lossy_decode_frame(AVCodecContext *avctx, AVFrame *p, } return ret; } +int init_canvas_frame(WebPContext *s, int format, int key_frame); -static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, - int *got_frame, AVPacket *avpkt) +static int webp_decode_frame_common(AVCodecContext *avctx, uint8_t *data, int size, + int *got_frame, int key_frame) { WebPContext *s = avctx->priv_data; GetByteContext gb; int ret; uint32_t chunk_type, chunk_size; - int vp8x_flags = 0; - s->avctx = avctx; - s->width = 0; - s->height = 0; - *got_frame = 0; - s->has_alpha = 0; - s->has_exif = 0; - s->has_iccp = 0; - bytestream2_init(&gb, avpkt->data, avpkt->size); - - if (bytestream2_get_bytes_left(&gb) < 12) - return AVERROR_INVALIDDATA; - - if (bytestream2_get_le32(&gb) != MKTAG('R', 'I', 'F', 'F')) { - av_log(avctx, AV_LOG_ERROR, "missing RIFF tag\n"); - return AVERROR_INVALIDDATA; - } - - chunk_size = bytestream2_get_le32(&gb); - if (bytestream2_get_bytes_left(&gb) < chunk_size) - return AVERROR_INVALIDDATA; - - if (bytestream2_get_le32(&gb) != MKTAG('W', 'E', 'B', 'P')) { - av_log(avctx, AV_LOG_ERROR, "missing WEBP tag\n"); - return AVERROR_INVALIDDATA; - } + bytestream2_init(&gb, data, size); while (bytestream2_get_bytes_left(&gb) > 8) { char chunk_str[5] = { 0 }; @@ -1363,6 +1358,10 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, return AVERROR_INVALIDDATA; chunk_size += chunk_size & 1; + // we need to dive into RIFF chunk + if (chunk_type == MKTAG('R', 'I', 'F', 'F')) + chunk_size = 4; + if (bytestream2_get_bytes_left(&gb) < chunk_size) { /* we seem to be running out of data, but it could also be that the bitstream has trailing junk leading to bogus chunk_size. */ @@ -1370,10 +1369,26 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, } switch (chunk_type) { + case MKTAG('R', 'I', 'F', 'F'): + if (bytestream2_get_le32(&gb) != MKTAG('W', 'E', 'B', 'P')) { + av_log(avctx, AV_LOG_ERROR, "missing WEBP tag\n"); + return AVERROR_INVALIDDATA; + } + s->vp8x_flags = 0; + s->canvas_width = 0; + s->canvas_height = 0; + s->has_exif = 0; + s->has_iccp = 0; + ff_thread_release_ext_buffer(avctx, &s->canvas_frame); + break; case MKTAG('V', 'P', '8', ' '): if (!*got_frame) { - ret = vp8_lossy_decode_frame(avctx, p, got_frame, - avpkt->data + bytestream2_tell(&gb), + ret = init_canvas_frame(s, AV_PIX_FMT_YUVA420P, key_frame); + if (ret < 0) + return ret; + + ret = vp8_lossy_decode_frame(avctx, s->frame, got_frame, + data + bytestream2_tell(&gb), chunk_size); if (ret < 0) return ret; @@ -1382,8 +1397,13 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, break; case MKTAG('V', 'P', '8', 'L'): if (!*got_frame) { - ret = vp8_lossless_decode_frame(avctx, p, got_frame, - avpkt->data + bytestream2_tell(&gb), + ret = init_canvas_frame(s, AV_PIX_FMT_ARGB, key_frame); + if (ret < 0) + return ret; + ff_thread_finish_setup(s->avctx); + + ret = vp8_lossless_decode_frame(avctx, s->frame, got_frame, + data + bytestream2_tell(&gb), chunk_size, 0); if (ret < 0) return ret; @@ -1392,14 +1412,16 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, bytestream2_skip(&gb, chunk_size); break; case MKTAG('V', 'P', '8', 'X'): - if (s->width || s->height || *got_frame) { + if (s->canvas_width || s->canvas_height || *got_frame) { av_log(avctx, AV_LOG_ERROR, "Canvas dimensions are already set\n"); return AVERROR_INVALIDDATA; } - vp8x_flags = bytestream2_get_byte(&gb); + s->vp8x_flags = bytestream2_get_byte(&gb); bytestream2_skip(&gb, 3); s->width = bytestream2_get_le24(&gb) + 1; s->height = bytestream2_get_le24(&gb) + 1; + s->canvas_width = s->width; + s->canvas_height = s->height; ret = av_image_check_size(s->width, s->height, 0, avctx); if (ret < 0) return ret; @@ -1407,7 +1429,7 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, case MKTAG('A', 'L', 'P', 'H'): { int alpha_header, filter_m, compression; - if (!(vp8x_flags & VP8X_FLAG_ALPHA)) { + if (!(s->vp8x_flags & VP8X_FLAG_ALPHA)) { av_log(avctx, AV_LOG_WARNING, "ALPHA chunk present, but alpha bit not set in the " "VP8X header\n"); @@ -1416,8 +1438,9 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, av_log(avctx, AV_LOG_ERROR, "invalid ALPHA chunk size\n"); return AVERROR_INVALIDDATA; } + alpha_header = bytestream2_get_byte(&gb); - s->alpha_data = avpkt->data + bytestream2_tell(&gb); + s->alpha_data = data + bytestream2_tell(&gb); s->alpha_data_size = chunk_size - 1; bytestream2_skip(&gb, s->alpha_data_size); @@ -1444,14 +1467,13 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, av_log(avctx, AV_LOG_VERBOSE, "Ignoring extra EXIF chunk\n"); goto exif_end; } - if (!(vp8x_flags & VP8X_FLAG_EXIF_METADATA)) + if (!(s->vp8x_flags & VP8X_FLAG_EXIF_METADATA)) av_log(avctx, AV_LOG_WARNING, "EXIF chunk present, but Exif bit not set in the " "VP8X header\n"); s->has_exif = 1; - bytestream2_init(&exif_gb, avpkt->data + exif_offset, - avpkt->size - exif_offset); + bytestream2_init(&exif_gb, data + exif_offset, size - exif_offset); if (ff_tdecode_header(&exif_gb, &le, &ifd_offset) < 0) { av_log(avctx, AV_LOG_ERROR, "invalid TIFF header " "in Exif data\n"); @@ -1464,7 +1486,7 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, goto exif_end; } - av_dict_copy(&p->metadata, exif_metadata, 0); + av_dict_copy(&s->frame->metadata, exif_metadata, 0); exif_end: av_dict_free(&exif_metadata); @@ -1479,21 +1501,64 @@ exif_end: bytestream2_skip(&gb, chunk_size); break; } - if (!(vp8x_flags & VP8X_FLAG_ICC)) + if (!(s->vp8x_flags & VP8X_FLAG_ICC)) av_log(avctx, AV_LOG_WARNING, "ICCP chunk present, but ICC Profile bit not set in the " "VP8X header\n"); s->has_iccp = 1; - sd = av_frame_new_side_data(p, AV_FRAME_DATA_ICC_PROFILE, chunk_size); + sd = av_frame_new_side_data(s->frame, AV_FRAME_DATA_ICC_PROFILE, chunk_size); if (!sd) return AVERROR(ENOMEM); bytestream2_get_buffer(&gb, sd->data, chunk_size); break; } - case MKTAG('A', 'N', 'I', 'M'): + case MKTAG('A', 'N', 'I', 'M'): { + const AVPixFmtDescriptor *desc; + int a, r, g, b; + if (!(s->vp8x_flags & VP8X_FLAG_ANIMATION)) { + av_log(avctx, AV_LOG_WARNING, + "ANIM chunk present, but animation bit not set in the " + "VP8X header\n"); + } + // background is stored as BGRA, we need ARGB + s->background_argb[3] = b = bytestream2_get_byte(&gb); + s->background_argb[2] = g = bytestream2_get_byte(&gb); + s->background_argb[1] = r = bytestream2_get_byte(&gb); + s->background_argb[0] = a = bytestream2_get_byte(&gb); + + // convert the background color to YUVA + desc = av_pix_fmt_desc_get(AV_PIX_FMT_YUVA420P); + s->background_yuva[desc->comp[0].plane] = RGB_TO_Y_CCIR(r, g, b); + s->background_yuva[desc->comp[1].plane] = RGB_TO_U_CCIR(r, g, b, 0); + s->background_yuva[desc->comp[2].plane] = RGB_TO_V_CCIR(r, g, b, 0); + s->background_yuva[desc->comp[3].plane] = a; + + bytestream2_skip(&gb, 2); // loop count is ignored + break; + } case MKTAG('A', 'N', 'M', 'F'): + if (!(s->vp8x_flags & VP8X_FLAG_ANIMATION)) { + av_log(avctx, AV_LOG_WARNING, + "ANMF chunk present, but animation bit not set in the " + "VP8X header\n"); + } + s->pos_x = bytestream2_get_le24(&gb) * 2; + s->pos_y = bytestream2_get_le24(&gb) * 2; + s->width = bytestream2_get_le24(&gb) + 1; + s->height = bytestream2_get_le24(&gb) + 1; + bytestream2_skip(&gb, 3); // duration + s->anmf_flags = bytestream2_get_byte(&gb); + + if (s->width + s->pos_x > s->canvas_width || + s->height + s->pos_y > s->canvas_height) { + av_log(avctx, AV_LOG_ERROR, + "frame does not fit into canvas\n"); + return AVERROR_INVALIDDATA; + } + s->vp8x_flags |= VP8X_FLAG_ANIMATION; + break; case MKTAG('X', 'M', 'P', ' '): AV_WL32(chunk_str, chunk_type); av_log(avctx, AV_LOG_WARNING, "skipping unsupported chunk: %s\n", @@ -1509,21 +1574,508 @@ exif_end: } } - if (!*got_frame) { - av_log(avctx, AV_LOG_ERROR, "image data not found\n"); - return AVERROR_INVALIDDATA; + return size; +} + +int init_canvas_frame(WebPContext *s, int format, int key_frame) +{ + AVFrame *canvas = s->canvas_frame.f; + int height; + int ret; + + // canvas is needed only for animation + if (!(s->vp8x_flags & VP8X_FLAG_ANIMATION)) + return 0; + + // avoid init for non-key frames whose format and size did not change + if (!key_frame && + canvas->data[0] && + canvas->format == format && + canvas->width == s->canvas_width && + canvas->height == s->canvas_height) + return 0; + + // canvas changes within IPPP sequences will loose thread sync + // because of the ThreadFrame reallocation and will wait forever + // so if frame-threading is used, forbid canvas changes and unlock + // previous frames + if (!key_frame && canvas->data[0]) { + if (s->avctx->thread_count > 1) { + av_log(s->avctx, AV_LOG_WARNING, "Canvas change detected. The output will be damaged. Use -threads 1 to try decoding with best effort.\n"); + // unlock previous frames that have sent an _await() call + ff_thread_report_progress(&s->canvas_frame, INT_MAX, 0); + return AVERROR_PATCHWELCOME; + } else { + // warn for damaged frames + av_log(s->avctx, AV_LOG_WARNING, "Canvas change detected. The output will be damaged.\n"); + } + } + + s->avctx->pix_fmt = format; + canvas->format = format; + canvas->width = s->canvas_width; + canvas->height = s->canvas_height; + + // VP8 decoder changed the width and height in AVCodecContext. + // Change it back to the canvas size. + ret = ff_set_dimensions(s->avctx, s->canvas_width, s->canvas_height); + if (ret < 0) + return ret; + + ff_thread_release_ext_buffer(s->avctx, &s->canvas_frame); + ret = ff_thread_get_ext_buffer(s->avctx, &s->canvas_frame, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return ret; + + if (canvas->format == AV_PIX_FMT_ARGB) { + height = canvas->height; + memset(canvas->data[0], 0, height * canvas->linesize[0]); + } else /* if (canvas->format == AV_PIX_FMT_YUVA420P) */ { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(canvas->format); + for (int comp = 0; comp < desc->nb_components; comp++) { + int plane = desc->comp[comp].plane; + + if (comp == 1 || comp == 2) + height = AV_CEIL_RSHIFT(canvas->height, desc->log2_chroma_h); + else + height = FFALIGN(canvas->height, 1 << desc->log2_chroma_h); + + memset(canvas->data[plane], s->transparent_yuva[plane], + height * canvas->linesize[plane]); + } + } + + return 0; +} + +/* + * Blend src1 (foreground) and src2 (background) into dest, in ARGB format. + * width, height are the dimensions of src1 + * pos_x, pos_y is the position in src2 and in dest + */ +static void blend_alpha_argb(uint8_t *dest_data[4], int dest_linesize[4], + const uint8_t *src1_data[4], int src1_linesize[4], + const uint8_t *src2_data[4], int src2_linesize[4], + int src2_step[4], + int width, int height, int pos_x, int pos_y) +{ + for (int y = 0; y < height; y++) { + const uint8_t *src1 = src1_data[0] + y * src1_linesize[0]; + const uint8_t *src2 = src2_data[0] + (y + pos_y) * src2_linesize[0] + pos_x * src2_step[0]; + uint8_t *dest = dest_data[0] + (y + pos_y) * dest_linesize[0] + pos_x * sizeof(uint32_t); + for (int x = 0; x < width; x++) { + int src1_alpha = src1[0]; + int src2_alpha = src2[0]; + + if (src1_alpha == 255) { + memcpy(dest, src1, sizeof(uint32_t)); + } else if (src1_alpha + src2_alpha == 0) { + memset(dest, 0, sizeof(uint32_t)); + } else { + int tmp_alpha = src2_alpha - ROUNDED_DIV(src1_alpha * src2_alpha, 255); + int blend_alpha = src1_alpha + tmp_alpha; + + dest[0] = blend_alpha; + dest[1] = ROUNDED_DIV(src1[1] * src1_alpha + src2[1] * tmp_alpha, blend_alpha); + dest[2] = ROUNDED_DIV(src1[2] * src1_alpha + src2[2] * tmp_alpha, blend_alpha); + dest[3] = ROUNDED_DIV(src1[3] * src1_alpha + src2[3] * tmp_alpha, blend_alpha); + } + src1 += sizeof(uint32_t); + src2 += src2_step[0]; + dest += sizeof(uint32_t); + } + } +} + +/* + * Blend src1 (foreground) and src2 (background) into dest, in YUVA format. + * width, height are the dimensions of src1 + * pos_x, pos_y is the position in src2 and in dest + */ +static void blend_alpha_yuva(WebPContext *s, + uint8_t *dest_data[4], int dest_linesize[4], + const uint8_t *src1_data[4], int src1_linesize[4], + int src1_format, + const uint8_t *src2_data[4], int src2_linesize[4], + int src2_step[4], + int width, int height, int pos_x, int pos_y) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(src1_format); + + int plane_y = desc->comp[0].plane; + int plane_u = desc->comp[1].plane; + int plane_v = desc->comp[2].plane; + int plane_a = desc->comp[3].plane; + + // blend U & V planes first, because the later step may modify alpha plane + int w = AV_CEIL_RSHIFT(width, desc->log2_chroma_w); + int h = AV_CEIL_RSHIFT(height, desc->log2_chroma_h); + int px = AV_CEIL_RSHIFT(pos_x, desc->log2_chroma_w); + int py = AV_CEIL_RSHIFT(pos_y, desc->log2_chroma_h); + int tile_w = 1 << desc->log2_chroma_w; + int tile_h = 1 << desc->log2_chroma_h; + + for (int y = 0; y < h; y++) { + const uint8_t *src1_u = src1_data[plane_u] + y * src1_linesize[plane_u]; + const uint8_t *src1_v = src1_data[plane_v] + y * src1_linesize[plane_v]; + const uint8_t *src2_u = src2_data[plane_u] + (y + py) * src2_linesize[plane_u] + px * src2_step[plane_u]; + const uint8_t *src2_v = src2_data[plane_v] + (y + py) * src2_linesize[plane_v] + px * src2_step[plane_v]; + uint8_t *dest_u = dest_data[plane_u] + (y + py) * dest_linesize[plane_u] + px; + uint8_t *dest_v = dest_data[plane_v] + (y + py) * dest_linesize[plane_v] + px; + for (int x = 0; x < w; x++) { + // calculate the average alpha of the tile + int src1_alpha = 0; + int src2_alpha = 0; + for (int yy = 0; yy < tile_h; yy++) { + for (int xx = 0; xx < tile_w; xx++) { + src1_alpha += src1_data[plane_a][(y * tile_h + yy) * src1_linesize[plane_a] + + (x * tile_w + xx)]; + src2_alpha += src2_data[plane_a][((y + py) * tile_h + yy) * src2_linesize[plane_a] + + ((x + px) * tile_w + xx) * src2_step[plane_a]]; + } + } + src1_alpha = AV_CEIL_RSHIFT(src1_alpha, desc->log2_chroma_w + desc->log2_chroma_h); + src2_alpha = AV_CEIL_RSHIFT(src2_alpha, desc->log2_chroma_w + desc->log2_chroma_h); + + if (src1_alpha == 255) { + *dest_u = *src1_u; + *dest_v = *src1_v; + } else if (src1_alpha + src2_alpha == 0) { + *dest_u = s->transparent_yuva[plane_u]; + *dest_v = s->transparent_yuva[plane_v]; + } else { + int tmp_alpha = src2_alpha - ROUNDED_DIV(src1_alpha * src2_alpha, 255); + int blend_alpha = src1_alpha + tmp_alpha; + *dest_u = ROUNDED_DIV(*src1_u * src1_alpha + *src2_u * tmp_alpha, blend_alpha); + *dest_v = ROUNDED_DIV(*src1_v * src1_alpha + *src2_v * tmp_alpha, blend_alpha); + } + src1_u++; + src1_v++; + src2_u += src2_step[plane_u]; + src2_v += src2_step[plane_v]; + dest_u++; + dest_v++; + } + } + + // blend Y & A planes + for (int y = 0; y < height; y++) { + const uint8_t *src1_y = src1_data[plane_y] + y * src1_linesize[plane_y]; + const uint8_t *src1_a = src1_data[plane_a] + y * src1_linesize[plane_a]; + const uint8_t *src2_y = src2_data[plane_y] + (y + pos_y) * src2_linesize[plane_y] + pos_x * src2_step[plane_y]; + const uint8_t *src2_a = src2_data[plane_a] + (y + pos_y) * src2_linesize[plane_a] + pos_x * src2_step[plane_a]; + uint8_t *dest_y = dest_data[plane_y] + (y + pos_y) * dest_linesize[plane_y] + pos_x; + uint8_t *dest_a = dest_data[plane_a] + (y + pos_y) * dest_linesize[plane_a] + pos_x; + for (int x = 0; x < width; x++) { + int src1_alpha = *src1_a; + int src2_alpha = *src2_a; + + if (src1_alpha == 255) { + *dest_y = *src1_y; + *dest_a = 255; + } else if (src1_alpha + src2_alpha == 0) { + *dest_y = s->transparent_yuva[plane_y]; + *dest_a = 0; + } else { + int tmp_alpha = src2_alpha - ROUNDED_DIV(src1_alpha * src2_alpha, 255); + int blend_alpha = src1_alpha + tmp_alpha; + *dest_y = ROUNDED_DIV(*src1_y * src1_alpha + *src2_y * tmp_alpha, blend_alpha); + *dest_a = blend_alpha; + } + src1_y++; + src1_a++; + src2_y += src2_step[plane_y]; + src2_a += src2_step[plane_a]; + dest_y++; + dest_a++; + } + } +} + +static int blend_frame_into_canvas(WebPContext *s) +{ + AVFrame *canvas = s->canvas_frame.f; + AVFrame *frame = s->frame; + int ret; + int width, height; + int pos_x, pos_y; + + ret = av_frame_copy_props(canvas, frame); + if (ret < 0) + return ret; + + if ((s->anmf_flags & ANMF_BLENDING_METHOD) == ANMF_BLENDING_METHOD_OVERWRITE + || frame->format == AV_PIX_FMT_YUV420P) { + // do not blend, overwrite + + if (canvas->format == AV_PIX_FMT_ARGB) { + width = s->width; + height = s->height; + pos_x = s->pos_x; + pos_y = s->pos_y; + + for (int y = 0; y < height; y++) { + const uint32_t *src = (uint32_t *) (frame->data[0] + y * frame->linesize[0]); + uint32_t *dst = (uint32_t *) (canvas->data[0] + (y + pos_y) * canvas->linesize[0]) + pos_x; + memcpy(dst, src, width * sizeof(uint32_t)); + } + } else /* if (canvas->format == AV_PIX_FMT_YUVA420P) */ { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + int plane; + + for (int comp = 0; comp < desc->nb_components; comp++) { + plane = desc->comp[comp].plane; + width = s->width; + height = s->height; + pos_x = s->pos_x; + pos_y = s->pos_y; + if (comp == 1 || comp == 2) { + width = AV_CEIL_RSHIFT(width, desc->log2_chroma_w); + height = AV_CEIL_RSHIFT(height, desc->log2_chroma_h); + pos_x = AV_CEIL_RSHIFT(pos_x, desc->log2_chroma_w); + pos_y = AV_CEIL_RSHIFT(pos_y, desc->log2_chroma_h); + } + + for (int y = 0; y < height; y++) { + const uint8_t *src = frame->data[plane] + y * frame->linesize[plane]; + uint8_t *dst = canvas->data[plane] + (y + pos_y) * canvas->linesize[plane] + pos_x; + memcpy(dst, src, width); + } + } + + if (desc->nb_components < 4) { + // frame does not have alpha, set alpha to 255 + desc = av_pix_fmt_desc_get(canvas->format); + plane = desc->comp[3].plane; + width = s->width; + height = s->height; + pos_x = s->pos_x; + pos_y = s->pos_y; + + for (int y = 0; y < height; y++) { + uint8_t *dst = canvas->data[plane] + (y + pos_y) * canvas->linesize[plane] + pos_x; + memset(dst, 255, width); + } + } + } + } else { + // alpha blending + + if (canvas->format == AV_PIX_FMT_ARGB) { + int src2_step[4] = { sizeof(uint32_t) }; + blend_alpha_argb(canvas->data, canvas->linesize, + (const uint8_t **) frame->data, frame->linesize, + (const uint8_t **) canvas->data, canvas->linesize, + src2_step, s->width, s->height, s->pos_x, s->pos_y); + } else /* if (canvas->format == AV_PIX_FMT_YUVA420P) */ { + int src2_step[4] = { 1, 1, 1, 1 }; + blend_alpha_yuva(s, canvas->data, canvas->linesize, + (const uint8_t **) frame->data, frame->linesize, + frame->format, + (const uint8_t **) canvas->data, canvas->linesize, + src2_step, s->width, s->height, s->pos_x, s->pos_y); + } + } + + return 0; +} + +static int copy_canvas_to_frame(WebPContext *s, AVFrame *frame, int key_frame) +{ + AVFrame *canvas = s->canvas_frame.f; + int ret; + + // VP8 decoder changed the width and height in AVCodecContext. + // Change it back to the canvas size. + ret = ff_set_dimensions(s->avctx, canvas->width, canvas->height); + if (ret < 0) + return ret; + + s->avctx->pix_fmt = canvas->format; + frame->format = canvas->format; + frame->width = canvas->width; + frame->height = canvas->height; + + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) + return ret; + + ret = av_frame_copy_props(frame, canvas); + if (ret < 0) + return ret; + + // blend the canvas with the background color into the output frame + if (canvas->format == AV_PIX_FMT_ARGB) { + int src2_step[4] = { 0 }; + const uint8_t *src2_data[4] = { &s->background_argb[0] }; + blend_alpha_argb(frame->data, frame->linesize, + (const uint8_t **) canvas->data, canvas->linesize, + (const uint8_t **) src2_data, src2_step, src2_step, + canvas->width, canvas->height, 0, 0); + } else /* if (canvas->format == AV_PIX_FMT_YUVA420P) */ { + int src2_step[4] = { 0, 0, 0, 0 }; + blend_alpha_yuva(s, frame->data, frame->linesize, + (const uint8_t **) canvas->data, canvas->linesize, + canvas->format, + s->background_data, src2_step, src2_step, + canvas->width, canvas->height, 0, 0); + } + + if (key_frame) { + frame->pict_type = AV_PICTURE_TYPE_I; + } else { + frame->pict_type = AV_PICTURE_TYPE_P; + } + + return 0; +} + +static int dispose_prev_frame_in_canvas(WebPContext *s) +{ + AVFrame *canvas = s->canvas_frame.f; + int width, height; + int pos_x, pos_y; + + if ((s->prev_anmf_flags & ANMF_DISPOSAL_METHOD) == ANMF_DISPOSAL_METHOD_BACKGROUND) { + // dispose to background + + if (canvas->format == AV_PIX_FMT_ARGB) { + width = s->prev_width; + height = s->prev_height; + pos_x = s->prev_pos_x; + pos_y = s->prev_pos_y; + + for (int y = 0; y < height; y++) { + uint32_t *dst = (uint32_t *) (canvas->data[0] + (y + pos_y) * canvas->linesize[0]) + pos_x; + memset(dst, 0, width * sizeof(uint32_t)); + } + } else /* if (canvas->format == AV_PIX_FMT_YUVA420P) */ { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(canvas->format); + int plane; + + for (int comp = 0; comp < desc->nb_components; comp++) { + plane = desc->comp[comp].plane; + width = s->prev_width; + height = s->prev_height; + pos_x = s->prev_pos_x; + pos_y = s->prev_pos_y; + if (comp == 1 || comp == 2) { + width = AV_CEIL_RSHIFT(width, desc->log2_chroma_w); + height = AV_CEIL_RSHIFT(height, desc->log2_chroma_h); + pos_x = AV_CEIL_RSHIFT(pos_x, desc->log2_chroma_w); + pos_y = AV_CEIL_RSHIFT(pos_y, desc->log2_chroma_h); + } + + for (int y = 0; y < height; y++) { + uint8_t *dst = canvas->data[plane] + (y + pos_y) * canvas->linesize[plane] + pos_x; + memset(dst, s->transparent_yuva[plane], width); + } + } + } + } + + return 0; +} + +static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, + int *got_frame, AVPacket *avpkt) +{ + WebPContext *s = avctx->priv_data; + int ret; + int key_frame = avpkt->flags & AV_PKT_FLAG_KEY; + + for (int i = 0; i < avpkt->side_data_elems; ++i) { + if (avpkt->side_data[i].type == AV_PKT_DATA_NEW_EXTRADATA) { + ret = webp_decode_frame_common(avctx, avpkt->side_data[i].data, + avpkt->side_data[i].size, + got_frame, key_frame); + if (ret < 0) + goto end; + } } - return avpkt->size; + *got_frame = 0; + + if (key_frame) { + // The canvas is passed from one thread to another in a sequence + // starting with a key frame followed by non-key frames. + // The key frame reports progress 1, + // the N-th non-key frame awaits progress N = s->await_progress + // and reports progress N + 1. + s->await_progress = 0; + } + + // reset the frame params + s->anmf_flags = 0; + s->width = 0; + s->height = 0; + s->pos_x = 0; + s->pos_y = 0; + s->has_alpha = 0; + + ret = webp_decode_frame_common(avctx, avpkt->data, avpkt->size, got_frame, key_frame); + if (ret < 0) + goto end; + + if (*got_frame) { + if (!(s->vp8x_flags & VP8X_FLAG_ANIMATION)) { + // no animation, output the decoded frame + av_frame_move_ref(p, s->frame); + } else { + if (!key_frame) { + ff_thread_await_progress(&s->canvas_frame, s->await_progress, 0); + + ret = dispose_prev_frame_in_canvas(s); + if (ret < 0) + goto end; + } + + ret = blend_frame_into_canvas(s); + if (ret < 0) + goto end; + + ret = copy_canvas_to_frame(s, p, key_frame); + if (ret < 0) + goto end; + + ff_thread_report_progress(&s->canvas_frame, s->await_progress + 1, 0); + } + + p->pts = avpkt->pts; + } + + ret = avpkt->size; + +end: + av_frame_unref(s->frame); + return ret; } static av_cold int webp_decode_init(AVCodecContext *avctx) { WebPContext *s = avctx->priv_data; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(AV_PIX_FMT_YUVA420P); + s->avctx = avctx; s->pkt = av_packet_alloc(); - if (!s->pkt) + s->canvas_frame.f = av_frame_alloc(); + s->frame = av_frame_alloc(); + if (!s->pkt || !s->canvas_frame.f || !s->frame) { + av_packet_free(&s->pkt); + av_frame_free(&s->canvas_frame.f); + av_frame_free(&s->frame); return AVERROR(ENOMEM); + } + + // prepare data pointers for YUVA background + for (int i = 0; i < 4; i++) + s->background_data[i] = &s->background_yuva[i]; + + // convert transparent black from RGBA to YUVA + s->transparent_yuva[desc->comp[0].plane] = RGB_TO_Y_CCIR(0, 0, 0); + s->transparent_yuva[desc->comp[1].plane] = RGB_TO_U_CCIR(0, 0, 0, 0); + s->transparent_yuva[desc->comp[2].plane] = RGB_TO_V_CCIR(0, 0, 0, 0); + s->transparent_yuva[desc->comp[3].plane] = 0; return 0; } @@ -1533,6 +2085,9 @@ static av_cold int webp_decode_close(AVCodecContext *avctx) WebPContext *s = avctx->priv_data; av_packet_free(&s->pkt); + ff_thread_release_ext_buffer(avctx, &s->canvas_frame); + av_frame_free(&s->canvas_frame.f); + av_frame_free(&s->frame); if (s->initialized) return ff_vp8_decode_free(avctx); @@ -1540,15 +2095,56 @@ static av_cold int webp_decode_close(AVCodecContext *avctx) return 0; } +static void webp_decode_flush(AVCodecContext *avctx) +{ + WebPContext *s = avctx->priv_data; + + ff_thread_release_ext_buffer(avctx, &s->canvas_frame); +} + +#if HAVE_THREADS +static int webp_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) +{ + WebPContext *wsrc = src->priv_data; + WebPContext *wdst = dst->priv_data; + int ret; + + if (dst == src) + return 0; + + ff_thread_release_ext_buffer(dst, &wdst->canvas_frame); + if (wsrc->canvas_frame.f->data[0] && + (ret = ff_thread_ref_frame(&wdst->canvas_frame, &wsrc->canvas_frame)) < 0) + return ret; + + wdst->vp8x_flags = wsrc->vp8x_flags; + wdst->canvas_width = wsrc->canvas_width; + wdst->canvas_height = wsrc->canvas_height; + wdst->prev_anmf_flags = wsrc->anmf_flags; + wdst->prev_width = wsrc->width; + wdst->prev_height = wsrc->height; + wdst->prev_pos_x = wsrc->pos_x; + wdst->prev_pos_y = wsrc->pos_y; + wdst->await_progress = wsrc->await_progress + 1; + + memcpy(wdst->background_argb, wsrc->background_argb, sizeof(wsrc->background_argb)); + memcpy(wdst->background_yuva, wsrc->background_yuva, sizeof(wsrc->background_yuva)); + + return 0; +} +#endif + const FFCodec ff_webp_decoder = { .p.name = "webp", CODEC_LONG_NAME("WebP image"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_WEBP, .priv_data_size = sizeof(WebPContext), + UPDATE_THREAD_CONTEXT(webp_update_thread_context), .init = webp_decode_init, FF_CODEC_DECODE_CB(webp_decode_frame), .close = webp_decode_close, + .flush = webp_decode_flush, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .caps_internal = FF_CODEC_CAP_ICC_PROFILES, + .caps_internal = FF_CODEC_CAP_ICC_PROFILES | FF_CODEC_CAP_ALLOCATE_PROGRESS, }; From patchwork Thu Jun 8 14:20:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42010 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp434587pzb; Thu, 8 Jun 2023 07:21:26 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5Z/NAsxjUGLshYSZ6Ki02Uy4BcR2XBI/SUdpGWa23QTcifBkUddQ/f1Y1IfpNchnMC+lQr X-Received: by 2002:a17:907:3f1c:b0:96a:fd8a:f840 with SMTP id hq28-20020a1709073f1c00b0096afd8af840mr10391051ejc.49.1686234086031; Thu, 08 Jun 2023 07:21:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686234086; cv=none; d=google.com; s=arc-20160816; b=llBkIGajL1NRUB/KcPL144XFyCONc34SUC/JQCgXV/m3QiZBqfPP8CMM1PdrdiwDdj vGSYd4RmITO5XIS5RNE5JksH35L+YAi9bInsqAg4e3mpaYJhOliE92NzCcpM9KYhiYnC x01x3QuZncV6vkysULV7gn6/+IbWlkFXzyvAZIFuD6pHIL6LlkwauQsF5ESKz4ye6SFw OW0Cd83d95+Lxk/w38rEr8XTphD/u7yqUEWFwyLzlzDqNou/Kjv1u1p55rMnQqNKJxJy gasg3/vuyD+zrp0uoT4kEwxzU3JnUCwe4ESDtG+OtebvDImN45jxxs9fdugruz5ErigP tbPw== 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=tGR90c8XikxcFWJXmVKW+B0fxO80rFVmJ3kqeowwsWs=; b=PGP18wYfPIO2X5OWY987AB5/BgZh0q1KRhBbQ9VnZhDHDv1BiJXoVlYH3bWF9ZHZtL EGcFbIoal+WnHg/19roO8oNhqFoZHnrrZe/z0Y8DMmukq4XcK3gr1ddbZDpY5y3k1Mfo bn/6bOC+vnRXPRQuOurJGlqdcPOzPTQxLWRPN73S3MCMSlt8IaBScSCbxz6LgSVwiX+N Ic+4zsVrUzxga/8d7A+bOvHTzD9wKSjzKBmB+1bpvdF1Jr+USe1e3eOM4AxcWzmzpTWl tRI9pwUkXJjNfoBv6cn2yEBTPnGtyPQXtn3h5ESPqD5SqfyJKRJ0TMdMGS3MlybXKFAq torQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=pV1iWwvN; 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=NONE dis=NONE) header.from=mail.de Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id t19-20020a170906269300b0096a90bba2ddsi742864ejc.29.2023.06.08.07.21.25; Thu, 08 Jun 2023 07:21:26 -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=@mail.de header.s=mailde202009 header.b=pV1iWwvN; 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=NONE dis=NONE) header.from=mail.de Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D7E1768C308; Thu, 8 Jun 2023 17:20:43 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from shout02.mail.de (shout02.mail.de [62.201.172.25]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4770C68C2E6 for ; Thu, 8 Jun 2023 17:20:37 +0300 (EEST) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout02.mail.de (Postfix) with ESMTP id 21099A0631 for ; Thu, 8 Jun 2023 16:20:32 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix03.mail.de (Postfix) with ESMTP id 0A96180229 for ; Thu, 8 Jun 2023 16:20:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1686234032; bh=OCb1Kt4K1lPDLsFB+fYaZtz+gNqHYdQ+1nOwqLG3MNQ=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=pV1iWwvNsp/gO2kcNlfBXBA6Q4eccsCj0/+K7PaK3EXtZMglKefbIr7IG+mvOWVAP Ho3iLgey3NpwtQ05/1KIQu8j3l97bQGbsYONK/5cW2yC+JNxJZnXdGoUcpGatS8gS4 oOmqrg5QU4pWM0x69DBM61AIO+zFH8B2atJPcd+kh46ViUo13MJe/RytcQyvPHCtdS uFpAxmbmB/cxG1kwdT73mGm419h8e3tnKf+fnUL9o94nGQwE7f02JrIIwwrRA8y/gF RaQYSi9IIxjxbxavoVmuLYn2lwes0arPCJGFm90PVKd8iv/E6BKDYsvtTAxIZRXqal f6KdmMH8XnhrQ== Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtp01.mail.de (Postfix) with ESMTPSA id B6786100B7D for ; Thu, 8 Jun 2023 16:20:31 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Thu, 8 Jun 2023 16:20:29 +0200 Message-Id: <20230608142029.16564-5-thilo.borgmann@mail.de> In-Reply-To: <20230608142029.16564-1-thilo.borgmann@mail.de> References: <20230608142029.16564-1-thilo.borgmann@mail.de> MIME-Version: 1.0 X-purgate: clean X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate-type: clean X-purgate-Ad: Categorized by eleven eXpurgate (R) http://www.eleven.de X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate: clean X-purgate-size: 6446 X-purgate-ID: 154282::1686234031-077FA7B6-466E95DB/0/0 Subject: [FFmpeg-devel] [PATCH v1 4/4] avcodec/webp: make init_canvas_frame static 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: cY/dPUFZMsGx --- libavcodec/webp.c | 143 +++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/libavcodec/webp.c b/libavcodec/webp.c index bee43fcf19..d3e3f85dd3 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -1337,7 +1337,77 @@ static int vp8_lossy_decode_frame(AVCodecContext *avctx, AVFrame *p, } return ret; } -int init_canvas_frame(WebPContext *s, int format, int key_frame); + +static int init_canvas_frame(WebPContext *s, int format, int key_frame) +{ + AVFrame *canvas = s->canvas_frame.f; + int height; + int ret; + + // canvas is needed only for animation + if (!(s->vp8x_flags & VP8X_FLAG_ANIMATION)) + return 0; + + // avoid init for non-key frames whose format and size did not change + if (!key_frame && + canvas->data[0] && + canvas->format == format && + canvas->width == s->canvas_width && + canvas->height == s->canvas_height) + return 0; + + // canvas changes within IPPP sequences will loose thread sync + // because of the ThreadFrame reallocation and will wait forever + // so if frame-threading is used, forbid canvas changes and unlock + // previous frames + if (!key_frame && canvas->data[0]) { + if (s->avctx->thread_count > 1) { + av_log(s->avctx, AV_LOG_WARNING, "Canvas change detected. The output will be damaged. Use -threads 1 to try decoding with best effort.\n"); + // unlock previous frames that have sent an _await() call + ff_thread_report_progress(&s->canvas_frame, INT_MAX, 0); + return AVERROR_PATCHWELCOME; + } else { + // warn for damaged frames + av_log(s->avctx, AV_LOG_WARNING, "Canvas change detected. The output will be damaged.\n"); + } + } + + s->avctx->pix_fmt = format; + canvas->format = format; + canvas->width = s->canvas_width; + canvas->height = s->canvas_height; + + // VP8 decoder changed the width and height in AVCodecContext. + // Change it back to the canvas size. + ret = ff_set_dimensions(s->avctx, s->canvas_width, s->canvas_height); + if (ret < 0) + return ret; + + ff_thread_release_ext_buffer(s->avctx, &s->canvas_frame); + ret = ff_thread_get_ext_buffer(s->avctx, &s->canvas_frame, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return ret; + + if (canvas->format == AV_PIX_FMT_ARGB) { + height = canvas->height; + memset(canvas->data[0], 0, height * canvas->linesize[0]); + } else /* if (canvas->format == AV_PIX_FMT_YUVA420P) */ { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(canvas->format); + for (int comp = 0; comp < desc->nb_components; comp++) { + int plane = desc->comp[comp].plane; + + if (comp == 1 || comp == 2) + height = AV_CEIL_RSHIFT(canvas->height, desc->log2_chroma_h); + else + height = FFALIGN(canvas->height, 1 << desc->log2_chroma_h); + + memset(canvas->data[plane], s->transparent_yuva[plane], + height * canvas->linesize[plane]); + } + } + + return 0; +} static int webp_decode_frame_common(AVCodecContext *avctx, uint8_t *data, int size, int *got_frame, int key_frame) @@ -1577,77 +1647,6 @@ exif_end: return size; } -int init_canvas_frame(WebPContext *s, int format, int key_frame) -{ - AVFrame *canvas = s->canvas_frame.f; - int height; - int ret; - - // canvas is needed only for animation - if (!(s->vp8x_flags & VP8X_FLAG_ANIMATION)) - return 0; - - // avoid init for non-key frames whose format and size did not change - if (!key_frame && - canvas->data[0] && - canvas->format == format && - canvas->width == s->canvas_width && - canvas->height == s->canvas_height) - return 0; - - // canvas changes within IPPP sequences will loose thread sync - // because of the ThreadFrame reallocation and will wait forever - // so if frame-threading is used, forbid canvas changes and unlock - // previous frames - if (!key_frame && canvas->data[0]) { - if (s->avctx->thread_count > 1) { - av_log(s->avctx, AV_LOG_WARNING, "Canvas change detected. The output will be damaged. Use -threads 1 to try decoding with best effort.\n"); - // unlock previous frames that have sent an _await() call - ff_thread_report_progress(&s->canvas_frame, INT_MAX, 0); - return AVERROR_PATCHWELCOME; - } else { - // warn for damaged frames - av_log(s->avctx, AV_LOG_WARNING, "Canvas change detected. The output will be damaged.\n"); - } - } - - s->avctx->pix_fmt = format; - canvas->format = format; - canvas->width = s->canvas_width; - canvas->height = s->canvas_height; - - // VP8 decoder changed the width and height in AVCodecContext. - // Change it back to the canvas size. - ret = ff_set_dimensions(s->avctx, s->canvas_width, s->canvas_height); - if (ret < 0) - return ret; - - ff_thread_release_ext_buffer(s->avctx, &s->canvas_frame); - ret = ff_thread_get_ext_buffer(s->avctx, &s->canvas_frame, AV_GET_BUFFER_FLAG_REF); - if (ret < 0) - return ret; - - if (canvas->format == AV_PIX_FMT_ARGB) { - height = canvas->height; - memset(canvas->data[0], 0, height * canvas->linesize[0]); - } else /* if (canvas->format == AV_PIX_FMT_YUVA420P) */ { - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(canvas->format); - for (int comp = 0; comp < desc->nb_components; comp++) { - int plane = desc->comp[comp].plane; - - if (comp == 1 || comp == 2) - height = AV_CEIL_RSHIFT(canvas->height, desc->log2_chroma_h); - else - height = FFALIGN(canvas->height, 1 << desc->log2_chroma_h); - - memset(canvas->data[plane], s->transparent_yuva[plane], - height * canvas->linesize[plane]); - } - } - - return 0; -} - /* * Blend src1 (foreground) and src2 (background) into dest, in ARGB format. * width, height are the dimensions of src1