From patchwork Tue Jul 25 08:58:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42966 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2987:b0:130:ccc6:6c4b with SMTP id f7csp82857pzh; Tue, 25 Jul 2023 01:59:11 -0700 (PDT) X-Google-Smtp-Source: APBJJlGKaM2ctArrJRlkFIzTUOkcWVS1oTWCDRb6IAUp2MAi7dSrXeKGew+OPZYrghDgAPC6LS85 X-Received: by 2002:a17:906:292:b0:992:a838:a564 with SMTP id 18-20020a170906029200b00992a838a564mr11322274ejf.28.1690275551109; Tue, 25 Jul 2023 01:59:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690275551; cv=none; d=google.com; s=arc-20160816; b=KOvke6hr3IyzFk960WSqLkY9IbAMvvJVhKGSe4UvDnzSnjOTOSaPu4tH9gL5q/JdR3 TUn9EOVdDgnFgoNmtMEvBwR7yhIYeYmj1gR0SyGHlnt9Mx6okRNcIErekfnHQux+zDq0 zQF7gz2m4Vf2DWtKhz4d6F5Ql92N1LkMSU80fSbJ9XAMZLqUq7puZNIbZyD/D1bJCns9 IOaUdsNqwpJ53ocbRLNgergK9qTEvL1YCbh1SmCvpyk57xZg1Xh41t75pbGeZaF4Uq2b 6De50koTGrSxuETVOFstc759mH32K9uFtmtRPmE6JxuiEK/mI2h+HFBy814vZMwV3pF+ kBpg== 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=LSJFPJrqHwUFU61ggs6fE5wRYA3v270Ed5KBmf17fxE=; fh=HD4ndZ9hLL4mcfDFnlTmTEEgN7ZZ8c1Zu9bV17rPaE8=; b=RibevuhlLsyQ10xHpeWYDcUE3NQidq3AtaT228GiNiasQ5q107D8sMTRHwul0bcTTi q8bv2nzZ/ze5cF++u0GBl6lTQuX/7lSrDB67XIxKEGBqtDsa14L3efbNhxinJBfM+IOh 451/D9zHI3FTWQ2bykDppjXB33SK5TXyS7LPqAE+N8WbnGgc2MJ+lDOit4FIxBGL2vit 515dV3m35mNWcI7nejpdnwOMWKOfBvKTrRtBdMtVusH+Ioza7r2L/Mc2ALA2lg12Vl3K uBFO0//P5St6v6CX2rjVOJjccg8PikblS8AY3BZVqg+Ekif6HH7syuPiqySaPUc72DcY Q+rg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=e8xYFxFc; 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=QUARANTINE sp=QUARANTINE 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 d26-20020a170906371a00b00992f7e90360si7787734ejc.727.2023.07.25.01.59.10; Tue, 25 Jul 2023 01:59:11 -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=e8xYFxFc; 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=QUARANTINE sp=QUARANTINE 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 797FD68C792; Tue, 25 Jul 2023 11:58:56 +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 A4BA268C768 for ; Tue, 25 Jul 2023 11:58:48 +0300 (EEST) Received: from postfix01.mail.de (postfix01.bt.mail.de [10.0.121.125]) by shout02.mail.de (Postfix) with ESMTP id 53945A01A4 for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix01.mail.de (Postfix) with ESMTP id 3A8A9801B2 for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1690275528; bh=Z8glE4xtKH3glfapW0X2m82i4DDCRgwZfUjxsg6IceY=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=e8xYFxFcGfdB+GAeTwRovPAXMP1rq3odIJOcEn5E9SJ1x45/3cXxPuB2St8djbJyr bMQIONJkNZDPC40uLTHJYGnmmj2XyW3T4fzNo3iM8EhnCUuNSJyB3jXvIXUWfIN8Nb 2rrGfNxsVIkcnQMuvvulIWL5RH+6FJWvxfHUCOZ88LpprWG90BizLpR1ByXmek3Tar 8XbUepmox+uocSR/wi4LkGz8fge9MOPfj9mB6j4X2wTY6Bk4kDjUVzH80QFdVlEVhc Bwv0nfJDUwyPV8aJVie9QjldjjlLGgUfv3Ba1tigV0uzVe2NB3ItCA+aNnLjVO25hW sc50QVSl9jfMQ== 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 06FE810012D for ; Tue, 25 Jul 2023 10:58:47 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jul 2023 10:58:40 +0200 Message-Id: <20230725085846.93593-2-thilo.borgmann@mail.de> In-Reply-To: <20230725085846.93593-1-thilo.borgmann@mail.de> References: <20230725085846.93593-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: 2020 X-purgate-ID: 154282::1690275528-FC7F07B6-8596111A/0/0 Subject: [FFmpeg-devel] [PATCH v4 1/7] 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: H3TnJmLEUFMo --- libavcodec/webp.c | 1 + libavcodec/webp.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 libavcodec/webp.h diff --git a/libavcodec/webp.c b/libavcodec/webp.c index d35cb66f8d..b0475c344a 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -52,6 +52,7 @@ #include "thread.h" #include "tiff_common.h" #include "vp8.h" +#include "webp.h" #define VP8X_FLAG_ANIMATION 0x02 #define VP8X_FLAG_XMP_METADATA 0x04 diff --git a/libavcodec/webp.h b/libavcodec/webp.h new file mode 100644 index 0000000000..53bf59e7cd --- /dev/null +++ b/libavcodec/webp.h @@ -0,0 +1,38 @@ +/* + * 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 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 + +#endif /* AVCODEC_WEBP_H */ From patchwork Tue Jul 25 08:58:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42967 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2987:b0:130:ccc6:6c4b with SMTP id f7csp82913pzh; Tue, 25 Jul 2023 01:59:21 -0700 (PDT) X-Google-Smtp-Source: APBJJlGybJw+Rw9Fd9CeUpldUeMwUdB6y83QCMYoZb6gD8VyYQnsAREpX0HcK8memntTmr8qy75H X-Received: by 2002:a17:907:2e19:b0:99b:6c47:1145 with SMTP id ig25-20020a1709072e1900b0099b6c471145mr11247612ejc.32.1690275560810; Tue, 25 Jul 2023 01:59:20 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690275560; cv=none; d=google.com; s=arc-20160816; b=mlmVIEkaCuGoUaaRZGgPna1glZWCyXdegqr/fbDovY1wYXcIIUyKFDnFQC1+YKa2+H CyuXAXFEBMStUfSwoKSxWCsvLMKx8o+JazGV+iccefcXm0q3iCzuE/wHUbH57rB6qRgi QhaqQhe1tVF+mn1ZSikEcNq9tzimIlwHZ8nqqeHACTXZHyXzOa0gzzUKxvCvniD+8vim hj02MhevQHXILclCeAzcIXk60EONm3fKu0LId/mnWMefRMO51Dxa3/JQ0i3T0gh/jPgl zCmgZxy34y+DCavnPvPpuiijc/TG14neJ+oWOqeQ7IaBjvpefA8Hy/+rWFzgX1hEZSQX Jxyg== 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=Z9hntZa9UWaWi53KeHvZDy8VnQgrNSQxkUSTA06YLMU=; fh=HD4ndZ9hLL4mcfDFnlTmTEEgN7ZZ8c1Zu9bV17rPaE8=; b=Auw4CuWqeqjQzljtywo8A+fpYQIDs5GXy9Ww4xZmEBsZvE0pba2XGTMRcfTJHpMyrf xoXcGkobapYXMOoYuvYR60yTiXbwN1TKT80CZ3eJF89n2tC9LGPF3obBvMbXg/AipVBp mrgHzXFliedP/4xG8NlyomcpjYQ6z5ka7uiubqimekSiEapYKquWEYfiDu4Z47pD1hFK DlO+ERUHDLaxdBlhMiBTz7eB3ZgtxV20FhkGMlRosItyoFgG8s8Oa9sujI0FfKARayrz NM0JWggjR/igF49hwdfQHhIxChvZe+nCKCyry21gLH50dMz7SXKVibdh047WWam67rpn yaDA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=u3Jl2zYD; 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=QUARANTINE sp=QUARANTINE 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 g15-20020a17090670cf00b00992cd4e0b83si7400966ejk.426.2023.07.25.01.59.20; Tue, 25 Jul 2023 01:59:20 -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=u3Jl2zYD; 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=QUARANTINE sp=QUARANTINE 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 62F3968C859; Tue, 25 Jul 2023 11:58:57 +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 EC90B68C768 for ; Tue, 25 Jul 2023 11:58:48 +0300 (EEST) Received: from postfix01.mail.de (postfix01.bt.mail.de [10.0.121.125]) by shout02.mail.de (Postfix) with ESMTP id 9870DA05BC for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix01.mail.de (Postfix) with ESMTP id 8046C801B2 for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1690275528; bh=TYGz2EQnHNbeYdN2pwgHL4sOIfaLv4DxsMpMofi+Go0=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=u3Jl2zYDLRn6rEigiMOgzrmtN63osUVNvrpbfCuoWglTggnE+hikY/UBkEHn+3RXI TbAJzd7NiKrxcZOSQNHfk+AhUa6pzP6NUCTa6R2ebSp2yCMPRU33X22LbT/szS/zc0 oVo/pE4BjimOnOMft67WN+dBJi6sLU3UiDwyGf4VJm8WSJs0+24s/FWKYHFmHVIf/3 gILR04rXQz5zZMlCrCd2Xi/I3Kq7UdiRV2mvqtbv9ye5t1TD98AvkN9eXO6NDyB9cA XxuTke/IgT+gdsO/ERDZRUp3Sff0xMJmIgnAVYfFCG6YFoegGg/izXoPzxpg91BjeH wV8ESEDk5djPQ== 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 491A210013A for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jul 2023 10:58:41 +0200 Message-Id: <20230725085846.93593-3-thilo.borgmann@mail.de> In-Reply-To: <20230725085846.93593-1-thilo.borgmann@mail.de> References: <20230725085846.93593-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: 581 X-purgate-ID: 154282::1690275528-FC7F07B6-783B7222/0/0 Subject: [FFmpeg-devel] [PATCH v4 2/7] avcodec/webp: remove unused definitions 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: qeuLUn6VpRoU --- libavcodec/webp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libavcodec/webp.c b/libavcodec/webp.c index b0475c344a..6ba81a6a99 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -60,8 +60,6 @@ #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 From patchwork Tue Jul 25 08:58:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42964 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2987:b0:130:ccc6:6c4b with SMTP id f7csp83015pzh; Tue, 25 Jul 2023 01:59:35 -0700 (PDT) X-Google-Smtp-Source: APBJJlFT/XsqNfi+gMc6niqBp0CZkixFM+9JCkrvlK0YQ/nCUzR5SceNj03TgBwWxbiHEMHDN8nB X-Received: by 2002:a50:fa82:0:b0:522:1e2f:99db with SMTP id w2-20020a50fa82000000b005221e2f99dbmr7745946edr.5.1690275575464; Tue, 25 Jul 2023 01:59:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690275575; cv=none; d=google.com; s=arc-20160816; b=WoWebHYHJdksD5b8N6RCkcbVCcfu0J4uj2fWe/hen8t3FC3d1/NseUwxzsWbRbRqY2 VkDVYtid/7Y21zJ6aQh18rBd5umyFtELGk0PD8+oK3ewprLuV1pc6q5NoEm9z+POElzi xZOj18HXejvzJ2KQ2ze7Kd+RAYJ+XFbb9TC7BgJPL/AFupnkzG1+Zzt2JNd/+5qajBTC MyswfWMASNpS/fEZygp6yRHVLfQ3PSGm9HxvYCbbl77MNOxYXQOTnX+wn6uP8+MMOlbo S1Cb/oi9FGsFf3mOa3yES7bzk1kdfmkwOsdKXX/FF7S01OScVaTjT1uNjLL3K4RcGgpW xjaQ== 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=kjnIlu+46juVopWYbYKp4bNgWlSHVpUdoTn8YUrfeLc=; fh=HD4ndZ9hLL4mcfDFnlTmTEEgN7ZZ8c1Zu9bV17rPaE8=; b=MZDGcQ3oarQAnyds4VpETS3pmNY+aIZZQosWpb0ptfK++q7QlLPLWKSOMsKhpGG7cY xjtgqZ0VwN83EJ7vSJHfpSDzmfqZ28z75z9Lq+Ly3QWtmH71418vgdKIj9uo+Ha9zsF8 y5zglJ+Ov+hZ8o734emgiRVz8jQH9hrJ+hx+T+jHsZNGzgddAMV17pM0VSDhhSFppFVx atZ36GNOp79SYEPbEpIYwkqyjNQAv1OOTV/TnMhHvTPP6N8ZIlBdWNlHKqsNpaIGnDkj YYaOlkOc89Vy73vIuvFXBu/7LQG5uCknUmdi+Gl5tva154uBIyVuct42RpW1GKj5A09m 22sg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=iGRJrYog; 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=QUARANTINE sp=QUARANTINE 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 j22-20020aa7c0d6000000b005221c27b489si4588178edp.534.2023.07.25.01.59.29; Tue, 25 Jul 2023 01:59:35 -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=iGRJrYog; 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=QUARANTINE sp=QUARANTINE 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 42A5268C875; Tue, 25 Jul 2023 11:58:58 +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 3FBAA68C768 for ; Tue, 25 Jul 2023 11:58:49 +0300 (EEST) Received: from postfix01.mail.de (postfix01.bt.mail.de [10.0.121.125]) by shout01.mail.de (Postfix) with ESMTP id D827CA01A9 for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix01.mail.de (Postfix) with ESMTP id BF1D3801B2 for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1690275528; bh=3Zbx0i+bRtlSwUiJQz77Tuzi17XuKmB92hijIOT5GWc=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=iGRJrYogbnP7dL1lAVQHiI/ckWojoXc3Fwj5ewht8S0kc0JSnutHTEEOG/+lN7eMd CtNJtoHL3WDpHf7Hd7h1DHOAx41VXLpAh4RmCsjmJCFvwf9ctfHvTIKOH6PYycNApf PUX9XxY1A/JiFKc3Xt0OKpXjwDuCyZenkn9D/l651KjWZxxIsrEbUsh4ZT7wtqU1/D DvYJRDXmmmJooXgsFgl/NSEnme3mKuGN6xSz7dDQaGtcHGh1StCldAgp3xik5LbfJ+ 3RySOcccgwD/z6DZYmLqRWFLONRqKRENPMSK0RrR5gyaRkDRRh8EouGGPnNJ3GpTRb ofRPZViH63mUw== 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 8F9BF1001A8 for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jul 2023 10:58:42 +0200 Message-Id: <20230725085846.93593-4-thilo.borgmann@mail.de> In-Reply-To: <20230725085846.93593-1-thilo.borgmann@mail.de> References: <20230725085846.93593-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: 6596 X-purgate-ID: 154282::1690275528-FC7F07B6-6C81B3EE/0/0 Subject: [FFmpeg-devel] [PATCH v4 3/7] 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: 3iBf/DkJoG0x --- libavcodec/webp_parser.c | 130 +++++++++++++++++++++++++++------------ 1 file changed, 89 insertions(+), 41 deletions(-) diff --git a/libavcodec/webp_parser.c b/libavcodec/webp_parser.c index bd5f94dac5..da853bb1f5 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; + 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 { - ctx->pc.state64 = 0; - goto restart; } - } 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 might be truncated remains before 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 Tue Jul 25 08:58:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42965 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2987:b0:130:ccc6:6c4b with SMTP id f7csp83115pzh; Tue, 25 Jul 2023 01:59:50 -0700 (PDT) X-Google-Smtp-Source: APBJJlHNfXh8ko4B3u/4cvBhCzOLqGgsTsTQhx1hjQ11BP5xED7LQ99Ei4GQtEAl9vagWVr2ky8K X-Received: by 2002:aa7:d74f:0:b0:51d:a308:74f9 with SMTP id a15-20020aa7d74f000000b0051da30874f9mr10579413eds.29.1690275590749; Tue, 25 Jul 2023 01:59:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690275590; cv=none; d=google.com; s=arc-20160816; b=UKWYN3CfT+1+IAJuRSRmNFEX8JFUwJB3vDfVUJ7YKy17Dwbvoeo0TFh3c7wi43Ino8 vrN9gcNw2sIgxn+76vLK3xmEc++LkAZHBVGDxdGLLgb5ypOsDvD2Qedv0RhS2u0nruy4 N56VyM9fjZEcIGPszfyprCereNVLmSWWZfBZsg4qApmlcydrJmrAHL8rDVUdf4D31460 Vv4YOzv2BQWmQSd3PRexmmiipkQKDsxQwd8knyYpM1E2MPrzm/0uwJib6Yz2UQz1qegu rLBFZAxQJUFwBonDgeWaQVcFIeHYYvEuGB7XMXIRucdvbJewygwwUMgdd6depy554PkT hV1w== 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=/Nhmbn4oqljG6Dfe50R5nhjELGxNQYg3LcKQe0SmBwU=; fh=HD4ndZ9hLL4mcfDFnlTmTEEgN7ZZ8c1Zu9bV17rPaE8=; b=LhnYZnnsQym0YB4aELuhrKiIYdogkreC3D2TqqBRyldn9cJLVgh+pEs+zCSVZ7YR/h wgxMQjhVsbC2lMkXCobETrCwPxTwI4IlWcVPikN5erbErP1qlSsQqKt/GkMWcMRhciZR dvq2prIVaskpiZhxoqWExwRsVYIlK0mRn2JwWiVwkTgItEuJM88PzwJqXWFFNgKUQ/t2 BO3KWG9eWB/wNpb6IXUQJewRrk9uP3MD+FXbm/0lM8T0dtT2HJC1lFKk5Uysw7xmQt9i Ra0M5wcd+t2ucsC3RkCSNDGx1kZtPo5wVISpmJxxPlMZ/R03ksbJBGeJdL7vflNpZlwh GpFQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=iJhpfNU7; 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=QUARANTINE sp=QUARANTINE 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 v4-20020aa7cd44000000b005219e26dddfsi7582830edw.664.2023.07.25.01.59.41; Tue, 25 Jul 2023 01:59:50 -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=iJhpfNU7; 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=QUARANTINE sp=QUARANTINE 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 34D2E68C88A; Tue, 25 Jul 2023 11:59:00 +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 1184468C80C for ; Tue, 25 Jul 2023 11:58:54 +0300 (EEST) Received: from postfix01.mail.de (postfix01.bt.mail.de [10.0.121.125]) by shout01.mail.de (Postfix) with ESMTP id 2E5E1A02C6 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix01.mail.de (Postfix) with ESMTP id 1516E801B2 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1690275529; bh=rl52gE9qHvov7/yBfH8/yz4xzDWZZxpl/IWgVeV6z+E=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=iJhpfNU7T+BPU7Y4IIe4aBjuU3fTHRJYpfo8S0ytSgBgHucAVd3NHO1/XXELDIULr PLnNjR8dcB2Ns7RzI8utLX6kTa9WjXQWtHob4r5Cnyj+emAGpd3AfXCxX3EKsNFOYf +xgG1nF/mWbuvj8me2o6euzzyjrwI3AH8u4PWUpSIJZSc7QN2KDxWo8T2J0t+5y2lL c6xRdWVxx/ntnr4mb9KCN1uKUhtrKZN8fqXZlSjpPRBcCOMxOn+ReKew7UhzRBwDCk xWJeHaakb4KWiMxG1WQ7C3dNBbGW2TzLWHG9ShIF0r0Dr9K6OnQNbnmErPS8RJROpR IqQfmkgFdv8/g== 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 CF63B10013A for ; Tue, 25 Jul 2023 10:58:48 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jul 2023 10:58:43 +0200 Message-Id: <20230725085846.93593-5-thilo.borgmann@mail.de> In-Reply-To: <20230725085846.93593-1-thilo.borgmann@mail.de> References: <20230725085846.93593-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: 42017 X-purgate-ID: 154282::1690275528-FC7F07B6-DC054CBB/0/0 Subject: [FFmpeg-devel] [PATCH v4 4/7] 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: B9bEpMVOlqsQ 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 | 719 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 663 insertions(+), 62 deletions(-) diff --git a/Changelog b/Changelog index 3876082844..271926ed8f 100644 --- a/Changelog +++ b/Changelog @@ -25,6 +25,7 @@ version : - Raw VVC bitstream parser, muxer and demuxer - Bitstream filter for editing metadata in VVC streams - Bitstream filter for converting VVC from MP4 to Annex B +- animated WebP parser/decoder version 6.0: - Radiance HDR image support diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 4406dd8318..47a38a4036 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 9411511e04..9f55381cf1 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -30,7 +30,7 @@ #include "version_major.h" #define LIBAVCODEC_VERSION_MINOR 22 -#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 6ba81a6a99..773ac171e6 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" @@ -192,6 +196,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 */ @@ -203,9 +209,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 */ @@ -569,7 +590,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) @@ -1067,7 +1088,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) { @@ -1090,7 +1111,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; } @@ -1107,7 +1127,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) @@ -1304,7 +1324,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"); @@ -1322,7 +1341,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, @@ -1332,41 +1351,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 }; @@ -1377,6 +1372,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. */ @@ -1384,10 +1383,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; @@ -1396,8 +1411,12 @@ 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; + + ret = vp8_lossless_decode_frame(avctx, s->frame, got_frame, + data + bytestream2_tell(&gb), chunk_size, 0); if (ret < 0) return ret; @@ -1406,14 +1425,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; @@ -1421,7 +1442,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"); @@ -1430,8 +1451,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); @@ -1458,14 +1480,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"); @@ -1478,7 +1499,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); @@ -1493,21 +1514,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", @@ -1523,21 +1587,514 @@ 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 avpkt->size; + 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 width, height; + int pos_x, pos_y; + + 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; + + 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; + AVFrame *canvas = s->canvas_frame.f; + 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; + } + } + + *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 (s->vp8x_flags & VP8X_FLAG_ANIMATION) { + // no animation, output the decoded frame + // VP8 decoder might have changed the width and height of the frame + AVFrame *frame = s->frame; + ret = av_frame_copy_props(canvas, frame); + if (ret < 0) + return ret; + + ret = ff_set_dimensions(s->avctx, canvas->width, canvas->height); + if (ret < 0) + return ret; + + s->avctx->pix_fmt = canvas->format; + } + + ff_thread_finish_setup(s->avctx); + + 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; } @@ -1547,6 +2104,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); @@ -1554,15 +2114,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 Tue Jul 25 08:58:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42968 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2987:b0:130:ccc6:6c4b with SMTP id f7csp83133pzh; Tue, 25 Jul 2023 01:59:53 -0700 (PDT) X-Google-Smtp-Source: APBJJlEf+YH2oPFk1jQFtCDTw6N6yiRtyW5bTi06Bj275Pihhq4gHNj7PXW7P7a2i1qDiyA9LFX7 X-Received: by 2002:a05:6512:3298:b0:4fb:822d:83ab with SMTP id p24-20020a056512329800b004fb822d83abmr8060463lfe.20.1690275593455; Tue, 25 Jul 2023 01:59:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690275593; cv=none; d=google.com; s=arc-20160816; b=qwXvNY+B9jluA0P/x5vF2IAEu7BS9AHWJOJlblZEpwGZUa/kvmpI0DNU3ryb2+73tl Y8J/0C4qg+64ar7OCU5dPMXIfhxJBQqoMtczF2dTQiF7Qe4gPgjXYyNSIUsW06tRXSgL 3vY5Ye96Z4Z6/c9Jmh1f7eE0UQkgpopXOpv11bZuWqUcarZL8IaQqJORoaazcvrfAWBh c7bOB8wBcFmBocb/K8clklBZTYt34HNY2eGLbmD6DlisDdBoW0Pi3tTIMdNjG92PD3U1 2RLZvG1N4nz025Gbc+vhFAjTuodEu2cAGYKk3Ua7vdWgGDrHacOLqwLuOb4WHIDhm0+P /BNQ== 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=4KA2OO0KqkY7CKhPCutgQZlIEDCSAw7F44wkzCvCLWs=; fh=HD4ndZ9hLL4mcfDFnlTmTEEgN7ZZ8c1Zu9bV17rPaE8=; b=cTAWdA5I8QfL6Bo2/hN6uRAZFqtTLS1eEc6QLXkU3uLsa/vfUuBvCgkJ1CSR44Our0 ajpw4HVliOACI3FPECcGKwPI//j1j+k0E+JP+iUScMKQTrxkfOyxfS6Kq5x5OC608zj/ aJa7fnybRGb7PFgSClXwJz7DYj8AlOfSR3a5WzbvFLez4XIKftxCkX0KakE/75ElETvl PD62PqDSawJwNcTEeA/pNFM3YWY8bSJrGp9FLrR2EZbf6hC0bOC2cGRgE8Xj/m6C1rJe rE6hOF5jbv5DxL8+g4v3yjS6Fi2Sv2494mltN1Y4PeXrBehvY4mfkG5OzcvajKSPAJwJ Kblw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=X8O0MOiR; 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=QUARANTINE sp=QUARANTINE 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 q6-20020a056402032600b0052237ca8f29si2378160edw.615.2023.07.25.01.59.52; Tue, 25 Jul 2023 01:59:53 -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=X8O0MOiR; 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=QUARANTINE sp=QUARANTINE 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 2FB3968C892; Tue, 25 Jul 2023 11:59:01 +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 21D2A68C878 for ; Tue, 25 Jul 2023 11:58:54 +0300 (EEST) Received: from postfix01.mail.de (postfix01.bt.mail.de [10.0.121.125]) by shout02.mail.de (Postfix) with ESMTP id 6E651A05C0 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix01.mail.de (Postfix) with ESMTP id 56AB7801B2 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1690275529; bh=ToE4Tf2kN+6Ubf7hMZykAWaylsVnYP2paX0q+rmpOHk=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=X8O0MOiRy+SAv+lkScGlNeE9VCN/2Vb2xeWSeWMSZN8RGhKbD1hdpYLXCPSOCtcif Ufmmc9yzD/7XtyH7jHUUE4H4lPAccp5jvvfgUkRhWYf7YwkDGBaz8QQucXoE70C9ie 5HHFXmH7iZ85U9YdNCar7E0cak3Zdc71AEo9XAYa0Bi/XtwWI4FjhQjmqWjNpIOXa+ mhLr+EB5MYWzZcsN6+ot9WYftlvM3cF2G8k35L3P3+Hb8h3RA8dG5YKkHqaGqObEqp kZGf4VUNakPyK7L+hAKlRUUv/gwc7jbUjGiB4YuhYZ5JcYTKL3cf2RXqhL+IvMVGio uuBvYizRscQMA== 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 252D61001A8 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jul 2023 10:58:44 +0200 Message-Id: <20230725085846.93593-6-thilo.borgmann@mail.de> In-Reply-To: <20230725085846.93593-1-thilo.borgmann@mail.de> References: <20230725085846.93593-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: 6450 X-purgate-ID: 154282::1690275529-FC7F07B6-75417204/0/0 Subject: [FFmpeg-devel] [PATCH v4 5/7] 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: w56NmIfVBza5 --- libavcodec/webp.c | 143 +++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/libavcodec/webp.c b/libavcodec/webp.c index 773ac171e6..83e1ed6778 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -1351,7 +1351,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 lose 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) @@ -1590,77 +1660,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 From patchwork Tue Jul 25 08:58:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42969 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2987:b0:130:ccc6:6c4b with SMTP id f7csp83207pzh; Tue, 25 Jul 2023 02:00:04 -0700 (PDT) X-Google-Smtp-Source: APBJJlE5aLp+TUB2q1BX0ok983u+V/Xqq6IhkMHdlkpUACAR3Ed0CSuMAf4Fgovy5Eaw0t74bcUj X-Received: by 2002:a17:907:985:b0:99b:56d4:82bb with SMTP id bf5-20020a170907098500b0099b56d482bbmr1906378ejc.6.1690275603663; Tue, 25 Jul 2023 02:00:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690275603; cv=none; d=google.com; s=arc-20160816; b=cr5d2O4uUp4v14ilY0Ejw3cX2QnAtwdhyVPC8aJYUps2oliPrEzLEGEABY3BK1KEAC ixf37eJVaplaHl7WNqO82OURK2LLdlrAst3g8xIK+uqnY0xmekZWu/yVgx/Xzzo/6qvR ytzMqmZuKpgsix4hodN+ITMjx0cTyG0VfPhqvICTQ7POCK0srZD41WfBWm5My4MEC2OS 2d3kPd2ee7063FbmcTeoibkJym5X5xpH0gFMsj3xJw6Mg5a8Sq/ee7RccN2xRKPSMTpY Gm3oFBoGRS1ze4UPElnyqtcMm9tYnDUsK3NwDEtRGuCaNnfHqJIZcKoD5bcEjcjndBBz axog== 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=/dl6aMgIvOUmGf8vcac/ZlbqoK91bbTZPSjHNYj1Q3A=; fh=HD4ndZ9hLL4mcfDFnlTmTEEgN7ZZ8c1Zu9bV17rPaE8=; b=OUHTT3sxbjfJMTt53qk+ydpFvhtHluy1y7lesiO4x4h6psSaniWC8vpSd6aAR2XQX4 eSYoj//AXoF4vk9uJykQb/rO3//snlLnK6tMSrFjclTm3YX6Rq3PEkUCfNiMwrx+AHI0 RgJbVlnK0HDjKtCsRXIdOfabfaWqzPSZpbERy4Jt71UrUeioxm/sULU0UeblI3RGt2gI t14Af8fZbKk/lUsVusukRypHyW5wipgFoUcfsRpgoxZAS1UDqu7T47S/kohwlV1vEoyC l0kbjI+o3sAzrNpcxX72ZqAi6tMGgWSza1l0qbB0eSGkyF2HlRXXvaw4055RF+xI0weH +T+g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b=rw6rI+oE; 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=QUARANTINE sp=QUARANTINE 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 n7-20020a170906378700b009895a6d847fsi7416223ejc.742.2023.07.25.02.00.03; Tue, 25 Jul 2023 02:00:03 -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=rw6rI+oE; 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=QUARANTINE sp=QUARANTINE 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 2E1AE68C80C; Tue, 25 Jul 2023 11:59:02 +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 59FC568C878 for ; Tue, 25 Jul 2023 11:58:54 +0300 (EEST) Received: from postfix01.mail.de (postfix01.bt.mail.de [10.0.121.125]) by shout02.mail.de (Postfix) with ESMTP id B6347A05C1 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix01.mail.de (Postfix) with ESMTP id 9EEB8801B2 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1690275529; bh=nIkqNu/XRbgcvjo6uRJU01zTw1dL5JQpdBlVtjiNu7M=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=rw6rI+oEZOS4xp158HfjfHL9x3GzWlAFuKllmvI8lngWuUWtklYxG+IqeJjugudmj fX9qXSQ2PzIKvAfsNi/unmW7OPAw1hIG/mbJIr7VwRZ6sKeWy96Qmq6TqX3ylzSZuW B3XogjGYCgC5bZ3uRB0da6P1rWlS0cWi8rxazoU5qhidB66hTOjhChjjDh7wGhw4YZ Wq3zwA3AsrR8BTxprVcBdlGAHE5cLUBMvwKzCQ1dorHuOKfeR/6df+OVmE9MBaRkKE IvEV30uRHESNzLcO9oWrKI/arfIG8SkCIo0a3AEkScTDslGcfq7O9D8QDF5CVNPNGL MLtDigzN387xA== 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 65CC410012D for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jul 2023 10:58:45 +0200 Message-Id: <20230725085846.93593-7-thilo.borgmann@mail.de> In-Reply-To: <20230725085846.93593-1-thilo.borgmann@mail.de> References: <20230725085846.93593-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: 35069 X-purgate-ID: 154282::1690275529-FC7F07B6-FD011EB8/0/0 Subject: [FFmpeg-devel] [PATCH v4 6/7] libavformat/webp: add WebP demuxer 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: dx8j06jbzHt8 From: Josef Zlomek Adds the demuxer of animated WebP files. It supports non-animated, animated, truncated, and concatenated files. Reading from a pipe (and other non-seekable inputs) is also supported. The WebP demuxer splits the input stream into packets containing one frame. It also marks the key frames properly. The loop count is ignored by default (same behaviour as animated PNG and GIF), it may be enabled by the option '-ignore_loop 0'. The frame rate is set according to the frame delay in the ANMF chunk. If the delay is too low, or the image is not animated, the default frame rate is set to 10 fps, similarly to other WebP libraries and browsers. The fate suite was updated accordingly. Signed-off-by: Josef Zlomek --- Changelog | 1 + doc/demuxers.texi | 28 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/version.h | 2 +- libavformat/webpdec.c | 733 ++++++++++++++++++++ tests/ref/fate/exif-image-webp | 12 +- tests/ref/fate/webp-rgb-lena-lossless | 2 +- tests/ref/fate/webp-rgb-lena-lossless-rgb24 | 2 +- tests/ref/fate/webp-rgb-lossless | 2 +- tests/ref/fate/webp-rgb-lossy-q80 | 2 +- tests/ref/fate/webp-rgba-lossless | 2 +- tests/ref/fate/webp-rgba-lossy-q80 | 2 +- 13 files changed, 777 insertions(+), 13 deletions(-) create mode 100644 libavformat/webpdec.c diff --git a/Changelog b/Changelog index 271926ed8f..9d16537e2d 100644 --- a/Changelog +++ b/Changelog @@ -26,6 +26,7 @@ version : - Bitstream filter for editing metadata in VVC streams - Bitstream filter for converting VVC from MP4 to Annex B - animated WebP parser/decoder +- animated WebP demuxer version 6.0: - Radiance HDR image support diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 2d33b47a56..084a9c97bb 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -936,4 +936,32 @@ which in turn, acts as a ceiling for the size of scripts that can be read. Default is 1 MiB. @end table +@section webp + +Animated WebP demuxer. + +It accepts the following options: + +@table @option +@item -min_delay @var{int} +Set the minimum valid delay between frames in milliseconds. +Range is 0 to 60000. Default value is 10. + +@item -max_webp_delay @var{int} +Set the maximum valid delay between frames in milliseconds. +Range is 0 to 16777215. Default value is 16777215 (over four hours), +the maximum value allowed by the specification. + +@item -default_delay @var{int} +Set the default delay between frames in milliseconds. +Range is 0 to 60000. Default value is 100. + +@item -ignore_loop @var{bool} +WebP files can contain information to loop a certain number of times +(or infinitely). If @option{ignore_loop} is set to true, then the loop +setting from the input will be ignored and looping will not occur. +If set to false, then looping will occur and will cycle the number +of times according to the WebP. Default value is true. +@end table + @c man end DEMUXERS diff --git a/libavformat/Makefile b/libavformat/Makefile index 4cb00f8700..4293dad0c6 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -616,6 +616,7 @@ OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ av1.o avlanguage.o OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o +OBJS-$(CONFIG_WEBP_DEMUXER) += webpdec.o OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 6324952bd2..98ed52bbde 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -500,6 +500,7 @@ extern const AVInputFormat ff_webm_dash_manifest_demuxer; extern const FFOutputFormat ff_webm_dash_manifest_muxer; extern const FFOutputFormat ff_webm_chunk_muxer; extern const FFOutputFormat ff_webp_muxer; +extern const AVInputFormat ff_webp_demuxer; extern const AVInputFormat ff_webvtt_demuxer; extern const FFOutputFormat ff_webvtt_muxer; extern const AVInputFormat ff_wsaud_demuxer; diff --git a/libavformat/version.h b/libavformat/version.h index 979952183c..787ee8c90b 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ #include "version_major.h" #define LIBAVFORMAT_VERSION_MINOR 10 -#define LIBAVFORMAT_VERSION_MICRO 100 +#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ diff --git a/libavformat/webpdec.c b/libavformat/webpdec.c new file mode 100644 index 0000000000..b4024809f7 --- /dev/null +++ b/libavformat/webpdec.c @@ -0,0 +1,733 @@ +/* + * WebP demuxer + * 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 demuxer. + */ + +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavcodec/webp.h" + +/** + * WebP headers (chunks before the first frame) and important info from them. + */ +typedef struct WebPHeaders { + int64_t offset; ///< offset in the (concatenated) file + uint8_t *data; ///< raw data + uint32_t size; ///< size of data + uint32_t webp_size; ///< size of the WebP file + int canvas_width; ///< width of the canvas + int canvas_height; ///< height of the canvas + int num_loop; ///< number of times to loop the animation +} WebPHeaders; + +typedef struct WebPDemuxContext { + const AVClass *class; + /** + * Time span in milliseconds before the next frame + * should be drawn on screen. + */ + int delay; + /** + * Minimum allowed delay between frames in milliseconds. + * Values below this threshold are considered to be invalid + * and set to value of default_delay. + */ + int min_delay; + int max_delay; + int default_delay; + + /* + * loop options + */ + int ignore_loop; ///< ignore loop setting + int num_loop; ///< number of times to loop the animation + int cur_loop; ///< current loop counter + int64_t file_start; ///< start position of the current animation file + int64_t infinite_loop_start; ///< start position of the infinite loop + + uint32_t remaining_size; ///< remaining size of the current animation file + int64_t seekback_buffer_end; ///< position of the end of the seek back buffer + int64_t prev_end_position; ///< position after the previous packet + size_t num_webp_headers; ///< number of (concatenated) WebP files' headers + WebPHeaders *webp_headers; ///< (concatenated) WebP files' headers + + /* + * variables for the key frame detection + */ + int nb_frames; ///< number of frames of the current animation file + int canvas_width; ///< width of the canvas + int canvas_height; ///< height of the canvas + int prev_width; ///< width of the previous frame + int prev_height; ///< height of the previous frame + int prev_anmf_flags; ///< flags of the previous frame + int prev_key_frame; ///< flag if the previous frame was a key frame +} WebPDemuxContext; + +/** + * Major web browsers display WebPs at ~10-15fps when rate is not + * explicitly set or have too low values. We assume default rate to be 10. + * Default delay = 1000 microseconds / 10fps = 100 milliseconds per frame. + */ +#define WEBP_DEFAULT_DELAY 100 +/** + * By default delay values less than this threshold considered to be invalid. + */ +#define WEBP_MIN_DELAY 10 + +static int webp_probe(const AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB32(b) == MKBETAG('R', 'I', 'F', 'F') && + AV_RB32(b + 8) == MKBETAG('W', 'E', 'B', 'P')) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int ensure_seekback(AVFormatContext *s, int64_t bytes) +{ + WebPDemuxContext *wdc = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + + int64_t pos = avio_tell(pb); + if (pos < 0) + return pos; + + if (pos + bytes <= wdc->seekback_buffer_end) + return 0; + + if ((ret = ffio_ensure_seekback(pb, bytes)) < 0) + return ret; + + wdc->seekback_buffer_end = pos + bytes; + return 0; +} + +static int resync(AVFormatContext *s, int seek_to_start) +{ + WebPDemuxContext *wdc = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int i; + uint64_t state = 0; + + // ensure seek back for the file header and the first chunk header + if ((ret = ensure_seekback(s, 12 + 8)) < 0) + return ret; + + for (i = 0; i < 12; i++) { + state = (state << 8) | avio_r8(pb); + if (i == 11) { + if ((uint32_t) state == MKBETAG('W', 'E', 'B', 'P')) + break; + i -= 4; + } + if (i == 7) { + // ensure seek back for the rest of file header and the chunk header + if ((ret = ensure_seekback(s, 4 + 8)) < 0) + return ret; + + if ((state >> 32) != MKBETAG('R', 'I', 'F', 'F')) + i--; + else { + uint32_t fsize = av_bswap32(state); + if (!(fsize > 15 && fsize <= UINT32_MAX - 10)) + i -= 4; + else + wdc->remaining_size = fsize - 4; + } + } + if (avio_feof(pb)) + return AVERROR_EOF; + } + + wdc->file_start = avio_tell(pb) - 12; + + if (seek_to_start) { + if ((ret = avio_seek(pb, -12, SEEK_CUR)) < 0) + return ret; + wdc->remaining_size += 12; + } + + return 0; +} + +static int is_key_frame(AVFormatContext *s, int has_alpha, int anmf_flags, + int width, int height) +{ + WebPDemuxContext *wdc = s->priv_data; + + if (wdc->nb_frames == 1) + return 1; + + if (width == wdc->canvas_width && + height == wdc->canvas_height && + (!has_alpha || (anmf_flags & ANMF_BLENDING_METHOD) == ANMF_BLENDING_METHOD_OVERWRITE)) + return 1; + + if ((wdc->prev_anmf_flags & ANMF_DISPOSAL_METHOD) == ANMF_DISPOSAL_METHOD_BACKGROUND && + (wdc->prev_key_frame || (wdc->prev_width == wdc->canvas_width && + wdc->prev_height == wdc->canvas_height))) + return 1; + + return 0; +} + +static int webp_read_header(AVFormatContext *s) +{ + WebPDemuxContext *wdc = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int ret, n; + uint32_t chunk_type, chunk_size; + int canvas_width = 0; + int canvas_height = 0; + int width = 0; + int height = 0; + int is_frame = 0; + + wdc->delay = wdc->default_delay; + wdc->num_loop = 1; + wdc->infinite_loop_start = -1; + + if ((ret = resync(s, 0)) < 0) + return ret; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + while (!is_frame && wdc->remaining_size > 0 && !avio_feof(pb)) { + chunk_type = avio_rl32(pb); + chunk_size = avio_rl32(pb); + if (chunk_size == UINT32_MAX) + return AVERROR_INVALIDDATA; + chunk_size += chunk_size & 1; + if (avio_feof(pb)) + break; + + if (wdc->remaining_size < 8 + chunk_size) + return AVERROR_INVALIDDATA; + wdc->remaining_size -= 8 + chunk_size; + + // ensure seek back for the chunk body and the next chunk header + if ((ret = ensure_seekback(s, chunk_size + 8)) < 0) + return ret; + + switch (chunk_type) { + case MKTAG('V', 'P', '8', 'X'): + if (chunk_size >= 10) { + avio_skip(pb, 4); + canvas_width = avio_rl24(pb) + 1; + canvas_height = avio_rl24(pb) + 1; + ret = avio_skip(pb, chunk_size - 10); + } else + ret = avio_skip(pb, chunk_size); + break; + case MKTAG('V', 'P', '8', ' '): + if (chunk_size >= 10) { + avio_skip(pb, 6); + width = avio_rl16(pb) & 0x3fff; + height = avio_rl16(pb) & 0x3fff; + is_frame = 1; + ret = avio_skip(pb, chunk_size - 10); + } else + ret = avio_skip(pb, chunk_size); + break; + case MKTAG('V', 'P', '8', 'L'): + if (chunk_size >= 5) { + avio_skip(pb, 1); + n = avio_rl32(pb); + width = (n & 0x3fff) + 1; // first 14 bits + height = ((n >> 14) & 0x3fff) + 1; // next 14 bits + is_frame = 1; + ret = avio_skip(pb, chunk_size - 5); + } else + ret = avio_skip(pb, chunk_size); + break; + case MKTAG('A', 'N', 'M', 'F'): + if (chunk_size >= 12) { + avio_skip(pb, 6); + width = avio_rl24(pb) + 1; + height = avio_rl24(pb) + 1; + is_frame = 1; + ret = avio_skip(pb, chunk_size - 12); + } else + ret = avio_skip(pb, chunk_size); + break; + default: + ret = avio_skip(pb, chunk_size); + break; + } + + if (ret < 0) + return ret; + + // fallback if VP8X chunk was not present + if (!canvas_width && width > 0) + canvas_width = width; + if (!canvas_height && height > 0) + canvas_height = height; + } + + // WebP format operates with time in "milliseconds", therefore timebase is 1/1000 + avpriv_set_pts_info(st, 64, 1, 1000); + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_WEBP; + st->codecpar->codec_tag = MKTAG('W', 'E', 'B', 'P'); + st->codecpar->width = canvas_width; + st->codecpar->height = canvas_height; + st->start_time = 0; + + // jump to start because WebP decoder needs header data too + if ((ret = avio_seek(pb, wdc->file_start, SEEK_SET)) < 0) + return ret; + wdc->remaining_size = 0; + + return 0; +} + +static WebPHeaders *webp_headers_lower_or_equal(WebPHeaders *headers, size_t n, + int64_t offset) +{ + size_t s, e; + + if (n == 0) + return NULL; + if (headers[0].offset > offset) + return NULL; + + s = 0; + e = n - 1; + while (s < e) { + size_t mid = (s + e + 1) / 2; + if (headers[mid].offset == offset) + return &headers[mid]; + else if (headers[mid].offset > offset) + e = mid - 1; + else + s = mid; + } + + return &headers[s]; +} + +static int append_chunk(WebPHeaders *headers, AVIOContext *pb, + uint32_t chunk_size) +{ + uint32_t previous_size = headers->size; + uint8_t *new_data; + + if (headers->size > UINT32_MAX - chunk_size) + return AVERROR_INVALIDDATA; + + new_data = av_realloc(headers->data, headers->size + chunk_size); + if (!new_data) + return AVERROR(ENOMEM); + + headers->data = new_data; + headers->size += chunk_size; + + return avio_read(pb, headers->data + previous_size, chunk_size); +} + +static int webp_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + WebPDemuxContext *wdc = s->priv_data; + AVIOContext *pb = s->pb; + int ret, n; + int64_t packet_start = avio_tell(pb), packet_end; + uint32_t chunk_type, chunk_size; + int width = 0, height = 0; + int is_frame = 0; + int key_frame = 0; + int anmf_flags = 0; + int has_alpha = 0; + int reading_headers = 0; + int reset_key_frame = 0; + WebPHeaders *headers = NULL; + + if (packet_start != wdc->prev_end_position) { + // seek occurred, find the corresponding WebP headers + headers = webp_headers_lower_or_equal(wdc->webp_headers, wdc->num_webp_headers, + packet_start); + if (!headers) + return AVERROR_BUG; + + wdc->file_start = headers->offset; + wdc->remaining_size = headers->webp_size - (packet_start - headers->offset); + wdc->canvas_width = headers->canvas_width; + wdc->canvas_height = headers->canvas_height; + wdc->num_loop = headers->num_loop; + wdc->cur_loop = 0; + reset_key_frame = 1; + } + + if (wdc->remaining_size == 0) { + // if the loop count is finite, loop the current animation + if (avio_tell(pb) != wdc->file_start && + !wdc->ignore_loop && wdc->num_loop > 1 && ++wdc->cur_loop < wdc->num_loop) { + if ((ret = avio_seek(pb, wdc->file_start, SEEK_SET)) < 0) + return ret; + packet_start = avio_tell(pb); + } else { + // start of a new animation file + wdc->delay = wdc->default_delay; + if (wdc->num_loop) + wdc->num_loop = 1; + } + + // resync to the start of the next file + ret = resync(s, 1); + if (ret == AVERROR_EOF) { + // we reached EOF, if the loop count is infinite, loop the whole input + if (!wdc->ignore_loop && !wdc->num_loop) { + if ((ret = avio_seek(pb, wdc->infinite_loop_start, SEEK_SET)) < 0) + return ret; + ret = resync(s, 1); + } else { + wdc->prev_end_position = avio_tell(pb); + return AVERROR_EOF; + } + } + if (ret < 0) + return ret; + packet_start = avio_tell(pb); + + reset_key_frame = 1; + } + + if (reset_key_frame) { + // reset variables used for key frame detection + wdc->nb_frames = 0; + wdc->canvas_width = 0; + wdc->canvas_height = 0; + wdc->prev_width = 0; + wdc->prev_height = 0; + wdc->prev_anmf_flags = 0; + wdc->prev_key_frame = 0; + } + + if (packet_start == wdc->file_start) { + headers = webp_headers_lower_or_equal(wdc->webp_headers, wdc->num_webp_headers, + packet_start); + if (!headers || headers->offset != wdc->file_start) { + // grow the array of WebP files' headers + wdc->num_webp_headers++; + wdc->webp_headers = av_realloc_f(wdc->webp_headers, + wdc->num_webp_headers, + sizeof(WebPHeaders)); + if (!wdc->webp_headers) + return AVERROR(ENOMEM); + + headers = &wdc->webp_headers[wdc->num_webp_headers - 1]; + memset(headers, 0, sizeof(*headers)); + headers->offset = wdc->file_start; + } else { + // headers for this WebP file have been already read, skip them + if ((ret = avio_seek(pb, headers->size, SEEK_CUR)) < 0) + return ret; + packet_start = avio_tell(pb); + + wdc->remaining_size = headers->webp_size - headers->size; + wdc->canvas_width = headers->canvas_width; + wdc->canvas_height = headers->canvas_height; + + if (wdc->cur_loop >= wdc->num_loop) + wdc->cur_loop = 0; + wdc->num_loop = headers->num_loop; + } + } + + while (wdc->remaining_size > 0 && !avio_feof(pb)) { + chunk_type = avio_rl32(pb); + chunk_size = avio_rl32(pb); + if (chunk_size == UINT32_MAX) + return AVERROR_INVALIDDATA; + chunk_size += chunk_size & 1; + + if (avio_feof(pb)) + break; + + // dive into RIFF chunk and do not ensure seek back for the whole file + if (chunk_type == MKTAG('R', 'I', 'F', 'F') && chunk_size > 4) + chunk_size = 4; + + // ensure seek back for the chunk body and the next chunk header + if ((ret = ensure_seekback(s, chunk_size + 8)) < 0) + return ret; + + switch (chunk_type) { + case MKTAG('R', 'I', 'F', 'F'): + if (avio_tell(pb) != wdc->file_start + 8) { + // premature RIFF found, shorten the file size + WebPHeaders *tmp = webp_headers_lower_or_equal(wdc->webp_headers, + wdc->num_webp_headers, + avio_tell(pb)); + tmp->webp_size -= wdc->remaining_size; + wdc->remaining_size = 0; + goto flush; + } + + reading_headers = 1; + if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 || + (ret = append_chunk(headers, pb, 8 + chunk_size)) < 0) + return ret; + packet_start = avio_tell(pb); + + headers->offset = wdc->file_start; + headers->webp_size = 8 + AV_RL32(headers->data + headers->size - chunk_size - 4); + break; + case MKTAG('V', 'P', '8', 'X'): + reading_headers = 1; + if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 || + (ret = append_chunk(headers, pb, 8 + chunk_size)) < 0) + return ret; + packet_start = avio_tell(pb); + + if (chunk_size >= 10) { + headers->canvas_width = AV_RL24(headers->data + headers->size - chunk_size + 4) + 1; + headers->canvas_height = AV_RL24(headers->data + headers->size - chunk_size + 7) + 1; + } + break; + case MKTAG('A', 'N', 'I', 'M'): + reading_headers = 1; + if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 || + (ret = append_chunk(headers, pb, 8 + chunk_size)) < 0) + return ret; + packet_start = avio_tell(pb); + + if (chunk_size >= 6) { + headers->num_loop = AV_RL16(headers->data + headers->size - chunk_size + 4); + wdc->num_loop = headers->num_loop; + wdc->cur_loop = 0; + if (!wdc->ignore_loop && wdc->num_loop != 1) { + // ensure seek back for the rest of the file + // and for the header of the next concatenated file + uint32_t loop_end = wdc->remaining_size - chunk_size + 12; + if ((ret = ensure_seekback(s, loop_end)) < 0) + return ret; + + if (!wdc->num_loop && wdc->infinite_loop_start < 0) + wdc->infinite_loop_start = wdc->file_start; + } + } + break; + case MKTAG('V', 'P', '8', ' '): + if (is_frame) + // found a start of the next non-animated frame + goto flush; + is_frame = 1; + + reading_headers = 0; + if (chunk_size >= 10) { + avio_skip(pb, 6); + width = avio_rl16(pb) & 0x3fff; + height = avio_rl16(pb) & 0x3fff; + wdc->nb_frames++; + ret = avio_skip(pb, chunk_size - 10); + } else + ret = avio_skip(pb, chunk_size); + break; + case MKTAG('V', 'P', '8', 'L'): + if (is_frame) + // found a start of the next non-animated frame + goto flush; + is_frame = 1; + + reading_headers = 0; + if (chunk_size >= 5) { + avio_skip(pb, 1); + n = avio_rl32(pb); + width = (n & 0x3fff) + 1; // first 14 bits + height = ((n >> 14) & 0x3fff) + 1; ///next 14 bits + has_alpha = (n >> 28) & 1; // next 1 bit + wdc->nb_frames++; + ret = avio_skip(pb, chunk_size - 5); + } else + ret = avio_skip(pb, chunk_size); + break; + case MKTAG('A', 'N', 'M', 'F'): + if (is_frame) + // found a start of the next animated frame + goto flush; + + reading_headers = 0; + if (chunk_size >= 16) { + avio_skip(pb, 6); + width = avio_rl24(pb) + 1; + height = avio_rl24(pb) + 1; + wdc->delay = avio_rl24(pb); + anmf_flags = avio_r8(pb); + if (wdc->delay < wdc->min_delay) + wdc->delay = wdc->default_delay; + wdc->delay = FFMIN(wdc->delay, wdc->max_delay); + // dive into the chunk to set the has_alpha flag + chunk_size = 16; + ret = 0; + } else + ret = avio_skip(pb, chunk_size); + break; + case MKTAG('A', 'L', 'P', 'H'): + reading_headers = 0; + has_alpha = 1; + ret = avio_skip(pb, chunk_size); + break; + default: + if (reading_headers) { + if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 || + (ret = append_chunk(headers, pb, 8 + chunk_size)) < 0) + return ret; + packet_start = avio_tell(pb); + } else + ret = avio_skip(pb, chunk_size); + break; + } + if (ret == AVERROR_EOF) { + // EOF was reached but the position may still be in the middle + // of the buffer. Seek to the end of the buffer so that EOF is + // handled properly in the next invocation of webp_read_packet. + if ((ret = avio_seek(pb, pb->buf_end - pb->buf_ptr, SEEK_CUR) < 0)) + return ret; + wdc->prev_end_position = avio_tell(pb); + wdc->remaining_size = 0; + return AVERROR_EOF; + } + if (ret < 0) + return ret; + + // fallback if VP8X chunk was not present + if (headers) { + if (!headers->canvas_width && width > 0) + headers->canvas_width = width; + if (!headers->canvas_height && height > 0) + headers->canvas_height = height; + } + + if (wdc->remaining_size < 8 + chunk_size) + return AVERROR_INVALIDDATA; + wdc->remaining_size -= 8 + chunk_size; + + packet_end = avio_tell(pb); + } + + if (wdc->remaining_size > 0 && avio_feof(pb)) { + // premature EOF, shorten the file size + WebPHeaders *tmp = webp_headers_lower_or_equal(wdc->webp_headers, + wdc->num_webp_headers, + avio_tell(pb)); + tmp->webp_size -= wdc->remaining_size; + wdc->remaining_size = 0; + } + +flush: + if ((ret = avio_seek(pb, packet_start, SEEK_SET)) < 0) + return ret; + + if ((ret = av_get_packet(pb, pkt, packet_end - packet_start)) < 0) + return ret; + + wdc->prev_end_position = packet_end; + + if (headers && headers->data) { + uint8_t *data = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + headers->size); + if (!data) + return AVERROR(ENOMEM); + memcpy(data, headers->data, headers->size); + + ffstream(s->streams[0])->need_context_update = 1; + s->streams[0]->codecpar->width = headers->canvas_width; + s->streams[0]->codecpar->height = headers->canvas_height; + + // copy the fields needed for the key frame detection + wdc->canvas_width = headers->canvas_width; + wdc->canvas_height = headers->canvas_height; + } + + key_frame = is_frame && is_key_frame(s, has_alpha, anmf_flags, width, height); + if (key_frame) + pkt->flags |= AV_PKT_FLAG_KEY; + else + pkt->flags &= ~AV_PKT_FLAG_KEY; + + wdc->prev_width = width; + wdc->prev_height = height; + wdc->prev_anmf_flags = anmf_flags; + wdc->prev_key_frame = key_frame; + + pkt->stream_index = 0; + pkt->duration = is_frame ? wdc->delay : 0; + pkt->pts = pkt->dts = AV_NOPTS_VALUE; + + if (is_frame && wdc->nb_frames == 1) + s->streams[0]->r_frame_rate = (AVRational) {1000, pkt->duration}; + + return ret; +} + +static int webp_read_close(AVFormatContext *s) +{ + WebPDemuxContext *wdc = s->priv_data; + + for (size_t i = 0; i < wdc->num_webp_headers; ++i) + av_freep(&wdc->webp_headers[i].data); + av_freep(&wdc->webp_headers); + wdc->num_webp_headers = 0; + + return 0; +} + +static const AVOption options[] = { + { "min_delay" , "minimum valid delay between frames (in milliseconds)", offsetof(WebPDemuxContext, min_delay) , AV_OPT_TYPE_INT, {.i64 = WEBP_MIN_DELAY} , 0, 1000 * 60, AV_OPT_FLAG_DECODING_PARAM }, + { "max_webp_delay", "maximum valid delay between frames (in milliseconds)", offsetof(WebPDemuxContext, max_delay) , AV_OPT_TYPE_INT, {.i64 = 0xffffff} , 0, 0xffffff , AV_OPT_FLAG_DECODING_PARAM }, + { "default_delay" , "default delay between frames (in milliseconds)" , offsetof(WebPDemuxContext, default_delay), AV_OPT_TYPE_INT, {.i64 = WEBP_DEFAULT_DELAY}, 0, 1000 * 60, AV_OPT_FLAG_DECODING_PARAM }, + { "ignore_loop" , "ignore loop setting" , offsetof(WebPDemuxContext, ignore_loop) , AV_OPT_TYPE_BOOL,{.i64 = 1} , 0, 1 , AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass demuxer_class = { + .class_name = "WebP demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +AVInputFormat ff_webp_demuxer = { + .name = "webp", + .long_name = NULL_IF_CONFIG_SMALL("WebP image"), + .priv_data_size = sizeof(WebPDemuxContext), + .read_probe = webp_probe, + .read_header = webp_read_header, + .read_packet = webp_read_packet, + .read_close = webp_read_close, + .flags = AVFMT_GENERIC_INDEX, + .priv_class = &demuxer_class, +}; diff --git a/tests/ref/fate/exif-image-webp b/tests/ref/fate/exif-image-webp index 783abefc60..e477812a9d 100644 --- a/tests/ref/fate/exif-image-webp +++ b/tests/ref/fate/exif-image-webp @@ -8,12 +8,12 @@ pkt_dts=0 pkt_dts_time=0.000000 best_effort_timestamp=0 best_effort_timestamp_time=0.000000 -pkt_duration=1 -pkt_duration_time=0.040000 -duration=1 -duration_time=0.040000 -pkt_pos=0 -pkt_size=39276 +pkt_duration=100 +pkt_duration_time=0.100000 +duration=100 +duration_time=0.100000 +pkt_pos=30 +pkt_size=39246 width=400 height=225 crop_top=0 diff --git a/tests/ref/fate/webp-rgb-lena-lossless b/tests/ref/fate/webp-rgb-lena-lossless index c00715a5e7..e784c501eb 100644 --- a/tests/ref/fate/webp-rgb-lena-lossless +++ b/tests/ref/fate/webp-rgb-lena-lossless @@ -1,4 +1,4 @@ -#tb 0: 1/25 +#tb 0: 1/10 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 128x128 diff --git a/tests/ref/fate/webp-rgb-lena-lossless-rgb24 b/tests/ref/fate/webp-rgb-lena-lossless-rgb24 index 7f8f550afe..395a01fa1b 100644 --- a/tests/ref/fate/webp-rgb-lena-lossless-rgb24 +++ b/tests/ref/fate/webp-rgb-lena-lossless-rgb24 @@ -1,4 +1,4 @@ -#tb 0: 1/25 +#tb 0: 1/10 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 128x128 diff --git a/tests/ref/fate/webp-rgb-lossless b/tests/ref/fate/webp-rgb-lossless index 8dbdfd6887..1db3ce70f7 100644 --- a/tests/ref/fate/webp-rgb-lossless +++ b/tests/ref/fate/webp-rgb-lossless @@ -1,4 +1,4 @@ -#tb 0: 1/25 +#tb 0: 1/10 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 12x8 diff --git a/tests/ref/fate/webp-rgb-lossy-q80 b/tests/ref/fate/webp-rgb-lossy-q80 index f61d75ac13..cd43415b95 100644 --- a/tests/ref/fate/webp-rgb-lossy-q80 +++ b/tests/ref/fate/webp-rgb-lossy-q80 @@ -1,4 +1,4 @@ -#tb 0: 1/25 +#tb 0: 1/10 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 12x8 diff --git a/tests/ref/fate/webp-rgba-lossless b/tests/ref/fate/webp-rgba-lossless index bb654ae442..2f763c6c46 100644 --- a/tests/ref/fate/webp-rgba-lossless +++ b/tests/ref/fate/webp-rgba-lossless @@ -1,4 +1,4 @@ -#tb 0: 1/25 +#tb 0: 1/10 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 12x8 diff --git a/tests/ref/fate/webp-rgba-lossy-q80 b/tests/ref/fate/webp-rgba-lossy-q80 index d2c2aa3fce..6b114f772e 100644 --- a/tests/ref/fate/webp-rgba-lossy-q80 +++ b/tests/ref/fate/webp-rgba-lossy-q80 @@ -1,4 +1,4 @@ -#tb 0: 1/25 +#tb 0: 1/10 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 12x8 From patchwork Tue Jul 25 08:58:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 42970 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2987:b0:130:ccc6:6c4b with SMTP id f7csp83314pzh; Tue, 25 Jul 2023 02:00:13 -0700 (PDT) X-Google-Smtp-Source: APBJJlExl1tyRCW2h0Y4szMLSyVvrL199dro8ckKz3k5CusUL4HcyuCYzoN56c8z3OxO4bhwhvtB X-Received: by 2002:a17:906:cc0e:b0:99b:465c:fb9f with SMTP id ml14-20020a170906cc0e00b0099b465cfb9fmr12652565ejb.8.1690275613149; Tue, 25 Jul 2023 02:00:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690275613; cv=none; d=google.com; s=arc-20160816; b=Ah1poIHJbpWtCfS5tMs5/QUvsQFC7kCZ8VkOWL3uOVmcpqZ8PJZk8zP4VOv0APEUFU fisAVhMDOsYykrIpWIMIjMVsogicqgSJI67ESm9mkiCfWs43rGN8ULOevOMuEKUv2Gpo q4pOnuRfNyOB/Ss/pOfEnBkSOitf/uS7gKAjJsRs9Jh4wbHVk6cPSInrKcO48HsXvEic eb1syyONR9a4wu3TmEZrMLAp5Ry0an3ykTTNmVCYbncejW+cqOp+yWbYk+G5yM79yA1G xQAu8YBBdiADzzUD4QccOU+h9Xt2IrxWhGi1APCp+LEKGTmZjpUXSsh8OLGJFxC57ljT Kf7w== 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=IoQY0oMOmcLzCpGnM+FY9rI0FS75QSUPEKg0iyfnOYk=; fh=HD4ndZ9hLL4mcfDFnlTmTEEgN7ZZ8c1Zu9bV17rPaE8=; b=Yfl0F+sE4qpkJJtM0PGqefApAxZobQz51aQz+Ijq5exv2nrLY8+98pbOysNs62SXac IEJyzHMVEqnf7LqydKPJOU9lSaK7Vw+eST2VQw+pzIz9sHXGZLSYyNEgghCsuoJTLeSV AKr8pDQq0TLdXUygkjpPMmqxQ/o/qzWYaa+Kg5LeTi8EOLzfAnZ6esZ9QP0sfBJI3QXa Gzw3Bhimwf80rsEPmdDu3B+GcuSVbC+AN/fykXM12Lv72iZyP5/UP9GfQxjHwg3ui0Eq h0G+i13YFm6sPwrHVlynGEp1TCIf+iCyC9PJ9tgFAuG+SUkfRYpuAocKQGApaqxVJupi oO8g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde202009 header.b="IXQr/DBh"; 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=QUARANTINE sp=QUARANTINE 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 y4-20020a1709064b0400b00992f309cfe7si7075239eju.602.2023.07.25.02.00.12; Tue, 25 Jul 2023 02:00:13 -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="IXQr/DBh"; 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=QUARANTINE sp=QUARANTINE 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 2A0E168C89D; Tue, 25 Jul 2023 11:59:03 +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 A1E6168C883 for ; Tue, 25 Jul 2023 11:58:59 +0300 (EEST) Received: from postfix01.mail.de (postfix01.bt.mail.de [10.0.121.125]) by shout02.mail.de (Postfix) with ESMTP id 070B5A05C5 for ; Tue, 25 Jul 2023 10:58:50 +0200 (CEST) Received: from smtp01.mail.de (smtp01.bt.mail.de [10.0.121.211]) by postfix01.mail.de (Postfix) with ESMTP id E26A3801B2 for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde202009; t=1690275529; bh=dahCJibtuUlAWK9Q8RqIxi5f3uGIcCcUQVtckb5eH00=; h=From:To:Subject:Date:Message-Id:From:To:CC:Subject:Reply-To; b=IXQr/DBhE06NfYr8+/gp43tIkcZJ9FJRTbyKzBb23jT0Bs7fe9V/WbfT24Zt4ufHG nZABkBjlaNt7cHLU6Ba+bZKJ8l9FC/0Wr4uucT6mR87+oqYbJ0C07FQBKaINyIJSDS e1+N2n2ftrTW2pL5rqD6r8aWDYaLirl3oYrwUSjrdyFnAjMrcMd9Fq5fnH/jq3Y9TF fndoSWfhXj5RX0ENBvAYd1FXdrw7LXjYRuDMLM7gYg3a8g8B2eK447hzj/InrsQWTG oGykJOxbyBCJPGnZVmK+9xfVmU8+paJc+2pYENnr5+gnzEqfeg66A4k21SEaN3tQGx 8X1dASs7tbVwA== 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 AE13410013A for ; Tue, 25 Jul 2023 10:58:49 +0200 (CEST) From: Thilo Borgmann To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jul 2023 10:58:46 +0200 Message-Id: <20230725085846.93593-8-thilo.borgmann@mail.de> In-Reply-To: <20230725085846.93593-1-thilo.borgmann@mail.de> References: <20230725085846.93593-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: 2138 X-purgate-ID: 154282::1690275529-FC7F07B6-4980EB1E/0/0 Subject: [FFmpeg-devel] [PATCH v4 7/7] fate: add test for animated WebP 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: 4cA6eHgkUeqk --- tests/fate/image.mak | 3 +++ tests/ref/fate/webp-anim | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/ref/fate/webp-anim diff --git a/tests/fate/image.mak b/tests/fate/image.mak index 400199c28a..2e0d1e8e3f 100644 --- a/tests/fate/image.mak +++ b/tests/fate/image.mak @@ -567,6 +567,9 @@ fate-webp-rgb-lossy-q80: CMD = framecrc -i $(TARGET_SAMPLES)/webp/rgb_q80.webp FATE_WEBP += fate-webp-rgba-lossy-q80 fate-webp-rgba-lossy-q80: CMD = framecrc -i $(TARGET_SAMPLES)/webp/rgba_q80.webp +FATE_WEBP += fate-webp-anim +fate-webp-anim: CMD = framecrc -i $(TARGET_SAMPLES)/webp/130227-100431-6817p.webp + FATE_WEBP-$(call DEMDEC, IMAGE2, WEBP) += $(FATE_WEBP) FATE_IMAGE_FRAMECRC += $(FATE_WEBP-yes) fate-webp: $(FATE_WEBP-yes) diff --git a/tests/ref/fate/webp-anim b/tests/ref/fate/webp-anim new file mode 100644 index 0000000000..49dd9e43a1 --- /dev/null +++ b/tests/ref/fate/webp-anim @@ -0,0 +1,22 @@ +#tb 0: 2/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 100x70 +#sar 0: 0/1 +0, 0, 0, 1, 28000, 0x2023ba6e +0, 1, 1, 1, 28000, 0x4292b778 +0, 2, 2, 1, 28000, 0x9772a187 +0, 3, 3, 1, 28000, 0xa98d8d04 +0, 4, 4, 1, 28000, 0xd323b6af +0, 5, 5, 1, 28000, 0x508aba99 +0, 6, 6, 1, 28000, 0x5c672dda +0, 7, 7, 1, 28000, 0xcc992d59 +0, 8, 8, 12, 28000, 0x82460e1b +0, 21, 21, 1, 28000, 0x3debbfc9 +0, 22, 22, 1, 28000, 0x427ab31f +0, 23, 23, 1, 28000, 0x6bbdec2e +0, 24, 24, 1, 28000, 0x5690b56b +0, 25, 25, 1, 28000, 0xb62963f3 +0, 26, 26, 1, 28000, 0x68dd37b2 +0, 27, 27, 1, 28000, 0x465c47d2 +0, 28, 28, 124, 28000, 0x465c47d2