From patchwork Sun Sep 12 20:20:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Reboredo X-Patchwork-Id: 30197 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2a4a:0:0:0:0 with SMTP id k10csp3423374iov; Sun, 12 Sep 2021 13:20:44 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzKGBMYhqilPS3AlxbW64oDKTre2OHACdJtvIIKzxZvX0zU7kiSZ5Qa9TljGDuqL68VWhzI X-Received: by 2002:aa7:cdd1:: with SMTP id h17mr9775897edw.346.1631478044161; Sun, 12 Sep 2021 13:20:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1631478044; cv=none; d=google.com; s=arc-20160816; b=W3XNMcwtP3z2a0etcgP6PCkuPriSNSfGbi4w+VxpAWEo9/UngzI+VIXdkIu8pr3VXs /+qYS9dvHIgeZBuZeLdJ9Ebrsmv5exj+68BW8PyBvNGI/08/sH8sYXx9IKHkq5feF1BU fxl7/8iDw7sS7nuDMqtMYFrOsu+blsfS59cIipfdB24iARNqwG+X22tAqOo1eq/ukZ7Z 7+4uwjO2LOgNfrUCCEKkPyFeAEEmxm2x63OveJuZy9sCrJIaEyeiI4C+uTOn3QEXbBx0 YSz+UnV9+FOiQkqPxanO2PKgmE84atuOSjlxauimgxmzURKPB7mUYBxZ7jCL+5/JvVJn Kdbg== 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=imn11/iA6tEkI6d0F5ikj2ew4d20y2DEY6h/iUg5X4M=; b=UOY8jcHDrIG8jwB2WM5Ez2CwaL3AqOfZNQoYNRzq029K/cuoZ5ABZlT+j+E3rcsyeF TXx7D87aPJBNYG4X6GI67kty7kPCprS5tMRBWXkvjwvLm2gwnZ627b+qx7HF5L4xRnrr 9dCGHtVVza2TTxS8gOuXWHcw7Gvj+Ia92u/e6yUjj3K6IwJQNVq0nLzfIt0HQRaemELJ yZ4nGIKH2K+ZVcDRaRcM3iPONDtdJSORDqWGe172GiZH2YsIWB8/FN+egoRtfVliF4HO jV89XUT7/1w/t93dUAkiRFpHCVCHbhNJpAZUlHddvOTdmdrNdC6d05cDhI81EFW3Zyt1 XMQA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=YLroYtrP; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d26si5211457ejz.183.2021.09.12.13.20.43; Sun, 12 Sep 2021 13:20:44 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=YLroYtrP; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id E7E5B68A9AD; Sun, 12 Sep 2021 23:20:34 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-oo1-f54.google.com (mail-oo1-f54.google.com [209.85.161.54]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3FD7968A7BB for ; Sun, 12 Sep 2021 23:20:27 +0300 (EEST) Received: by mail-oo1-f54.google.com with SMTP id y47-20020a4a9832000000b00290fb9f6d3fso2677469ooi.3 for ; Sun, 12 Sep 2021 13:20:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=E273Q3+441YmXtSA68u4D5hp7F5nsju16OcINgeWUbs=; b=YLroYtrPP00lWU/wI5FjdAeuSux6FdLM4mgp8/GmcM1/AORpQ5mFKkk7gd5yjUHxS9 Dgo3aO9nB/s41yzDD48DwftFDG0B0Vw1o1rLdbVb5qW/EKRX09+zr+mxkzEQNSl0kAEu 5R8Yt+zFffkyDMU8qJ3nd+X7lfgDALAX1TA//UxgH9NzL5czNCQXUYd6w44AT1orfN0k JBXCtmlt/5GjopFg4dB6nZxrJiPUEwvmJcLSDn3L8GhedQHtzl7EdKeDoEpSg9JBXMcI bsDt0DcQQ73buWVK8kcn4q26OdxfBULoCCsw9XhYfB3jsq+3Efn4uP/9Sny65JUdvujj lxQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=E273Q3+441YmXtSA68u4D5hp7F5nsju16OcINgeWUbs=; b=gAUhaU0yMBPgXHeY8pcjTB3anKulrvGeiZc2FsUaBdDT5IdUX64V7Eqm0GJOUgfGuU PwLbkPEqqxwqLUp9Ws3VdLbg9U7BzWPe+aNLXzTi6B367NhQPgDZD+9h9kOfNbvpVtTO js812eqLRXSee3a04SBczaCBedlF13QELBy0ezbjwxKjO0Ohhte5BJC4rDCNJsSg+Ogq WpRaaxIsrulC8yVayQf7xEMGHDO+bMLoBKp4D86RGz4sgiIGderitku8efILVCnxjtNr e5O2ueoZrEWq2GiiCRtvkYzhXmVq4NbBz/CcXanQB6WPFgXz8SFqatC62CP3BYXdo3GC OE3Q== X-Gm-Message-State: AOAM530ysNZK5e53V40Dv0dgVfgH3msWGd3mVdKyPEPEprrTeLFF7lgf wK+sYfKzkRtcmlUw3bAdAPq3/buW+uY= X-Received: by 2002:a4a:b402:: with SMTP id y2mr6604673oon.6.1631478025837; Sun, 12 Sep 2021 13:20:25 -0700 (PDT) Received: from localhost.localdomain (static.220.238.itcsa.net. [190.15.220.238]) by smtp.gmail.com with ESMTPSA id m24sm1206111oie.50.2021.09.12.13.20.25 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 12 Sep 2021 13:20:25 -0700 (PDT) From: Martin Reboredo To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Sep 2021 17:20:07 -0300 Message-Id: <20210912202010.1542872-2-yakoyoku@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210912202010.1542872-1-yakoyoku@gmail.com> References: <20210912202010.1542872-1-yakoyoku@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v3 1/4] avcodec/webp: compatibilize with avformat/webpdec 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: FQJvd5J6yfKl The demuxer implementation splits some RIFF chunks (`RIFF`/`VP8X`/`ANMF` + frame chunk) or sends the picture chunks separately. The internal WebP decoder waits for a complete file instead and by consequence it needs to be modified to support this kind of fractioned input. Fixes FATE tests with WebP. Signed-off-by: Martin Reboredo --- libavcodec/webp.c | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/libavcodec/webp.c b/libavcodec/webp.c index 3efd4438d9..7858d69481 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -40,6 +40,7 @@ * - XMP metadata */ +#include "libavformat/internal.h" #include "libavutil/imgutils.h" #define BITSTREAM_READER_LE @@ -191,6 +192,7 @@ typedef struct WebPContext { 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 */ + int read_header; /* RIFF header has been read */ int initialized; /* set once the VP8 context is initialized */ int has_alpha; /* has a separate alpha chunk */ enum AlphaCompression alpha_compression; /* compression type for alpha chunk */ @@ -1353,17 +1355,36 @@ static int webp_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, 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_type = bytestream2_get_le32(&gb); 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"); + for (int i = 0; !s->read_header && i < 4; i++) { + ff_lock_avformat(); + + if (s->read_header) { + ff_unlock_avformat(); + break; + } + + if (chunk_type == MKTAG('R', 'I', 'F', 'F')) { + int left = bytestream2_get_bytes_left(&gb); + if (left < chunk_size && left != 22) { + ff_unlock_avformat(); + return AVERROR_INVALIDDATA; + } + if (bytestream2_get_le32(&gb) != MKTAG('W', 'E', 'B', 'P')) { + av_log(avctx, AV_LOG_ERROR, "missing WEBP tag\n"); + ff_unlock_avformat(); + return AVERROR_INVALIDDATA; + } + s->read_header = 1; + } + + ff_unlock_avformat(); + } + + if (!s->read_header) { + av_log(avctx, AV_LOG_ERROR, "missing RIFF tag\n"); return AVERROR_INVALIDDATA; } @@ -1416,6 +1437,8 @@ static int webp_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, ret = av_image_check_size(s->width, s->height, 0, avctx); if (ret < 0) return ret; + if (bytestream2_get_bytes_left(&gb) == 0) + return AVERROR(EAGAIN); break; case MKTAG('A', 'L', 'P', 'H'): { int alpha_header, filter_m, compression; From patchwork Sun Sep 12 20:20:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Reboredo X-Patchwork-Id: 30201 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2a4a:0:0:0:0 with SMTP id k10csp3423450iov; Sun, 12 Sep 2021 13:20:52 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxPNbj+KX/sQ40Ak60mznWqFdMlrQQvCC4HPXslbYrPOfeankvUwDiiKdrz1KA6toUm3i2J X-Received: by 2002:a17:906:d287:: with SMTP id ay7mr8796224ejb.402.1631478052541; Sun, 12 Sep 2021 13:20:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1631478052; cv=none; d=google.com; s=arc-20160816; b=WoXmEnH5MhsqU1mLpui6IWBr93skjRy+Bkol/4be+T9S8V28z3LVZmxBG22mAwUaMA mzgH/hTRPpfeGm6AIyVjiBwN5rmcfo4ckz/SGssbqmUSL6/NU+gx6I5tpcYicfRo1pgl TutOERj5ngvZxZw8POpwljEzOBe3j06xsYPmzCXYPT6iLjup6qB1WKxRZnRN1j+AjsoH zvVrloaMUo8jX3XkYNFS9aJhzg2RcCeMVC5hOxjjflMlHd4Pos3TXxE15CB/py19Dug2 OW/HLfIlo7GY2b0raAQqq5HvDinsEEUbBG4X8IE+d0PTuwzkxfilpgDdoGmAkMEoOtJM v8Ew== 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=f0xRgWWvQpH2IS7/p2waQ4kKFsFbRlP0V8PAFPlNFmA=; b=zF3hHINY9+WyAqJpBDPKnlnbJYrRe0Yx+UjSPHJ94E1UShaUEee2dbmdfmnEip2VME 1RTkXnjeWsqxYLoGDVxcKehbzhqK7g8S1mNYl4pVqsMnmqYyI1j1WuDM4BsonCjY9htV hmrJiEhfWVFpVKwfsK8yiVopQqnTnXH/hI3YdFoTjGmVF72F6xk7J+LHo3QGMaoeGDRT mV90KZu3v/oqeA8rNOEigYIF/O/DJ8NXvwOji+imQlWOJsUC8MWqq4dG2Sj7NxyWQh+0 GC6Cjxst4fbwXlYEJYh7PriwPxpZ4HuOcGTBer89fbWeD1TmF4YDxHzLSkA66rJE/Rr1 /pqg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=bLfVfQI2; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id bi20si5329182ejb.575.2021.09.12.13.20.52; Sun, 12 Sep 2021 13:20:52 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=bLfVfQI2; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EB8B968A9C0; Sun, 12 Sep 2021 23:20:35 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ot1-f44.google.com (mail-ot1-f44.google.com [209.85.210.44]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D564D68A9AD for ; Sun, 12 Sep 2021 23:20:28 +0300 (EEST) Received: by mail-ot1-f44.google.com with SMTP id a20-20020a0568300b9400b0051b8ca82dfcso10496115otv.3 for ; Sun, 12 Sep 2021 13:20:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=JLMP4oeOTIl0ALOx9ckSGCHihE4aqQunZmBunXreHVg=; b=bLfVfQI2QYJbDWcs4HhO402JXY9ZM/z8tOCrCsVe9qM/I0H/UNBUUID2JfXaNMQTGu 8x9uYtH/UYlmlDH0qHVkepf5HOFAl2V9hc/lwm7epqrwvMUvZXlVubqzq07BtK97qUm6 /B+nS6fNVlixX8WlpgHiGjhqIjDvpX/1OpWwrGYnIAq8yOskcc12xzG2MW4eq/ZqTBfa sq7P523Q82/0yFMd2+KHQz411WNFlGUvYmBd+ZMZWzw1IpDTYD6Aa/Y+5p1w0FYmIqj2 K4RjWmNAfF8d7QE2zGyLqpWZS2B+ncPexPFHoD4DfcE2Cy5mAf9mRyoKKGhGpaUqfDdY KewQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=JLMP4oeOTIl0ALOx9ckSGCHihE4aqQunZmBunXreHVg=; b=QZ66lI9GjIW7g3Jv/WiCrf2nk0K/zVzkJqNmaSXKN66iq5YIWAAJ/fRqi4FOgm8GyU lMPAPjmoWdf55BCkY96EpQT1Oc3f6nuq7+NrvuW5AzR7U7sy8fy9AvAanBr6FggKcYhL +Yu5m6ij0bFj58GXXtqWtFiXarR8W8SVUcOG8GiH3JOpaHGo3G6caDSBhwz9HPxYIzQE xBZVSoa1L7BNeEF8nrymUQW6E/gGDK8sCDz/wN4ho7PIwj1u/cLRUhdLQwuJf271wwXw CB+1Qusrk60mqF1tH3DmZDx3mGCnwGkFllXYgf1jA6yJmSu+mOgql5z0zc1jwiExLcHQ H1KQ== X-Gm-Message-State: AOAM531wLgcn7PAP69JPvvazIGeyXc+AzoUkn0tVmV7oyOhQpx3IhLSv 1YwV/QmzCdwc8CexpB1AvJCE//ODmqQ= X-Received: by 2002:a9d:5f82:: with SMTP id g2mr7100614oti.318.1631478027131; Sun, 12 Sep 2021 13:20:27 -0700 (PDT) Received: from localhost.localdomain (static.220.238.itcsa.net. [190.15.220.238]) by smtp.gmail.com with ESMTPSA id m24sm1206111oie.50.2021.09.12.13.20.26 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 12 Sep 2021 13:20:26 -0700 (PDT) From: Martin Reboredo To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Sep 2021 17:20:08 -0300 Message-Id: <20210912202010.1542872-3-yakoyoku@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210912202010.1542872-1-yakoyoku@gmail.com> References: <20210912202010.1542872-1-yakoyoku@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v3 2/4] avformat/webpdec: WebP demuxer implementation X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: EQ0oitCb1K/j FFmpeg has the ability to mux encoded WebP packets, but it cannot demux the format. The purpose of this patch is to add a way to extract pictures from a WebP stream. Any other side data processing (mainly ICC profiles) is left up for later work. Although we have a demuxer with `image2`, it doesn't have support for animated frames like this patch. The WebP format is based on RIFF, and due to the charasteristics of the latter, I've took advantage from chunking for processing purposes. Package reading is done by taking chunks in a specific way. Starts by splitting the `RIFF`/`WEBP` header, then it goes by any of the three `VP8 ` (lossy)/`VP8L` (lossless)/`VP8X` (extended format). In the case of a `VP8X` chunk we check for relevant flags. We then follow by grabbing the `VP8 `/`ALPH` (alpha frame) + `VP8 `/`VP8L` chunks accourdingly. If the container specifies that is an animated package we take `ANIM` for the animation parameters and the many `ANMF` animation frames, which every of them contains an image chunk (`VP8 `/`ALPH` + `VP8 `/`VP8L`). Otherwise, if an unknown chunk is found, we just simply ignore it. Tested by remuxing WebP images (using `ffmpeg -i testa.webp -codec:v copy testb.webp`), viewed the images in my browser and compared the checksums. Mostly followed the WebP container specification [1] for the implementation, the VP8 bitstream [2] and the WebP lossless [3] specs were used too. Partially fixes #4907. [1]: https://developers.google.com/speed/webp/docs/riff_container [2]: https://datatracker.ietf.org/doc/html/rfc6386 [3]: https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification Signed-off-by: Martin Reboredo --- MAINTAINERS | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/riff.c | 1 + libavformat/webpdec.c | 326 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 330 insertions(+) create mode 100644 libavformat/webpdec.c diff --git a/MAINTAINERS b/MAINTAINERS index dcac46003e..f2d8f5eb17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -505,6 +505,7 @@ Muxers/Demuxers: wav.c Michael Niedermayer wc3movie.c Mike Melanson webm dash (matroskaenc.c) Vignesh Venkatasubramanian + webp*.c Martin Reboredo webvtt* Matthew J Heaney westwood.c Mike Melanson wtv.c Peter Ross diff --git a/libavformat/Makefile b/libavformat/Makefile index f7e47563da..aec2833c52 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -581,6 +581,7 @@ OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o +OBJS-$(CONFIG_WEBP_DEMUXER) += webpdec.o OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 5471f7c16f..55f3c9a956 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -473,6 +473,7 @@ extern const AVOutputFormat ff_webm_muxer; extern const AVInputFormat ff_webm_dash_manifest_demuxer; extern const AVOutputFormat ff_webm_dash_manifest_muxer; extern const AVOutputFormat ff_webm_chunk_muxer; +extern const AVInputFormat ff_webp_demuxer; extern const AVOutputFormat ff_webp_muxer; extern const AVInputFormat ff_webvtt_demuxer; extern const AVOutputFormat ff_webvtt_muxer; diff --git a/libavformat/riff.c b/libavformat/riff.c index 27a9706510..9bd940ba52 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -321,6 +321,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_VP7, MKTAG('V', 'P', '7', '1') }, { AV_CODEC_ID_VP8, MKTAG('V', 'P', '8', '0') }, { AV_CODEC_ID_VP9, MKTAG('V', 'P', '9', '0') }, + { AV_CODEC_ID_WEBP, MKTAG('W', 'E', 'B', 'P') }, { AV_CODEC_ID_ASV1, MKTAG('A', 'S', 'V', '1') }, { AV_CODEC_ID_ASV2, MKTAG('A', 'S', 'V', '2') }, { AV_CODEC_ID_VCR1, MKTAG('V', 'C', 'R', '1') }, diff --git a/libavformat/webpdec.c b/libavformat/webpdec.c new file mode 100644 index 0000000000..e5c27231be --- /dev/null +++ b/libavformat/webpdec.c @@ -0,0 +1,326 @@ +/* + * webp demuxer + * Copyright (c) 2021 Martin Reboredo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +typedef struct WebpDemuxContext { + AVClass *class; // class for AVOptions + int width; // width of the picture + int height; // height of the picture + int size; // size of the entire WebP file + int loop; // number of times to loop all the pictures (0 means indefinitely) + int read_webp_header; // RIFF header has been read + int using_webp_anim_decoder; // input WebP is animated + int vp8x; // VP8X chunk has been found + int lossless; // WebP file is lossless + int alpha; // input contains alpha + int iccp; // file contains an ICC profile +} WebpDemuxContext; + +static int webpdec_read_probe(const AVProbeData * p) +{ + if (AV_RL32(p->buf) != AV_RL32("RIFF")) + return 0; + + if (AV_RL32(&p->buf[8]) != AV_RL32("WEBP")) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int parse_animation_frame_duration(AVFormatContext * s, AVPacket * pkt) +{ + pkt->duration = av_rescale_q(AV_RL24(pkt->data + 20), + (AVRational) { 1, 1000 }, + s->streams[0]->time_base); + + return 0; +} + +static int parse_vp8x_chunk(AVFormatContext * s, AVPacket * pkt) +{ + WebpDemuxContext *w = s->priv_data; + AVIOContext *pb = s->pb; + int bgcolor = 0xFFFFFFFF; + int cont = 1, anim_frame = 0, alpha_frame = 0; + int64_t ret = 0; + + s->packet_size = 0; + + while (cont && ret >= 0) { + int skip = 0, rewind = 1; + int fourcc = avio_rl32(pb); + int size = avio_rl32(pb); + int padded_size = size + (size & 1); + int chunk_size = padded_size + 8; + s->packet_size += chunk_size; + + if (padded_size == 0) + return AVERROR_EOF; + + switch (fourcc) { + case MKTAG('V', 'P', '8', 'X'): + return AVERROR_INVALIDDATA; + /* case MKTAG('I', 'C', 'C', 'P'): + avio_read(pb, w->iccp_data, padded_size); */ + case MKTAG('A', 'L', 'P', 'H'): + if (!w->alpha || alpha_frame == 1) + return AVERROR_INVALIDDATA; + if (w->using_webp_anim_decoder && anim_frame == 0) + return AVERROR_INVALIDDATA; + + alpha_frame = 1; + break; + case MKTAG('V', 'P', '8', 'L'): + alpha_frame = 1; + case MKTAG('V', 'P', '8', ' '): + if (w->alpha && alpha_frame == 0) + return AVERROR_INVALIDDATA; + if (w->using_webp_anim_decoder && anim_frame == 0) + return AVERROR_INVALIDDATA; + + cont = 0; + break; + case MKTAG('A', 'N', 'I', 'M'): + if (w->loop == -1) { + bgcolor = avio_rl32(pb); + w->loop = avio_rl16(pb); + + ret = avio_seek(pb, -14, SEEK_CUR); + if (ret < 0) + return ret; + + ret = av_get_packet(pb, pkt, s->packet_size); + if (ret < 0) + return ret; + + return 0; + } + cont = 0; + break; + case MKTAG('A', 'N', 'M', 'F'): + if (!w->using_webp_anim_decoder || anim_frame == 1) + return AVERROR_INVALIDDATA; + + ret = avio_seek(pb, -8, SEEK_CUR); + if (ret < 0) + return ret; + + ret = av_get_packet(pb, pkt, s->packet_size); + if (ret < 0) + return ret; + + ret = parse_animation_frame_duration(s, pkt); + if (ret < 0) + return ret; + + anim_frame = 1; + rewind = 0; + return 0; + default: + s->packet_size -= chunk_size; + skip = 1; + rewind = 0; + break; + } + + if (skip) { + ret = avio_skip(pb, padded_size); + } + if (rewind) { + ret = avio_seek(pb, -8, SEEK_CUR); + if (ret < 0) + return ret; + ret = av_append_packet(pb, pkt, chunk_size); + } + } + + return ret; +} + +static int parse_header(AVFormatContext * s) +{ + WebpDemuxContext *w = s->priv_data; + AVIOContext *pb = s->pb; + int size; + unsigned int flags = 0; + int ret = 0; + + if (avio_rl32(pb) != AV_RL32("RIFF")) + return AVERROR_INVALIDDATA; + w->size = avio_rl32(pb) + 8; + if (avio_rl32(pb) != AV_RL32("WEBP")) + return AVERROR_INVALIDDATA; + + if (avio_rl24(pb) != AV_RL24("VP8")) + return AVERROR_INVALIDDATA; + switch (avio_r8(pb)) { + case 'X': + w->vp8x = 1; + break; + case 'L': + w->lossless = 1; + case ' ': + break; + default: + return AVERROR_INVALIDDATA; + } + size = avio_rl32(pb); + if (w->vp8x) { + flags = avio_r8(pb); + + if (flags & 0x02) + w->using_webp_anim_decoder = 1; + if (flags & 0x10) + w->alpha = 1; + if (flags & 0x20) + w->iccp = 1; + + ret = avio_skip(pb, 3); + if (ret < 0) + return ret; + + w->width = avio_rl24(pb) + 1; + w->height = avio_rl24(pb) + 1; + + ret = avio_seek(pb, -30, SEEK_CUR); + } else if (w->lossless) { + avio_r8(pb); + flags = avio_rl32(pb); + w->width = (flags & 0x3FFF) + 1; + w->height = ((flags >> 14) & 0x3FFF) + 1; + w->alpha = (flags >> 28) & 0x01; + + ret = avio_seek(pb, -25, SEEK_CUR); + } else { + ret = avio_skip(pb, 6); + if (ret < 0) + return ret; + + w->width = (avio_rl16(pb) & 0x3FFF); + w->height = (avio_rl16(pb) & 0x3FFF); + + ret = avio_seek(pb, -30, SEEK_CUR); + } + + return ret; +} + +static int webpdec_read_header(AVFormatContext * s) +{ + WebpDemuxContext *w = s->priv_data; + AVStream *st; + int ret; + + w->width = -1; + w->height = -1; + w->loop = -1; + w->read_webp_header = 0; + + ret = parse_header(s); + if (ret < 0) + return ret; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_WEBP; + st->codecpar->width = w->width; + st->codecpar->height = w->height; + st->codecpar->format = w->alpha ? AV_PIX_FMT_YUVA420P : AV_PIX_FMT_YUV420P; + + st->start_time = 0; + + avpriv_set_pts_info(st, 24, 1, 1000); + + return 0; +} + +static int webpdec_read_packet(AVFormatContext * s, AVPacket * pkt) +{ + WebpDemuxContext *w = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + + ret = avio_feof(pb); + if (ret < 0) + return ret; + else if (ret > 0) + return AVERROR_EOF; + + if (!w->read_webp_header) { + if (!w->using_webp_anim_decoder) { + pkt->duration = + av_rescale_q(33, (AVRational) { 1, 1000 }, s->streams[0]->time_base); + s->packet_size = w->size; + } else { + s->packet_size = 30; + } + + ret = av_get_packet(pb, pkt, s->packet_size); + if (ret < 0) + return ret; + + w->read_webp_header = 1; + + return 0; + } + + if (w->vp8x) { + ret = parse_vp8x_chunk(s, pkt); + if (ret < 0) + return ret; + } else { + int fourcc = avio_rl32(pb); + int size = avio_rl32(pb) + 8; + size = size + (size & 1); + if (fourcc != AV_RL32("VP8 ") && fourcc != AV_RL32("VP8L")) + return AVERROR_INVALIDDATA; + ret = avio_seek(pb, -8, SEEK_CUR); + if (ret < 0) + return ret; + ret = av_get_packet(pb, pkt, size); + if (ret < 0) + return ret; + } + + pkt->stream_index = 0; + + return 0; +} + +const AVInputFormat ff_webp_demuxer = { + .name = "webp", + .long_name = NULL_IF_CONFIG_SMALL("WebP"), + .extensions = "webp", + .mime_type = "image/webp", + .priv_data_size = sizeof(WebpDemuxContext), + .read_probe = webpdec_read_probe, + .read_header = webpdec_read_header, + .read_packet = webpdec_read_packet, + .flags = AVFMT_VARIABLE_FPS, +}; From patchwork Sun Sep 12 20:20:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Reboredo X-Patchwork-Id: 30200 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2a4a:0:0:0:0 with SMTP id k10csp3423527iov; Sun, 12 Sep 2021 13:21:01 -0700 (PDT) X-Google-Smtp-Source: ABdhPJy7ywrBlADbO5IpKopzSuZYyDWff5qc+NzEYyC3PShc+jophtBhSynlZlbynB+UcQc3q6Lk X-Received: by 2002:a17:907:8693:: with SMTP id qa19mr8852480ejc.497.1631478061804; Sun, 12 Sep 2021 13:21:01 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1631478061; cv=none; d=google.com; s=arc-20160816; b=0I1W550j828wu0hNeCJcQBHhxjwSASZ4tCfYppbqIS3hI61YdFJ2lEUOAXTY7d0a7e Vz2IN1jUIfXVHNCe7/Nsj52I5is6XpMORr2jCGLyYw1O6J+h7uCp76+hvP7GTzxtIdVI N/0NnTa4fl/MiNOdALOxB3xyPsUgxki/a70J3kjw6qgK67lWU+QUU3rXY30hAYkAQ74u EV0dBhlhobi0/8ZHRV9J7cdtT4JVAPqJWsMGIV3TJd7EDdnPGnjClOQuUbRasFk/9ynv HtKbfDcDLt/NVD6A64JUIQ1z2FFLP1fYDYZ9qWIsbWNvMed0C4sF2yCW4VN0BJjO2sHX DehA== 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=nTWNAIYMyMms6WSvgjziUP3X8UVkFm8kOp1YcXeIIAk=; b=J2uNIn3PgIGAKCLMAyCr1GEO5sRompReMtd4ZgkhSVZwBSKiPPGIICVXj3Ots2vtz0 bpZWUqBNw52mbUpYqmswfIpe9L2Nr9OWrok2rD9B1UEM+oyXb1/CE35iKR0DePnjWsv3 7L8H8oco9AX8IqT4WP1WfPKeRGwKswpm+anOnABCFXVIXewJr7OvproaQJukiGtSHYY8 3rp3ForucDsTYWOAcm47tXanZAWsATCG5ysDFClQ4THLQnevdaqyABNbCTNSSS6VYswb Pm+5ujQ+qDZ0gcdzEBytkGflwGueqiTO90ehhbkjPf+PYqnq+ZEAEIaulBy6EyWxgDUc moYg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=NS2GMuei; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id rn4si5943337ejb.248.2021.09.12.13.21.01; Sun, 12 Sep 2021 13:21:01 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=NS2GMuei; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3607768A628; Sun, 12 Sep 2021 23:20:37 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-oi1-f180.google.com (mail-oi1-f180.google.com [209.85.167.180]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 84A9E68A628 for ; Sun, 12 Sep 2021 23:20:29 +0300 (EEST) Received: by mail-oi1-f180.google.com with SMTP id h133so11387319oib.7 for ; Sun, 12 Sep 2021 13:20:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=p/zR5k45Z1cljUs7P1pz8EIpvlAIyMdBfZ1asn8Y3wA=; b=NS2GMueiVmkf+9TCR5tdU5+/jvj5rgPneooCLF5mXvv9cwiBkPykCxTYyHxHZ3lvhd aJk8/Mj/V3dOmDmYHmmo+TUMid0rQusmSKB0jTA5pb3MsNou+xwK2JS/cfEDjBtlxf6+ +DoK+Aulqs8kCzSQCf+Qei++9xxbtwJQ630WBDjP+X/9L4b4YoEntEvc0hfwq1QdX3o5 +BNiKITPVlbZq/NHDYmTt1M7jZAkmW2dY6n2O0qwdy5BhdGxTj8V7y4Q2cX0iPqUGmOr vxlRvApCe/ZoqWkbLcftblYNNm1d3M9cEvVbiyl0a0Mr/fIwrKpLSBWqTyyFdwtgE5L7 zb8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=p/zR5k45Z1cljUs7P1pz8EIpvlAIyMdBfZ1asn8Y3wA=; b=GUyGJ4QjNvKABi9Hy2KklctZoKZG0VqXG8VkrnYTMuvYzsw6IIsS04hFiMqVrASqWG 9/IVPV2JNgjyRRCP1SThEtG8KE4EQreP2VO/dHOcxeKYRCjB8aAtyL0BlwY3reK4vfxS F/3xm6BnVFcEBGtmMAeAGv4NEtkPtgukVPT4bYADYa18hnkjQRdDZLrMSlemUEmjkCEu 9C5poE2VDoX1AM9eKFbYMuKDqkLrg+aB2yy3vjc3slAOD/36779okMs9AVoXpnAfaHVu ISU1ySl5FpkDo0YWSAltHi7wkSoUHJL2pxQSTQp5/cuEW1aCWSZMP0WMM3vQcoQd/8pA 66oQ== X-Gm-Message-State: AOAM532sSyphDHFbsYHJo2Y6JkfXsOhA2I2jEPpYLfOOLLgQiDjdyIhH hmJLYvkEGnq2whKiY+stYbNAiCvbKec= X-Received: by 2002:aca:1216:: with SMTP id 22mr5433205ois.35.1631478028104; Sun, 12 Sep 2021 13:20:28 -0700 (PDT) Received: from localhost.localdomain (static.220.238.itcsa.net. [190.15.220.238]) by smtp.gmail.com with ESMTPSA id m24sm1206111oie.50.2021.09.12.13.20.27 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 12 Sep 2021 13:20:27 -0700 (PDT) From: Martin Reboredo To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Sep 2021 17:20:09 -0300 Message-Id: <20210912202010.1542872-4-yakoyoku@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210912202010.1542872-1-yakoyoku@gmail.com> References: <20210912202010.1542872-1-yakoyoku@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v3 3/4] avformat/webpenc: better detection of anim chunks 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: QmYwi5x6a5v9 Receiving RIFF chunks as `av_packet`s from `webpdec.c` in `webpenc.c` it wasn't doing proper animated frame detection/enumeration. Check for `ANIM`/`ANMF` chunks to see if the package is an animated WebP packet and for the `ANMF`/`ALPH`/`VP8 `/`VP8L` chunks if it's an actual frame. Signed-off-by: Martin Reboredo --- libavformat/webpenc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/libavformat/webpenc.c b/libavformat/webpenc.c index 9599fe7b85..50bee91910 100644 --- a/libavformat/webpenc.c +++ b/libavformat/webpenc.c @@ -55,13 +55,18 @@ static int is_animated_webp_packet(AVPacket *pkt) { int skip = 0; unsigned flags = 0; + int fourcc = AV_RL32(pkt->data); if (pkt->size < 4) return AVERROR_INVALIDDATA; - if (AV_RL32(pkt->data) == AV_RL32("RIFF")) + if (fourcc == AV_RL32("RIFF")) skip = 12; + else if (fourcc == AV_RL32("ANIM")) + return 1; + else if (fourcc == AV_RL32("ANMF")) + return 1; // Safe to do this as a valid WebP bitstream is >=30 bytes. - if (pkt->size < skip + 4) + if (pkt->size < skip + 4 && pkt->size != 12) return AVERROR_INVALIDDATA; if (AV_RL32(pkt->data + skip) == AV_RL32("VP8X")) { flags |= pkt->data[skip + 4 + 4]; @@ -143,6 +148,7 @@ static int flush(AVFormatContext *s, int trailer, int64_t pts) static int webp_write_packet(AVFormatContext *s, AVPacket *pkt) { WebpContext *w = s->priv_data; + int fourcc = AV_RL32(pkt->data); int ret; if (!pkt->size) @@ -161,7 +167,9 @@ static int webp_write_packet(AVFormatContext *s, AVPacket *pkt) return ret; av_packet_ref(&w->last_pkt, pkt); } - ++w->frame_count; + if (fourcc == AV_RL32("ANMF") || fourcc == AV_RL32("ALPH") || + fourcc == AV_RL32("VP8 ") || fourcc == AV_RL32("VP8L")) + ++w->frame_count; return 0; } From patchwork Sun Sep 12 20:20:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Reboredo X-Patchwork-Id: 30198 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2a4a:0:0:0:0 with SMTP id k10csp3423652iov; Sun, 12 Sep 2021 13:21:11 -0700 (PDT) X-Google-Smtp-Source: ABdhPJy5GlaPQdbLwSLmsOg4hSs1YXYAc9Phfy4v6GtXuyKHw4rgVR6Z/5ORA/wbeo0OOOEKTQSd X-Received: by 2002:a17:907:175d:: with SMTP id lf29mr4366719ejc.163.1631478070882; Sun, 12 Sep 2021 13:21:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1631478070; cv=none; d=google.com; s=arc-20160816; b=XG+Atr6tc9/AQFkICY1tHHSUR4SolUlDErcQoESY2h5g+flUWhQ1aNFAQ3/nk9Xgcc aT8HqIxacNbRA9pQOAxPUS8DurYW7v0H+wYT4HKaUvsg8dsc7XEgUX0C26yaP/Zog945 i+loEb/sDb+vQ+U5Tg6Fnr+4vrNh/iCi+m34M82hYE2YUrnagruVmzKKXCASIC7wEC7F 6zEmP0tXtndoWQYk7OkRGpS3V3rm+Wpx4dyaiHeOsmX04qhL7ax95N+88Nw827oq56aY 1EDTCUMRRFFfBvp5ykCI5FBOXg8v/HH1GQ8QwEP5rJcazObpSjXp0G19oIiI0Lw8jvyD Ahvw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=KU1E11EHOCjHPJ2KpmO307LdhyfKCBorI4bi3+8kMzw=; b=Kz8h3qaDFtGAdtPc/fa+QZSEp7c9GEI0E9ULxW8tB+MVdmD1tBgfsVDeZEyjn/QCB8 WGZNsg9XZPr+YSVGUUvhYMSQMtsTYdp91UIkR3y9ZeRreAIObL9jAge6C0qRjryxiXef Z6nmAci4VdOfv7jgHKAJjTMfou1cY/BgmxbjtnIp6MoMEiCltUUnCdGCbrI4/KvQfKPg ETgYlT8OUmjWClrvNmcaVU7mpo/gICTF3ZNp6OPPv8nL/h2aIZWvjynOdAxYXOeOkvzS EH06rpzMbksvsMyYon9ipsNkYiCSWWHWxask+jta03mk90izMdpt5Dp3ZeawldOGjGoA hikw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Jwp1iWxB; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id w22si4756002edc.482.2021.09.12.13.21.10; Sun, 12 Sep 2021 13:21:10 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Jwp1iWxB; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 44A9768AA2A; Sun, 12 Sep 2021 23:20:38 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ot1-f49.google.com (mail-ot1-f49.google.com [209.85.210.49]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AA6D168A9ED for ; Sun, 12 Sep 2021 23:20:31 +0300 (EEST) Received: by mail-ot1-f49.google.com with SMTP id k12-20020a056830150c00b0051abe7f680bso10470744otp.1 for ; Sun, 12 Sep 2021 13:20:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=+P8T1VuD/Uv/DJFyomkA3aAbh1XQ9Q82LT6nTc/LHv0=; b=Jwp1iWxBvKTwia6QZR3Xd+Pp5PslXB4PQmccPwwWEA/NcoVC4EHHagTC5cGcPR1+Es /nP5OdMk9cBEU5BHv9DTw6RvtSADNNPLO6yCWnpk+DkvVRE4A4BA6uvunEsyu9MCVZPM vLXq8vzPPBDd6pAun7p2PKWyRwh38VjoGiiNjjzCSV19IS+t1C/9CdLQHLMEgwlqzGhU UVNZrNfj2Qv95BJ7SkGZsMk/m2MG17KaR/7Iv5R0ox/eYJU6KONuwGd8DXcywW4k0aVJ 5tEaTOHLbxjbWyvqY/4boB0FOStwSsPuK9oYymQn0dCn3NlWWxfqL7kAm2J51U+3oKtq NUyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+P8T1VuD/Uv/DJFyomkA3aAbh1XQ9Q82LT6nTc/LHv0=; b=1bA6V83ubBlqFPqaJVM434pvpm0iRC4MJrimUhc9HKnmEHolRo5bOPjW6H4+63in1v MowMlLrc8p17rNECC48EIdNq8HW93jWvfjs/hBQlN6muDEnaIuuxZgKDCy0F4MHY2Jly 4FXv5ti+HuiSbaVPbZAM9ohtfkuSNy/CG+idRKfPrR3V0arlXtpOsaeVP9X1B5km+VJV wqk+L3w+CLLj5GI0ANuI1JtX+wOHxcd+zsHzRgqNnslGiNtz8iGEXCHeb9cUNwVIC5z9 TsCjVNGW7HM53EGQdTyUSlxwagDGOZ7ze93urOiAIsJ5GnmweepsaTnx3djAXvbS4urP 2xFw== X-Gm-Message-State: AOAM531QDXwQ3xAKXKxjVHwNJKHjbfKJKjleNsOcj+7/UIMy+u3aEoxp qrkWDZNfW+MVj311MyYmP2S5pcxmFjc= X-Received: by 2002:a05:6830:3114:: with SMTP id b20mr7259532ots.17.1631478030004; Sun, 12 Sep 2021 13:20:30 -0700 (PDT) Received: from localhost.localdomain (static.220.238.itcsa.net. [190.15.220.238]) by smtp.gmail.com with ESMTPSA id m24sm1206111oie.50.2021.09.12.13.20.28 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 12 Sep 2021 13:20:29 -0700 (PDT) From: Martin Reboredo To: ffmpeg-devel@ffmpeg.org Date: Sun, 12 Sep 2021 17:20:10 -0300 Message-Id: <20210912202010.1542872-5-yakoyoku@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210912202010.1542872-1-yakoyoku@gmail.com> References: <20210912202010.1542872-1-yakoyoku@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v3 4/4] avcodec/libwebpdec: libwebp decoder implementation X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Q8uhaNSJihPK Followup of the webp demuxer implementation. As the demuxer sends RIFF packets, the decoder choses what to do with the arriving chunks. Completely fixes #4907. Signed-off-by: Martin Reboredo --- configure | 4 +- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/libwebpdec.c | 419 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 libavcodec/libwebpdec.c diff --git a/configure b/configure index 98987ed186..73dc45fb0d 100755 --- a/configure +++ b/configure @@ -285,7 +285,7 @@ External library support: --enable-libvorbis enable Vorbis en/decoding via libvorbis, native implementation exists [no] --enable-libvpx enable VP8 and VP9 de/encoding via libvpx [no] - --enable-libwebp enable WebP encoding via libwebp [no] + --enable-libwebp enable WebP de/encoding via libwebp [no] --enable-libx264 enable H.264 encoding via x264 [no] --enable-libx265 enable HEVC encoding via x265 [no] --enable-libxavs enable AVS encoding via xavs [no] @@ -3314,6 +3314,7 @@ libvpx_vp8_decoder_deps="libvpx" libvpx_vp8_encoder_deps="libvpx" libvpx_vp9_decoder_deps="libvpx" libvpx_vp9_encoder_deps="libvpx" +libwebp_decoder_deps="libwebpdecoder" libwebp_encoder_deps="libwebp" libwebp_anim_encoder_deps="libwebp" libx262_encoder_deps="libx262" @@ -6518,6 +6519,7 @@ enabled libvpx && { } enabled libwebp && { + enabled libwebp_decoder && require_pkg_config libwebpdecoder "libwebpdecoder >= 0.2.0" webp/decode.h WebPGetDecoderVersion enabled libwebp_encoder && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; } enabled libx264 && { check_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode || diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 11873eecae..81936b9828 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1062,6 +1062,7 @@ OBJS-$(CONFIG_LIBVPX_VP8_DECODER) += libvpxdec.o OBJS-$(CONFIG_LIBVPX_VP8_ENCODER) += libvpxenc.o OBJS-$(CONFIG_LIBVPX_VP9_DECODER) += libvpxdec.o libvpx.o OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.o libvpx.o +OBJS-$(CONFIG_LIBWEBP_DECODER) += libwebpdec.o OBJS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.o libwebpenc.o OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_animencoder.o OBJS-$(CONFIG_LIBX262_ENCODER) += libx264.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index c42aba140d..223f8bbf15 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -768,6 +768,7 @@ extern AVCodec ff_libvpx_vp9_decoder; /* preferred over libwebp */ extern const AVCodec ff_libwebp_anim_encoder; extern const AVCodec ff_libwebp_encoder; +extern const AVCodec ff_libwebp_decoder; extern const AVCodec ff_libx262_encoder; #if CONFIG_LIBX264_ENCODER #include diff --git a/libavcodec/libwebpdec.c b/libavcodec/libwebpdec.c new file mode 100644 index 0000000000..c583f919e0 --- /dev/null +++ b/libavcodec/libwebpdec.c @@ -0,0 +1,419 @@ +/* + * WebP decoding support via libwebp + * Copyright (c) 2021 Martin Reboredo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * WebP decoder using libwebp (WebPDecode API) + */ + +#include +#include + +#include "decode.h" +#include "internal.h" +#include "libavutil/colorspace.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/error.h" +#include "libavutil/opt.h" + +struct WebPDecBgColor { + uint8_t y; + uint8_t u; + uint8_t v; + uint8_t a; +}; + +typedef struct LibWebPDecContext { + AVClass *class; // class for AVOptions + struct WebPDecBgColor bg_color; // background color for frame disposals + int loop; // number of times to loop all the pictures (0 means indefinitely) + int bypass_filter; // bypass filtering for decoded frames + int flip; // flip output images vertically + int dither_strength; // dithering strength applied to the images + int dispose; // dispose the previous frame with background color + int blend; // alpha blend with the previous frame + int chunk_unread; // chunk read is not yet complete + WebPDecoderConfig config; // libwebpdecoder configuration + AVFrame *frame; // current decoded frame + AVFrame *prev; // previously decoded frame + int prev_alpha; // previous frame had an alpha channel +} LibWebPDecContext; + +static int ff_libwebpdec_error_to_averror(int err) +{ + switch (err) { + case VP8_STATUS_OUT_OF_MEMORY: + return AVERROR(ENOMEM); + case VP8_STATUS_UNSUPPORTED_FEATURE: + case VP8_STATUS_BITSTREAM_ERROR: + case VP8_STATUS_INVALID_PARAM: + return AVERROR_INVALIDDATA; + case VP8_STATUS_NOT_ENOUGH_DATA: + return AVERROR(EAGAIN); + } + return AVERROR_UNKNOWN; +} + +static struct WebPDecBgColor ff_libwebpdec_bgra2yuv(int bgra) +{ + uint8_t r = (bgra >> 8) & 0xFF; + uint8_t g = (bgra >> 16) & 0xFF; + uint8_t b = bgra >> 24; + return (struct WebPDecBgColor) { + RGB_TO_Y_JPEG(r, g, b), + RGB_TO_U_JPEG(r, g, b), + RGB_TO_V_JPEG(r, g, b), + bgra & 0xFF, + }; +} + +static int ff_libwebpdec_parse_animation_frame(AVCodecContext *avctx, uint8_t *chunk) +{ + LibWebPDecContext *w = avctx->priv_data; + int flags = 0; + + flags = *(chunk + 23); + w->dispose = flags & 0x01; + w->blend = (flags & 0x02) == 0; + + return 0; +} + +// divide by 255 and round to nearest +// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16 +#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16) + +static void ff_libwebpdec_alpha_blend_frames(AVFrame *dst, const AVFrame *src, int alpha_bg) +{ + const uint8_t *y_src = src->data[0]; + const uint8_t *u_src = src->data[1]; + const uint8_t *v_src = src->data[2]; + const uint8_t *a_src = src->data[3]; + uint8_t *y_dst = dst->data[0]; + uint8_t *u_dst = dst->data[1]; + uint8_t *v_dst = dst->data[2]; + uint8_t *a_dst = dst->data[3]; + + for (int y = 0; y < src->height; y++) { + if ((y & 1) == 0) { + for (int x = 0; x < (src->width >> 1); x++) { + const uint8_t *a_bgp = (y + 1 == src->height) ? a_src : a_src + src->linesize[3]; + uint8_t *a_fgp = (y + 1 == src->height) ? a_dst : a_dst + dst->linesize[3]; + uint8_t a_bg = (alpha_bg) ? (a_src[2 * x] + a_src[2 * x + 1] + a_bgp[2 * x] + a_bgp[2 * x + 1]) >> 2 : 255; + uint8_t a_fg = (a_dst[2 * x] + a_dst[2 * x + 1] + a_fgp[2 * x] + a_fgp[2 * x + 1]) >> 2; + uint8_t out_uv_alpha = a_fg + FAST_DIV255((255 - a_fg) * a_bg); + + if (out_uv_alpha == 0) { + u_dst[x] = 0; + v_dst[x] = 0; + } else if (out_uv_alpha >= 255) { + u_dst[x] = FAST_DIV255(a_fg * u_dst[x] + (255 - a_fg) * u_src[x]); + v_dst[x] = FAST_DIV255(a_fg * v_dst[x] + (255 - a_fg) * v_src[x]); + } else { + u_dst[x] = (255 * a_fg * u_dst[x] + (255 - a_fg) * a_bg * u_src[x]) / (255 * out_uv_alpha); + v_dst[x] = (255 * a_fg * v_dst[x] + (255 - a_fg) * a_bg * v_src[x]) / (255 * out_uv_alpha); + } + } + u_src += src->linesize[1]; + v_src += src->linesize[2]; + u_dst += dst->linesize[1]; + v_dst += dst->linesize[2]; + } + for (int x = 0; x < src->width; x++) { + uint8_t a_bg = (alpha_bg) ? a_src[x] : 255; + uint8_t a_fg = a_dst[x]; + uint8_t out_y_alpha = a_fg + FAST_DIV255((255 - a_fg) * a_bg); + + if (out_y_alpha == 0) { + y_dst[x] = 0; + } else if (out_y_alpha == 255) { + y_dst[x] = FAST_DIV255(a_fg * y_dst[x] + (255 - a_fg) * y_src[x]); + } else { + y_dst[x] = (255 * a_fg * y_dst[x] + (255 - a_fg) * a_bg * y_src[x]) / (255 * out_y_alpha); + } + + a_dst[x] = out_y_alpha; + } + y_src += src->linesize[0]; + a_src += src->linesize[3]; + y_dst += dst->linesize[0]; + a_dst += dst->linesize[3]; + } +} + +static av_cold int libwebp_decode_init(AVCodecContext *avctx) +{ + LibWebPDecContext *s = avctx->priv_data; + int ret; + + if (!WebPInitDecoderConfig(&s->config)) { + return AVERROR_INVALIDDATA; + } + + s->config.options.bypass_filtering = s->bypass_filter; + s->config.options.dithering_strength = s->dither_strength; + s->config.options.alpha_dithering_strength = s->dither_strength; + s->config.options.flip = s->flip; + s->config.options.use_threads = avctx->thread_count; + + s->loop = -1; + s->chunk_unread = 0; + + s->frame = av_frame_alloc(); + s->prev = av_frame_alloc(); + if (s->frame == NULL || s->prev == NULL) { + av_frame_free(&s->frame); + av_frame_free(&s->prev); + return AVERROR(ENOMEM); + } + + ret = ff_get_buffer(avctx, s->frame, 0); + if (ret < 0) + return ret; + + s->frame->format = s->prev->format = avctx->pix_fmt; + s->frame->width = s->prev->width = avctx->width; + s->frame->height = s->prev->height = avctx->height; + + ret = av_frame_get_buffer(s->frame, 0); + if (ret < 0) + return ret; + + ret = av_frame_get_buffer(s->prev, 0); + if (ret < 0) + return ret; + + s->config.output.is_external_memory = 1; + s->config.output.width = avctx->width; + s->config.output.height = avctx->height; + + if (s->frame->format == AV_PIX_FMT_YUVA420P || s->frame->format == AV_PIX_FMT_YUV420P) { + s->config.output.u.YUVA.y = s->frame->data[0]; + s->config.output.u.YUVA.u = s->frame->data[1]; + s->config.output.u.YUVA.v = s->frame->data[2]; + s->config.output.u.YUVA.a = s->frame->data[3]; + s->config.output.u.YUVA.y_stride = s->frame->linesize[0]; + s->config.output.u.YUVA.u_stride = s->frame->linesize[1]; + s->config.output.u.YUVA.v_stride = s->frame->linesize[2]; + s->config.output.u.YUVA.a_stride = s->frame->linesize[3]; + s->config.output.u.YUVA.y_size = s->frame->linesize[0] * avctx->height; + s->config.output.u.YUVA.u_size = s->frame->linesize[1] * (avctx->height / 2); + s->config.output.u.YUVA.v_size = s->frame->linesize[2] * (avctx->height / 2); + s->config.output.u.YUVA.a_size = s->frame->linesize[3] * avctx->height; + if (s->frame->format == AV_PIX_FMT_YUVA420P) { + s->prev_alpha = 1; + s->config.output.colorspace = MODE_YUVA; + } else { + s->prev_alpha = 0; + s->config.output.colorspace = MODE_YUV; + } + } else { + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static void ff_libwebpdec_dispose_frame(AVCodecContext *avctx) +{ + LibWebPDecContext *s = avctx->priv_data; + + if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) { + memset(s->prev->data[0], s->bg_color.y, s->config.output.u.YUVA.y_size); + memset(s->prev->data[1], s->bg_color.u, s->config.output.u.YUVA.u_size); + memset(s->prev->data[2], s->bg_color.v, s->config.output.u.YUVA.v_size); + memset(s->prev->data[3], s->bg_color.a, s->config.output.u.YUVA.a_size); + } +} + +static int libwebp_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *pkt) +{ + LibWebPDecContext *s = avctx->priv_data; + AVFrame *picture = data; + uint8_t *chunk = pkt->data, *alpha_chunk = NULL; + int chunk_size = 0, alpha_chunk_size = 0, offset = 0; + int ret = 0, cont = 1, alpha = 0; + + if (s->dispose) { + ff_libwebpdec_dispose_frame(avctx); + } + + while (cont && ret >= 0) { + int skip = 1; + int fourcc = AV_RL32(chunk); + int size = AV_RL32(chunk + 4); + int padded_size = size + (size & 1); + chunk_size = padded_size + 8; + + cont = 0; + + switch (fourcc) { + case MKTAG('R', 'I', 'F', 'F'): + chunk_size = 12; + cont = 1; + break; + case MKTAG('V', 'P', '8', 'X'): + chunk_size = 18; + cont = 1; + break; + case MKTAG('A', 'N', 'I', 'M'): + if (s->loop == -1) { + s->bg_color = ff_libwebpdec_bgra2yuv(AV_RL32(chunk + 8)); + ff_libwebpdec_dispose_frame(avctx); + + s->loop = AV_RL16(chunk + 12); + } + + chunk_size = 14; + cont = 1; + break; + case MKTAG('A', 'N', 'M', 'F'): + ret = ff_libwebpdec_parse_animation_frame(avctx, chunk); + if (ret < 0) + return ret; + + chunk_size = 24; + if (s->chunk_unread) + return AVERROR_INVALIDDATA; + s->chunk_unread = 1; + cont = 1; + break; + case MKTAG('A', 'L', 'P', 'H'): + if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) + return AVERROR_INVALIDDATA; + if (pkt->size < offset + chunk_size) + return AVERROR(EAGAIN); + alpha_chunk = chunk; + alpha_chunk_size = chunk_size + AV_RL32(chunk + chunk_size + 4) + 8; + alpha = 1; + cont = 1; + break; + case MKTAG('V', 'P', '8', 'L'): + if (*(chunk + 12) & 0x10) { + if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) + return AVERROR_INVALIDDATA; + alpha_chunk = chunk; + alpha_chunk_size = chunk_size; + alpha = 1; + } + case MKTAG('V', 'P', '8', ' '): + s->config.output.colorspace = (alpha_chunk != NULL) ? MODE_YUVA : MODE_YUV; + s->chunk_unread = 0; + skip = 0; + break; + default: + cont = 1; + break; + } + + offset += chunk_size; + if (skip) + chunk += chunk_size; + + if (cont && offset > pkt->size) + return AVERROR(EAGAIN); + } + + if (alpha_chunk != NULL) { + chunk = alpha_chunk; + chunk_size = alpha_chunk_size; + } + ret = WebPDecode(chunk, chunk_size, &s->config); + if (ret != VP8_STATUS_OK) { + av_log(avctx, AV_LOG_ERROR, "WebPDecode() failed with error: %d\n", + ret); + + if (ret == VP8_STATUS_NOT_ENOUGH_DATA) + return AVERROR_INVALIDDATA; + + ret = ff_libwebpdec_error_to_averror(ret); + return ret; + } + + if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) { + if (!alpha) + memset(s->frame->data[3], 0xFF, s->config.output.u.YUVA.a_size); + if (s->blend) + ff_libwebpdec_alpha_blend_frames(s->frame, s->prev, s->prev_alpha); + } + + s->prev_alpha = alpha; + + ret = av_frame_copy(s->prev, s->frame); + if (ret < 0) + return ret; + + ret = av_frame_ref(picture, s->frame); + if (ret < 0) + return ret; + + *got_frame = 1; + + return pkt->size; +} + +static int libwebp_decode_close(AVCodecContext *avctx) +{ + LibWebPDecContext *s = avctx->priv_data; + av_frame_unref(s->frame); + av_frame_free(&s->frame); + av_frame_free(&s->prev); + + return 0; +} + +const enum AVPixelFormat ff_libwebpdec_pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_NONE +}; + +#define OFFSET(x) offsetof(LibWebPDecContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "bypass", "Bypass filter", OFFSET(bypass_filter), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VD }, + { "dither_strength", "Dithering strength", OFFSET(dither_strength), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, VD }, + { "flip", "Flip decoded pictures", OFFSET(flip), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VD }, + { NULL }, +}; + +const AVClass ff_libwebpdec_class = { + .class_name = "libwebp decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const AVCodec ff_libwebp_decoder = { + .name = "libwebp", + .long_name = NULL_IF_CONFIG_SMALL("libwebp WebP image"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_WEBP, + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = AV_CODEC_CAP_AUTO_THREADS, + .pix_fmts = ff_libwebpdec_pix_fmts, + .priv_class = &ff_libwebpdec_class, + .priv_data_size = sizeof(LibWebPDecContext), + .init = libwebp_decode_init, + .decode = libwebp_decode_frame, + .close = libwebp_decode_close, + .wrapper_name = "libwebp", +};